download
raw
5.17 kB
/**
* Provides methods dealing with buffer length retrieval for example.
*
* In general, a helper around HTML5 MediaElement TimeRanges gathered from `buffered` property.
*
* Also @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/buffered
*/
import { logger } from './logger';
export type BufferTimeRange = {
start: number;
end: number;
};
export type Bufferable = {
buffered: TimeRanges;
};
export type BufferInfo = {
len: number;
start: number;
end: number;
nextStart?: number;
buffered?: BufferTimeRange[];
bufferedIndex: number;
};
const noopBuffered: TimeRanges = {
length: 0,
start: () => 0,
end: () => 0,
};
export class BufferHelper {
/**
* Return true if `media`'s buffered include `position`
*/
static isBuffered(media: Bufferable, position: number): boolean {
if (media) {
const buffered = BufferHelper.getBuffered(media);
for (let i = buffered.length; i--; ) {
if (position >= buffered.start(i) && position <= buffered.end(i)) {
return true;
}
}
}
return false;
}
static bufferedRanges(media: Bufferable | null): BufferTimeRange[] {
if (media) {
const timeRanges = BufferHelper.getBuffered(media);
return BufferHelper.timeRangesToArray(timeRanges);
}
return [];
}
static timeRangesToArray(timeRanges: TimeRanges): BufferTimeRange[] {
const buffered: BufferTimeRange[] = [];
for (let i = 0; i < timeRanges.length; i++) {
buffered.push({ start: timeRanges.start(i), end: timeRanges.end(i) });
}
return buffered;
}
static bufferInfo(
media: Bufferable | null,
pos: number,
maxHoleDuration: number,
): BufferInfo {
if (media) {
const buffered = BufferHelper.bufferedRanges(media);
if (buffered.length) {
return BufferHelper.bufferedInfo(buffered, pos, maxHoleDuration);
}
}
return { len: 0, start: pos, end: pos, bufferedIndex: -1 };
}
static bufferedInfo(
buffered: BufferTimeRange[],
pos: number,
maxHoleDuration: number,
): BufferInfo {
pos = Math.max(0, pos);
// sort on buffer.start/smaller end (IE does not always return sorted buffered range)
if (buffered.length > 1) {
buffered.sort((a, b) => a.start - b.start || b.end - a.end);
}
let bufferedIndex: number = -1;
let buffered2: BufferTimeRange[] = [];
if (maxHoleDuration) {
// there might be some small holes between buffer time range
// consider that holes smaller than maxHoleDuration are irrelevant and build another
// buffer time range representations that discards those holes
for (let i = 0; i < buffered.length; i++) {
if (pos >= buffered[i].start && pos <= buffered[i].end) {
bufferedIndex = i;
}
const buf2len = buffered2.length;
if (buf2len) {
const buf2end = buffered2[buf2len - 1].end;
// if small hole (value between 0 or maxHoleDuration ) or overlapping (negative)
if (buffered[i].start - buf2end < maxHoleDuration) {
// merge overlapping time ranges
// update lastRange.end only if smaller than item.end
// e.g. [ 1, 15] with [ 2,8] => [ 1,15] (no need to modify lastRange.end)
// whereas [ 1, 8] with [ 2,15] => [ 1,15] ( lastRange should switch from [1,8] to [1,15])
if (buffered[i].end > buf2end) {
buffered2[buf2len - 1].end = buffered[i].end;
}
} else {
// big hole
buffered2.push(buffered[i]);
}
} else {
// first value
buffered2.push(buffered[i]);
}
}
} else {
buffered2 = buffered;
}
let bufferLen = 0;
let nextStart: number | undefined;
// bufferStart and bufferEnd are buffer boundaries around current playback position (pos)
let bufferStart: number = pos;
let bufferEnd: number = pos;
for (let i = 0; i < buffered2.length; i++) {
const start = buffered2[i].start;
const end = buffered2[i].end;
// logger.log('buf start/end:' + buffered.start(i) + '/' + buffered.end(i));
if (bufferedIndex === -1 && pos >= start && pos <= end) {
bufferedIndex = i;
}
if (pos + maxHoleDuration >= start && pos < end) {
// play position is inside this buffer TimeRange, retrieve end of buffer position and buffer length
bufferStart = start;
bufferEnd = end;
bufferLen = bufferEnd - pos;
} else if (pos + maxHoleDuration < start) {
nextStart = start;
break;
}
}
return {
len: bufferLen,
start: bufferStart || 0,
end: bufferEnd || 0,
nextStart,
buffered,
bufferedIndex,
};
}
/**
* Safe method to get buffered property.
* SourceBuffer.buffered may throw if SourceBuffer is removed from it's MediaSource
*/
static getBuffered(media: Bufferable): TimeRanges {
try {
return media.buffered || noopBuffered;
} catch (e) {
logger.log('failed to get media.buffered', e);
return noopBuffered;
}
}
}

Xet Storage Details

Size:
5.17 kB
·
Xet hash:
c491ab58b1a3aeed6c75c704d73fd332c4e8f75d85b066d3a1da92ca3812dc57

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.