rx-player
Version:
Canal+ HTML5 Video Player
127 lines (118 loc) • 4.35 kB
text/typescript
/**
* Copyright 2015 CANAL+ Group
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { areSameContent } from "../../../manifest";
import getMonotonicTimeStamp from "../../../utils/monotonic_timestamp";
import type { IChunkContext } from "./types";
/**
* Entry of a `BufferedHistory`, added when the initial buffered range of a
* **full segment** (and not just of a sub-chunk of a segment like in some
* low-latency streaming usages) in the buffer becomes known.
*/
export interface IBufferedHistoryEntry {
/** The RxPlayer's monotonically-raising timestamp when the event happened. */
date: number;
/**
* Timestamps of what has been buffered with that segment.
* `null` if it has been immediately garbage collected.
*/
buffered: null | {
/** Start time at which we observed that segment/chunk starts at, in seconds. */
start: number;
/** *End time at which we observed that segment/chunk ends at, in seconds. */
end: number;
};
/** Content metadata linked to the segment, allowing to recognize it. */
context: IChunkContext;
}
/**
* Register a short-lived history of buffer information.
*
* This class can be useful to develop heuristics based on short-term buffer
* history, such as knowing the real start and end of a buffered segment once
* it has been pushed in a buffer.
*
* By storing in a history important recent actions and events, the
* `BufferedHistory` can help other RxPlayer modules detect and work-around
* unusual behavior.
*
* @class BufferedHistory
*/
export default class BufferedHistory {
/** Complete recent history in chronological order (from oldest to newest) */
private _history: IBufferedHistoryEntry[];
/** Maximum time a history entry should be retained. */
private _lifetime: number;
/** Maximum number of entries the `BufferedHistory`'s history should have. */
private _maxHistoryLength: number;
/**
* @param {number} lifetime - Maximum time a history entry should be retained.
* @param {number} maxHistoryLength - Maximum number of entries the history
* should have.
*/
constructor(lifetime: number, maxHistoryLength: number) {
this._history = [];
this._lifetime = lifetime;
this._maxHistoryLength = maxHistoryLength;
}
/**
* Add an entry to the `BufferedHistory`'s history indicating the buffered
* range of a pushed segment.
*
* To call when the full range of a given segment becomes known.
*
* @param {Object} context
* @param {Array.<number>|null} buffered
*/
public addBufferedSegment(
context: IChunkContext,
buffered: { start: number; end: number } | null,
): void {
const now = getMonotonicTimeStamp();
this._history.push({ date: now, buffered, context });
this._cleanHistory(now);
}
/**
* Returns all entries linked to the given segment.
* @param {Object} context
* @returns {Array.<Object>}
*/
public getHistoryFor(context: IChunkContext): IBufferedHistoryEntry[] {
return this._history.filter((el) => areSameContent(el.context, context));
}
/**
* If the current history does not satisfy `_lifetime` or `_maxHistoryLength`,
* clear older entries until it does.
* @param {number} now - Current monotonically-raising timestamp.
*/
private _cleanHistory(now: number) {
const historyEarliestLimit = now - this._lifetime;
let firstKeptIndex = 0;
for (const event of this._history) {
if (event.date < historyEarliestLimit) {
firstKeptIndex++;
} else {
break;
}
}
if (firstKeptIndex > 0) {
this._history = this._history.splice(firstKeptIndex);
}
if (this._history.length > this._maxHistoryLength) {
const toRemove = this._history.length - this._maxHistoryLength;
this._history = this._history.splice(toRemove);
}
}
}