mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-25 17:45:03 +03:00
5637962073
Also now uses the same ContentDatabaseReader for offline segment requests. Change-Id: I9cebc90b46ca9067467a8a343d48b264abc7cf26
667 lines
20 KiB
JavaScript
667 lines
20 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2015 Google Inc.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
goog.provide('shaka.media.SourceBufferManager');
|
|
|
|
goog.require('shaka.asserts');
|
|
goog.require('shaka.player.Defaults');
|
|
goog.require('shaka.util.ContentDatabaseReader');
|
|
goog.require('shaka.util.EventManager');
|
|
goog.require('shaka.util.IBandwidthEstimator');
|
|
goog.require('shaka.util.PublicPromise');
|
|
goog.require('shaka.util.Task');
|
|
goog.require('shaka.util.TypedBind');
|
|
|
|
|
|
|
|
/**
|
|
* Creates a SourceBufferManager (SBM), which manages a SourceBuffer and
|
|
* provides an enhanced interface based on Promises.
|
|
*
|
|
* The SBM manages access to a SourceBuffer object through a fetch operation
|
|
* and a clear operation. It also maintains a "virtual source buffer" to keep
|
|
* track of which segments have been appended to the actual underlying
|
|
* SourceBuffer. The SBM uses this virtual source buffer because it cannot rely
|
|
* on the browser to tell it what is in the underlying SourceBuffer because a
|
|
* SegmentIndex may use PTS (presentation timestamps) and a browser may use
|
|
* DTS (decoding timestamps) or vice-versa.
|
|
*
|
|
* @param {!MediaSource} mediaSource The MediaSource, which must be in the
|
|
* 'open' state.
|
|
* @param {string} fullMimeType The full MIME type of the SourceBuffer to
|
|
* create, which |mediaSource| must support.
|
|
* @param {!shaka.util.IBandwidthEstimator} estimator A bandwidth estimator to
|
|
* attach to all requests.
|
|
*
|
|
* @throws {QuotaExceededError} if no more SourceBuffers are allowed.
|
|
*
|
|
* @struct
|
|
* @constructor
|
|
*/
|
|
shaka.media.SourceBufferManager = function(
|
|
mediaSource, fullMimeType, estimator) {
|
|
shaka.asserts.assert(mediaSource.readyState == 'open',
|
|
'The MediaSource should be in the \'open\' state.');
|
|
shaka.asserts.assert(fullMimeType.length > 0);
|
|
shaka.asserts.assert(MediaSource.isTypeSupported(fullMimeType));
|
|
|
|
var sourceBuffer = mediaSource.addSourceBuffer(fullMimeType);
|
|
shaka.asserts.assert(sourceBuffer, 'SourceBuffer should not be null.');
|
|
|
|
/** @private {!MediaSource} */
|
|
this.mediaSource_ = mediaSource;
|
|
|
|
/** @private {!SourceBuffer} */
|
|
this.sourceBuffer_ = /** @type {!SourceBuffer} */(sourceBuffer);
|
|
|
|
/** @private {!shaka.util.IBandwidthEstimator} */
|
|
this.estimator_ = estimator;
|
|
|
|
/** @private {!shaka.util.EventManager} */
|
|
this.eventManager_ = new shaka.util.EventManager();
|
|
|
|
/** @private {shaka.util.ContentDatabaseReader} */
|
|
this.contentDatabase_ = null;
|
|
|
|
/**
|
|
* Contains a list of segments that have been inserted into the SourceBuffer.
|
|
* These segments may or may not have been evicted by the browser.
|
|
* @private {!Array.<!shaka.media.SegmentReference>}
|
|
*/
|
|
this.inserted_ = [];
|
|
|
|
/** @private {number} */
|
|
this.timestampCorrection_ = 0;
|
|
|
|
/** @private {shaka.util.Task} */
|
|
this.task_ = null;
|
|
|
|
/** @private {shaka.util.PublicPromise} */
|
|
this.operationPromise_ = null;
|
|
|
|
/** @private {number} */
|
|
this.segmentRequestTimeout_ = shaka.player.Defaults.SEGMENT_REQUEST_TIMEOUT;
|
|
|
|
// For debugging purposes:
|
|
if (!COMPILED) {
|
|
/** @private {string} */
|
|
this.mimeType_ = fullMimeType.split(';')[0];
|
|
shaka.asserts.assert(this.mimeType_.length > 0);
|
|
}
|
|
|
|
this.eventManager_.listen(
|
|
this.sourceBuffer_,
|
|
'updateend',
|
|
this.onSourceBufferUpdateEnd_.bind(this));
|
|
};
|
|
|
|
|
|
/**
|
|
* A fudge factor to apply to buffered ranges to account for rounding error.
|
|
* @const {number}
|
|
* @private
|
|
*/
|
|
shaka.media.SourceBufferManager.FUDGE_FACTOR_ = 1 / 60;
|
|
|
|
|
|
/**
|
|
* Destroys the SourceBufferManager.
|
|
* @suppress {checkTypes} to set otherwise non-nullable types to null.
|
|
*/
|
|
shaka.media.SourceBufferManager.prototype.destroy = function() {
|
|
this.abort().catch(function() {});
|
|
|
|
if (this.operationPromise_) {
|
|
this.operationPromise_.destroy();
|
|
}
|
|
this.operationPromise_ = null;
|
|
this.task_ = null;
|
|
|
|
if (this.contentDatabase_) {
|
|
this.contentDatabase_.closeDatabaseConnection();
|
|
this.contentDatabase_ = null;
|
|
}
|
|
|
|
this.inserted_ = null;
|
|
|
|
this.eventManager_.destroy();
|
|
this.eventManager_ = null;
|
|
|
|
this.sourceBuffer_ = null;
|
|
this.mediaSource_ = null;
|
|
};
|
|
|
|
|
|
/**
|
|
* Checks if the given timestamp is buffered according to the virtual source
|
|
* buffer.
|
|
*
|
|
* Note that as a SegmentIndex may use PTS and a browser may use DTS or
|
|
* vice-versa, and due to MSE implementation details, isInserted(t) does not
|
|
* imply isBuffered(t) nor does isBuffered(t) imply isInserted(t).
|
|
*
|
|
* @param {number} timestamp The timestamp in seconds.
|
|
* @return {boolean} True if the timestamp is buffered.
|
|
*/
|
|
shaka.media.SourceBufferManager.prototype.isInserted = function(timestamp) {
|
|
return shaka.media.SegmentReference.find(this.inserted_, timestamp) >= 0;
|
|
};
|
|
|
|
|
|
/**
|
|
* Gets the SegmentReference corresponding to the last inserted segment.
|
|
*
|
|
* @return {shaka.media.SegmentReference}
|
|
*/
|
|
shaka.media.SourceBufferManager.prototype.getLastInserted = function() {
|
|
var length = this.inserted_.length;
|
|
return length > 0 ? this.inserted_[length - 1] : null;
|
|
};
|
|
|
|
|
|
/**
|
|
* Checks if the given timestamp is buffered according to the underlying
|
|
* SourceBuffer.
|
|
*
|
|
* @param {number} timestamp The timestamp in seconds.
|
|
* @return {boolean} True if the timestamp is buffered.
|
|
*/
|
|
shaka.media.SourceBufferManager.prototype.isBuffered = function(timestamp) {
|
|
return this.bufferedAheadOf(timestamp) > 0;
|
|
};
|
|
|
|
|
|
/**
|
|
* Computes how far ahead of the given timestamp we have buffered according to
|
|
* the underlying SourceBuffer.
|
|
*
|
|
* @param {number} timestamp The timestamp in seconds.
|
|
* @return {number} in seconds
|
|
*/
|
|
shaka.media.SourceBufferManager.prototype.bufferedAheadOf =
|
|
function(timestamp) {
|
|
var b = this.sourceBuffer_.buffered;
|
|
for (var i = 0; i < b.length; ++i) {
|
|
var start = b.start(i) - shaka.media.SourceBufferManager.FUDGE_FACTOR_;
|
|
var end = b.end(i) + shaka.media.SourceBufferManager.FUDGE_FACTOR_;
|
|
if (timestamp >= start && timestamp <= end) {
|
|
return b.end(i) - timestamp;
|
|
}
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
|
|
/**
|
|
* Fetches the segment corresponding to the given SegmentReference and appends
|
|
* the it to the underlying SourceBuffer. This cannot be called if another
|
|
* operation is in progress.
|
|
*
|
|
* @param {!shaka.media.SegmentReference} reference
|
|
* @param {number} timestampOffset An offset, in seconds, that will be applied
|
|
* to each timestamp within the segment before appending it to the
|
|
* underlying SourceBuffer.
|
|
* @param {ArrayBuffer} initData Optional initialization segment that
|
|
* will be appended to the underlying SourceBuffer before the retrieved
|
|
* segment.
|
|
* @return {!Promise.<?number>} A promise to a timestamp correction, which may
|
|
* be null if a timestamp correction could not be computed. A timestamp
|
|
* correction is computed if the underlying SourceBuffer is initially
|
|
* empty. The timestamp correction, if one is computed, is not
|
|
* automatically applied to the virtual source buffer; to apply a timestamp
|
|
* correction, call correct().
|
|
*/
|
|
shaka.media.SourceBufferManager.prototype.fetch = function(
|
|
reference, timestampOffset, initData) {
|
|
shaka.log.v1(this.logPrefix_(), 'fetch');
|
|
|
|
// Check state.
|
|
shaka.asserts.assert(!this.task_);
|
|
if (this.task_) {
|
|
var error = new Error('Cannot fetch (' + this.mimeType_ + '): ' +
|
|
'previous operation not complete.');
|
|
error.type = 'stream';
|
|
return Promise.reject(error);
|
|
}
|
|
|
|
this.task_ = new shaka.util.Task();
|
|
|
|
if (timestampOffset != this.sourceBuffer_.timestampOffset) {
|
|
shaka.log.debug(
|
|
this.logPrefix_(), 'setting timestampOffset to', timestampOffset);
|
|
this.sourceBuffer_.timestampOffset = timestampOffset;
|
|
}
|
|
|
|
if (reference.url.isOfflineUri() && !this.contentDatabase_) {
|
|
this.contentDatabase_ = new shaka.util.ContentDatabaseReader();
|
|
this.task_.append(
|
|
function() {
|
|
return [this.contentDatabase_.setUpDatabase()];
|
|
}.bind(this));
|
|
}
|
|
|
|
if (initData) {
|
|
this.task_.append(
|
|
function() {
|
|
return [this.append_(/** @type {!ArrayBuffer} */(initData)),
|
|
this.abort_.bind(this)];
|
|
}.bind(this));
|
|
}
|
|
|
|
this.task_.append(
|
|
function() {
|
|
var refDuration =
|
|
reference.endTime ? (reference.endTime - reference.startTime) : 1;
|
|
var params = new shaka.util.AjaxRequest.Parameters();
|
|
params.maxAttempts = 3;
|
|
params.baseRetryDelayMs = refDuration * 1000;
|
|
params.requestTimeoutMs = this.segmentRequestTimeout_ * 1000;
|
|
params.contentDatabase = this.contentDatabase_;
|
|
return [
|
|
reference.url.fetch(params, this.estimator_),
|
|
shaka.util.FailoverUri.prototype.abortFetch.bind(reference.url)];
|
|
}.bind(this));
|
|
|
|
// Sanity check: appendBuffer() should not modify the MediaSource's duration
|
|
// because an appropriate append window should have been set.
|
|
//
|
|
// On some browsers, even with an append window, inserting a segment that
|
|
// ends past the end of the append window can increase the MediaSource's
|
|
// duration (slightly). However, when this occurs it appears that no content
|
|
// is actually buffered past the end of the append window, and subsequently
|
|
// calling endOfStream() resets the MediaSource's duration to the correct
|
|
// value (i.e., the end of the append window).
|
|
//
|
|
// TODO: Determine if this is a browser bug or is actually compliant with the
|
|
// MSE spec.
|
|
if (!COMPILED) {
|
|
var durationBefore;
|
|
}
|
|
|
|
this.task_.append(shaka.util.TypedBind(this,
|
|
/** @param {!ArrayBuffer} data */
|
|
function(data) {
|
|
if (!COMPILED) {
|
|
durationBefore = this.mediaSource_.duration;
|
|
}
|
|
|
|
shaka.log.debug('Estimated bandwidth:',
|
|
(this.estimator_.getBandwidth() / 1e6).toFixed(2), 'Mbps');
|
|
return [this.append_(data), this.abort_.bind(this)];
|
|
}));
|
|
|
|
var computeTimestampCorrection =
|
|
this.sourceBuffer_.buffered.length == 0 &&
|
|
this.inserted_.length == 0;
|
|
|
|
/** @type {?number} */
|
|
var timestampCorrection = null;
|
|
|
|
this.task_.append(
|
|
function() {
|
|
if (!COMPILED) {
|
|
var durationAfter = this.mediaSource_.duration;
|
|
if (durationAfter != durationBefore) {
|
|
shaka.log.warning(
|
|
this.logPrefix_(),
|
|
'appendBuffer() should not modify the MediaSource\'s duration:',
|
|
'before', durationBefore,
|
|
'after', durationAfter,
|
|
'delta', durationAfter - durationBefore);
|
|
}
|
|
}
|
|
|
|
if (this.sourceBuffer_.buffered.length == 0) {
|
|
var error = new Error(
|
|
'Failed to buffer segment (' + this.mimeType_ + ').');
|
|
error.type = 'stream';
|
|
return [Promise.reject(error)];
|
|
}
|
|
|
|
if (computeTimestampCorrection) {
|
|
shaka.asserts.assert(this.inserted_.length == 0);
|
|
var expectedTimestamp = reference.startTime;
|
|
var actualTimestamp = this.sourceBuffer_.buffered.start(0);
|
|
timestampCorrection = actualTimestamp - expectedTimestamp;
|
|
}
|
|
|
|
var i = shaka.media.SegmentReference.find(
|
|
this.inserted_, reference.startTime);
|
|
if (i >= 0) {
|
|
// The SegmentReference at i has a start time less than |reference|'s.
|
|
this.inserted_.splice(i + 1, 0, reference);
|
|
} else {
|
|
this.inserted_.push(reference);
|
|
}
|
|
}.bind(this));
|
|
|
|
return this.startTask_().then(
|
|
function() {
|
|
return Promise.resolve(timestampCorrection);
|
|
}.bind(this));
|
|
};
|
|
|
|
|
|
/**
|
|
* Resets the virtual source buffer and clears all media from the underlying
|
|
* SourceBuffer. The returned promise will resolve immediately if there is no
|
|
* media within the underlying SourceBuffer. This cannot be called if another
|
|
* operation is in progress.
|
|
*
|
|
* @return {!Promise}
|
|
*/
|
|
shaka.media.SourceBufferManager.prototype.clear = function() {
|
|
shaka.log.v1(this.logPrefix_(), 'clear');
|
|
|
|
// Check state.
|
|
shaka.asserts.assert(!this.task_);
|
|
if (this.task_) {
|
|
var error = new Error('Cannot clear (' + this.mimeType_ + '): ' +
|
|
'previous operation not complete.');
|
|
error.type = 'stream';
|
|
return Promise.reject(error);
|
|
}
|
|
|
|
this.task_ = new shaka.util.Task();
|
|
|
|
this.task_.append(function() {
|
|
var p = this.clear_();
|
|
return [p, this.abort_.bind(this)];
|
|
}.bind(this));
|
|
|
|
return this.startTask_();
|
|
};
|
|
|
|
|
|
/**
|
|
* Resets the virtual source buffer and clears all media from the underlying
|
|
* SourceBuffer after the given timestamp. The returned promise will resolve
|
|
* immediately if there is no media within the underlying SourceBuffer. This
|
|
* cannot be called if another operation is in progress.
|
|
*
|
|
* @param {number} timestamp
|
|
*
|
|
* @return {!Promise}
|
|
*/
|
|
shaka.media.SourceBufferManager.prototype.clearAfter = function(timestamp) {
|
|
shaka.log.v1(this.logPrefix_(), 'clearAfter');
|
|
|
|
// Check state.
|
|
shaka.asserts.assert(!this.task_);
|
|
if (this.task_) {
|
|
var error = new Error('Cannot clearAfter (' + this.mimeType_ + '): ' +
|
|
'previous operation not complete.');
|
|
error.type = 'stream';
|
|
return Promise.reject(error);
|
|
}
|
|
|
|
this.task_ = new shaka.util.Task();
|
|
|
|
this.task_.append(function() {
|
|
var p = this.clearAfter_(timestamp);
|
|
return [p, this.abort_.bind(this)];
|
|
}.bind(this));
|
|
|
|
return this.startTask_();
|
|
};
|
|
|
|
|
|
/**
|
|
* Aborts the current operation if one exists.
|
|
* The returned promise will never be rejected.
|
|
*
|
|
* @return {!Promise}
|
|
*/
|
|
shaka.media.SourceBufferManager.prototype.abort = function() {
|
|
shaka.log.v1(this.logPrefix_(), 'abort');
|
|
if (!this.task_) {
|
|
return Promise.resolve();
|
|
}
|
|
return this.task_.abort();
|
|
};
|
|
|
|
|
|
/**
|
|
* Corrects each SegmentReference in the virtual source buffer by the given
|
|
* timestamp correction. The previous timestamp correction, if it exists, is
|
|
* replaced.
|
|
*
|
|
* @param {number} timestampCorrection
|
|
*/
|
|
shaka.media.SourceBufferManager.prototype.correct = function(
|
|
timestampCorrection) {
|
|
var delta = timestampCorrection - this.timestampCorrection_;
|
|
if (delta == 0) {
|
|
return;
|
|
}
|
|
|
|
this.inserted_ = shaka.media.SegmentReference.shift(this.inserted_, delta);
|
|
this.timestampCorrection_ = timestampCorrection;
|
|
|
|
shaka.log.debug(
|
|
this.logPrefix_(),
|
|
'applied timestamp correction of',
|
|
timestampCorrection,
|
|
'seconds to SourceBufferManager',
|
|
this);
|
|
};
|
|
|
|
|
|
/**
|
|
* Emits an error message and returns true if there are multiple buffered
|
|
* ranges; otherwise, does nothing and returns false.
|
|
*
|
|
* @return {boolean}
|
|
*/
|
|
shaka.media.SourceBufferManager.prototype.detectMultipleBufferedRanges =
|
|
function() {
|
|
if (this.sourceBuffer_.buffered.length > 1) {
|
|
shaka.log.error(
|
|
this.logPrefix_(),
|
|
'multiple buffered ranges detected:',
|
|
'Either the content has gaps in it,',
|
|
'the content\'s segments are not aligned across bitrates,',
|
|
'or the browser has evicted the middle of the buffer.');
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets the segment request timeout in seconds.
|
|
*
|
|
* @param {number} timeout
|
|
*/
|
|
shaka.media.SourceBufferManager.prototype.setSegmentRequestTimeout =
|
|
function(timeout) {
|
|
shaka.asserts.assert(!isNaN(timeout));
|
|
this.segmentRequestTimeout_ = timeout;
|
|
};
|
|
|
|
|
|
/**
|
|
* Starts the task and returns a Promise which is resolved/rejected after the
|
|
* task ends and is cleaned up.
|
|
*
|
|
* @return {!Promise}
|
|
* @private
|
|
*/
|
|
shaka.media.SourceBufferManager.prototype.startTask_ = function() {
|
|
shaka.asserts.assert(this.task_);
|
|
this.task_.start();
|
|
return this.task_.getPromise().then(shaka.util.TypedBind(this,
|
|
function() {
|
|
this.task_ = null;
|
|
})
|
|
).catch(shaka.util.TypedBind(this,
|
|
/** @param {*} error */
|
|
function(error) {
|
|
shaka.log.v1(this.logPrefix_(), 'task failed!');
|
|
this.task_ = null;
|
|
return Promise.reject(error);
|
|
})
|
|
);
|
|
};
|
|
|
|
|
|
/**
|
|
* Append to the source buffer.
|
|
*
|
|
* @param {!ArrayBuffer} data
|
|
* @return {!Promise}
|
|
* @private
|
|
*/
|
|
shaka.media.SourceBufferManager.prototype.append_ = function(data) {
|
|
shaka.asserts.assert(!this.operationPromise_);
|
|
shaka.asserts.assert(this.task_);
|
|
|
|
try {
|
|
// This will trigger an 'updateend' event.
|
|
this.sourceBuffer_.appendBuffer(data);
|
|
} catch (exception) {
|
|
return Promise.reject(exception);
|
|
}
|
|
|
|
this.operationPromise_ = new shaka.util.PublicPromise();
|
|
return this.operationPromise_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Clear the source buffer.
|
|
*
|
|
* @return {!Promise}
|
|
* @private
|
|
*/
|
|
shaka.media.SourceBufferManager.prototype.clear_ = function() {
|
|
shaka.asserts.assert(!this.operationPromise_);
|
|
|
|
if (this.sourceBuffer_.buffered.length == 0) {
|
|
shaka.log.v1(this.logPrefix_(), 'nothing to clear.');
|
|
shaka.asserts.assert(this.inserted_.length == 0);
|
|
return Promise.resolve();
|
|
}
|
|
|
|
try {
|
|
// This will trigger an 'updateend' event.
|
|
this.sourceBuffer_.remove(0, Number.POSITIVE_INFINITY);
|
|
} catch (exception) {
|
|
return Promise.reject(exception);
|
|
}
|
|
|
|
// Clear |inserted_| immediately since any inserted segments will be
|
|
// gone soon.
|
|
this.inserted_ = [];
|
|
|
|
this.operationPromise_ = new shaka.util.PublicPromise();
|
|
return this.operationPromise_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Clear the source buffer after the given timestamp (aligned to the next
|
|
* segment boundary).
|
|
*
|
|
* @param {number} timestamp
|
|
*
|
|
* @return {!Promise}
|
|
* @private
|
|
*/
|
|
shaka.media.SourceBufferManager.prototype.clearAfter_ = function(timestamp) {
|
|
shaka.asserts.assert(!this.operationPromise_);
|
|
|
|
if (this.sourceBuffer_.buffered.length == 0) {
|
|
shaka.log.v1(this.logPrefix_(), 'nothing to clear.');
|
|
shaka.asserts.assert(this.inserted_.length == 0);
|
|
return Promise.resolve();
|
|
}
|
|
|
|
var index = shaka.media.SegmentReference.find(this.inserted_, timestamp);
|
|
|
|
// If no segment found, or it's the last one, bail out gracefully.
|
|
if (index == -1 || index == this.inserted_.length - 1) {
|
|
shaka.log.v1(
|
|
this.logPrefix_(),
|
|
'nothing to clear: no segments on or after timestamp.');
|
|
return Promise.resolve();
|
|
}
|
|
|
|
try {
|
|
// This will trigger an 'updateend' event.
|
|
this.sourceBuffer_.remove(
|
|
this.inserted_[index + 1].startTime,
|
|
Number.POSITIVE_INFINITY);
|
|
} catch (exception) {
|
|
return Promise.reject(exception);
|
|
}
|
|
|
|
this.inserted_ = this.inserted_.slice(0, index + 1);
|
|
|
|
this.operationPromise_ = new shaka.util.PublicPromise();
|
|
return this.operationPromise_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Abort the current operation on the source buffer.
|
|
*
|
|
* @private
|
|
*/
|
|
shaka.media.SourceBufferManager.prototype.abort_ = function() {
|
|
shaka.log.v1(this.logPrefix_(), 'abort_');
|
|
shaka.asserts.assert(this.operationPromise_);
|
|
|
|
// See {@link http://www.w3.org/TR/media-source/#widl-SourceBuffer-abort-void}
|
|
if (this.mediaSource_.readyState == 'open') {
|
|
this.sourceBuffer_.abort();
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* |sourceBuffer_|'s 'updateend' callback.
|
|
*
|
|
* @param {!Event} event
|
|
* @private
|
|
*/
|
|
shaka.media.SourceBufferManager.prototype.onSourceBufferUpdateEnd_ =
|
|
function(event) {
|
|
shaka.log.v1(this.logPrefix_(), 'onSourceBufferUpdateEnd_');
|
|
|
|
shaka.asserts.assert(!this.sourceBuffer_.updating);
|
|
shaka.asserts.assert(this.operationPromise_);
|
|
|
|
this.operationPromise_.resolve();
|
|
this.operationPromise_ = null;
|
|
};
|
|
|
|
|
|
if (!COMPILED) {
|
|
/**
|
|
* Returns a string with the form 'SBM MIME_TYPE:' for logging purposes.
|
|
*
|
|
* @return {string}
|
|
* @private
|
|
*/
|
|
shaka.media.SourceBufferManager.prototype.logPrefix_ = function() {
|
|
return 'SBM ' + this.mimeType_ + ':';
|
|
};
|
|
}
|
|
|