rx-player
Version:
Canal+ HTML5 Video Player
167 lines (158 loc) • 5.62 kB
text/typescript
export interface IHTMLCue {
start: number;
end: number;
element: HTMLElement;
}
export interface ICuesGroup {
start: number;
end: number;
cues: IHTMLCue[];
}
/**
* Maximum time difference, in seconds, between two text segment's start times
* and/or end times for them to be considered the same.
*
* For example for two segments s1 and s2 which have a start time respectively
* of st1 and st2 and end time of et1 and et2:
* - if both the absolute difference between st1 and st2 AND the one between
* et1 and et2 is inferior or equal to the MAX_DELTA_BUFFER_TIME, s1 and s2
* are considered to target the exact same time. As a consequence, if s2 is
* added after s1 in the buffer, s1 will be completely replaced by it and
* vice-versa.
* - if only one of the two (absolute difference between st1 and st2 OR et1
* and et2) is inferior to the MAX_DELTA_BUFFER_TIME then the last added
* is not completely considered the same. It WILL still replace - either
* partially or completely (depending on the sign of the other difference) -
* the previously added segment.
* - if both differences are strictly superior to the MAX_DELTA_BUFFER_TIME,
* then they are not considered to have the same start nor the same end.
* They can still overlap however, and MIGHT thus still replace partially
* or completely each other.
*
* Setting a value too low might lead to two segments targeting the same time,
* both being present in the buffer. In worst case scenarios, this could lead
* to indicate that an unwanted text track is still here (theorically though,
* this is a case that should never happen for reasons that might be too long
* to explain here).
*
* Setting a value too high might lead to two segments targeting different times
* to be wrongly believed to target the same time. In worst case scenarios, this
* could lead to wanted text tracks being removed.
*
* When comparing 2 segments s1 and s2, you may want to take into account the duration
* of the segments:
* - if s1 is [0, 2] and s2 is [0, 2.1] s1 and s2 can be considered as nearly equal as
* there is a relative difference of: (2.1-2) / 2 = 5%;
* Formula: (end_s1 - end_s2) / duration_s2 = relative_difference
* - if s1 is [0, 0.04] and s2 is [0.04, 0.08] s1 and s2 may not considered as nearly
* equal as there is a relative difference of: (0.04-0.08) / 0.04 = 100%
*
* To compare relatively to the duration of a segment you can provide and additional
* parameter "delta" that remplace MAX_DELTA_BUFFER_TIME.
* If parameter "delta" is higher than MAX_DELTA_BUFFER_TIME, MAX_DELTA_BUFFER_TIME
* is used instead of delta. This ensure that segments are nearly equal when comparing
* relatively AND absolutely.
*
* @type Number
*/
const MAX_DELTA_BUFFER_TIME = 0.2;
/**
* @see MAX_DELTA_BUFFER_TIME
* @param {Number} a
* @param {Number} b
* @param {Number} delta
* @returns {Boolean}
*/
export function areNearlyEqual(
a: number,
b: number,
delta: number = MAX_DELTA_BUFFER_TIME,
): boolean {
return Math.abs(a - b) <= Math.min(delta, MAX_DELTA_BUFFER_TIME);
}
const EPSILON = 5e-2; // 5%
/**
* Check if two cues start are almost the same.
* It should depend on there relative length:
*
* [0, 2] and [2, 4] start are NOT equals
* [0, 2] and [0, 4] start are equals
* [0, 0.1] and [0.101, 2] start are NOT equals
* [0, 2] and [0.01, 4] start are equals
* [0, 100] and [1, 200] start are NOT equals
* @see MAX_DELTA_BUFFER_TIME
* @param {Number} firstCue the existing cue
* @param {Number} secondCue the cue that we test if it follow firstCue
* @returns {Boolean}
*/
export function areCuesStartNearlyEqual(
firstCue: ICuesGroup,
secondCue: ICuesGroup,
): boolean {
const firstCueDuration = firstCue.end - firstCue.start;
const secondCueDuration = secondCue.end - secondCue.start;
const diffBetweenStart = Math.abs(firstCue.start - secondCue.start);
const minDuration = Math.min(
firstCueDuration,
secondCueDuration,
MAX_DELTA_BUFFER_TIME,
);
return diffBetweenStart / minDuration <= EPSILON; // ratio diff/ minduration is bellow 5%
}
/**
* Get all cues which have data before the given time.
* @param {Object} cues
* @param {Number} time
* @returns {Array.<Object>}
*/
export function getCuesBefore(cues: IHTMLCue[], time: number): IHTMLCue[] {
for (let i = cues.length - 1; i >= 0; i--) {
const cue = cues[i];
if (cue.start < time) {
return cues.slice(0, i + 1);
}
}
return [];
}
/**
* Get all cues which have data after the given time.
* @param {Object} cues
* @param {Number} time
* @returns {Array.<Object>}
*/
export function getCuesAfter(cues: IHTMLCue[], time: number): IHTMLCue[] {
for (let i = 0; i < cues.length; i++) {
const cue = cues[i];
if (cue.end > time) {
return cues.slice(i, cues.length);
}
}
return [];
}
/**
* @param {Object} cuesInfos
* @param {Number} start
* @param {Number} end
* @returns {Array.<Object>}
*/
export function removeCuesInfosBetween(
cuesInfos: ICuesGroup,
start: number,
end: number,
): [ICuesGroup, ICuesGroup] {
const endCuesInfos1 = Math.max(cuesInfos.start, start);
const cues1 = getCuesBefore(cuesInfos.cues, start);
const cuesInfos1 = {
start: cuesInfos.start,
end: endCuesInfos1,
cues: cues1,
};
const startCuesInfos2 = Math.min(end, cuesInfos.end);
const cues2 = getCuesAfter(cuesInfos.cues, end);
const cuesInfos2 = {
start: startCuesInfos2,
end: cuesInfos.end,
cues: cues2,
};
return [cuesInfos1, cuesInfos2];
}