You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
|
|
/** * @file source-updater.js */ import videojs from 'video.js'; import { printableRange } from './ranges'; import logger from './util/logger'; import noop from './util/noop';
/** * A queue of callbacks to be serialized and applied when a * MediaSource and its associated SourceBuffers are not in the * updating state. It is used by the segment loader to update the * underlying SourceBuffers when new data is loaded, for instance. * * @class SourceUpdater * @param {MediaSource} mediaSource the MediaSource to create the * SourceBuffer from * @param {String} mimeType the desired MIME type of the underlying * SourceBuffer * @param {Object} sourceBufferEmitter an event emitter that fires when a source buffer is * added to the media source */ export default class SourceUpdater { constructor(mediaSource, mimeType, type, sourceBufferEmitter) { this.callbacks_ = []; this.pendingCallback_ = null; this.timestampOffset_ = 0; this.mediaSource = mediaSource; this.processedAppend_ = false; this.type_ = type; this.mimeType_ = mimeType; this.logger_ = logger(`SourceUpdater[${type}][${mimeType}]`);
if (mediaSource.readyState === 'closed') { mediaSource.addEventListener( 'sourceopen', this.createSourceBuffer_.bind(this, mimeType, sourceBufferEmitter)); } else { this.createSourceBuffer_(mimeType, sourceBufferEmitter); } }
createSourceBuffer_(mimeType, sourceBufferEmitter) { this.sourceBuffer_ = this.mediaSource.addSourceBuffer(mimeType);
this.logger_('created SourceBuffer');
if (sourceBufferEmitter) { sourceBufferEmitter.trigger('sourcebufferadded');
if (this.mediaSource.sourceBuffers.length < 2) { // There's another source buffer we must wait for before we can start updating
// our own (or else we can get into a bad state, i.e., appending video/audio data
// before the other video/audio source buffer is available and leading to a video
// or audio only buffer).
sourceBufferEmitter.on('sourcebufferadded', () => { this.start_(); }); return; } }
this.start_(); }
start_() { this.started_ = true;
// run completion handlers and process callbacks as updateend
// events fire
this.onUpdateendCallback_ = () => { let pendingCallback = this.pendingCallback_;
this.pendingCallback_ = null;
this.logger_(`buffered [${printableRange(this.buffered())}]`);
if (pendingCallback) { pendingCallback(); }
this.runCallback_(); };
this.sourceBuffer_.addEventListener('updateend', this.onUpdateendCallback_);
this.runCallback_(); }
/** * Aborts the current segment and resets the segment parser. * * @param {Function} done function to call when done * @see http://w3c.github.io/media-source/#widl-SourceBuffer-abort-void
*/ abort(done) { if (this.processedAppend_) { this.queueCallback_(() => { this.sourceBuffer_.abort(); }, done); } }
/** * Queue an update to append an ArrayBuffer. * * @param {ArrayBuffer} bytes * @param {Function} done the function to call when done * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-appendBuffer-void-ArrayBuffer-data
*/ appendBuffer(bytes, done) { this.processedAppend_ = true; this.queueCallback_(() => { this.sourceBuffer_.appendBuffer(bytes); }, done); }
/** * Indicates what TimeRanges are buffered in the managed SourceBuffer. * * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-buffered
*/ buffered() { if (!this.sourceBuffer_) { return videojs.createTimeRanges(); } return this.sourceBuffer_.buffered; }
/** * Queue an update to remove a time range from the buffer. * * @param {Number} start where to start the removal * @param {Number} end where to end the removal * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-remove-void-double-start-unrestricted-double-end
*/ remove(start, end) { if (this.processedAppend_) { this.queueCallback_(() => { this.logger_(`remove [${start} => ${end}]`); this.sourceBuffer_.remove(start, end); }, noop); } }
/** * Whether the underlying sourceBuffer is updating or not * * @return {Boolean} the updating status of the SourceBuffer */ updating() { return !this.sourceBuffer_ || this.sourceBuffer_.updating || this.pendingCallback_; }
/** * Set/get the timestampoffset on the SourceBuffer * * @return {Number} the timestamp offset */ timestampOffset(offset) { if (typeof offset !== 'undefined') { this.queueCallback_(() => { this.sourceBuffer_.timestampOffset = offset; }); this.timestampOffset_ = offset; } return this.timestampOffset_; }
/** * Queue a callback to run */ queueCallback_(callback, done) { this.callbacks_.push([callback.bind(this), done]); this.runCallback_(); }
/** * Run a queued callback */ runCallback_() { let callbacks;
if (!this.updating() && this.callbacks_.length && this.started_) { callbacks = this.callbacks_.shift(); this.pendingCallback_ = callbacks[1]; callbacks[0](); } }
/** * dispose of the source updater and the underlying sourceBuffer */ dispose() { this.sourceBuffer_.removeEventListener('updateend', this.onUpdateendCallback_); if (this.sourceBuffer_ && this.mediaSource.readyState === 'open') { this.sourceBuffer_.abort(); } } }
|