mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-26 17:46:26 +03:00
Choose live playback by type='dynamic'.
Also avoid fetching manifest updates when minimumUpdatePeriod is missing. In the absence of minimumUpdatePeriod, just reprocess the MPD we already downloaded. Closes #69, #70. Change-Id: I138bf136e5b0d691d9d625bbad02fb07ae21d32f
This commit is contained in:
+4
-14
@@ -78,8 +78,8 @@ shaka.dash.mpd.Mpd = function() {
|
||||
/** @type {?string} */
|
||||
this.id = null;
|
||||
|
||||
/** @type {?string} */
|
||||
this.type = null;
|
||||
/** @type {string} */
|
||||
this.type = 'static';
|
||||
|
||||
/** @type {goog.Uri} */
|
||||
this.baseUrl = null;
|
||||
@@ -99,7 +99,7 @@ shaka.dash.mpd.Mpd = function() {
|
||||
|
||||
/**
|
||||
* The interval, in seconds, to poll the media server for an updated
|
||||
* MPD, or null if updates are not required. This value is never zero.
|
||||
* MPD, or null if updates are not required.
|
||||
* @type {?number}
|
||||
*/
|
||||
this.minUpdatePeriod = null;
|
||||
@@ -821,7 +821,7 @@ shaka.dash.mpd.Mpd.prototype.parse = function(parent, elem) {
|
||||
|
||||
// Parse attributes.
|
||||
this.id = mpd.parseAttr_(elem, 'id', mpd.parseString_);
|
||||
this.type = mpd.parseAttr_(elem, 'type', mpd.parseString_);
|
||||
this.type = mpd.parseAttr_(elem, 'type', mpd.parseString_) || 'static';
|
||||
this.mediaPresentationDuration = mpd.parseAttr_(
|
||||
elem, 'mediaPresentationDuration', mpd.parseDuration_);
|
||||
this.minBufferTime =
|
||||
@@ -836,16 +836,6 @@ shaka.dash.mpd.Mpd.prototype.parse = function(parent, elem) {
|
||||
mpd.parseDuration_,
|
||||
this.minUpdatePeriod);
|
||||
|
||||
// If minimumUpdatePeriod is set to 0, then we shouldn't refresh the manifest
|
||||
// unless there is explicit signalling in the stream, according to:
|
||||
// http://azure.microsoft.com/blog/2014/09/13/dash-live-streaming-with-azure-media-service/
|
||||
// There is no way to get the signal from within the stream in MSE as of now.
|
||||
// So, if we see a minimumUpdatePeriod of zero, we merely set it to 10
|
||||
// seconds.
|
||||
if (this.minUpdatePeriod == 0) {
|
||||
this.minUpdatePeriod = 10;
|
||||
}
|
||||
|
||||
this.availabilityStartTime =
|
||||
mpd.parseAttr_(elem,
|
||||
'availabilityStartTime',
|
||||
|
||||
@@ -165,7 +165,7 @@ shaka.dash.MpdProcessor.prototype.calculateDurations_ = function(mpd) {
|
||||
var isSet = function(x) { return x == 0 || !!x; };
|
||||
|
||||
// @mediaPresentationDuration should only be used if the MPD is static.
|
||||
if (isSet(mpd.minUpdatePeriod)) {
|
||||
if (mpd.type != 'static') {
|
||||
mpd.mediaPresentationDuration = null;
|
||||
}
|
||||
|
||||
@@ -314,7 +314,7 @@ shaka.dash.MpdProcessor.prototype.filterAdaptationSet_ = function(
|
||||
* @private
|
||||
*/
|
||||
shaka.dash.MpdProcessor.prototype.createManifestInfo_ = function(mpd) {
|
||||
this.manifestInfo.live = mpd.minUpdatePeriod != null;
|
||||
this.manifestInfo.live = (mpd.type == 'dynamic');
|
||||
this.manifestInfo.minBufferTime = mpd.minBufferTime ||
|
||||
shaka.dash.MpdProcessor.DEFAULT_MIN_BUFFER_TIME;
|
||||
|
||||
@@ -394,7 +394,7 @@ shaka.dash.MpdProcessor.prototype.createManifestInfo_ = function(mpd) {
|
||||
//
|
||||
// TODO: Remove this hack once SourceBuffer synchronization is
|
||||
// implemented.
|
||||
if (mpd.minUpdatePeriod) {
|
||||
if (mpd.type == 'dynamic') {
|
||||
periodInfo.duration += 60 * 60 * 24 * 30;
|
||||
}
|
||||
}
|
||||
@@ -874,7 +874,7 @@ shaka.dash.MpdProcessor.prototype.buildStreamInfoFromSegmentTimeline_ =
|
||||
// complicated than the calculations in computeAvailableSegmentRange_() since
|
||||
// the duration of each segment is variable here.
|
||||
var earliestAvailableTimestamp = 0;
|
||||
if (mpd.minUpdatePeriod && (timeline.length > 0)) {
|
||||
if (mpd.type == 'dynamic' && timeline.length > 0) {
|
||||
var index = Math.max(0, timeline.length - 2);
|
||||
var timeShiftBufferDepth = mpd.timeShiftBufferDepth || 0;
|
||||
earliestAvailableTimestamp =
|
||||
@@ -949,7 +949,7 @@ shaka.dash.MpdProcessor.prototype.buildStreamInfoFromSegmentTimeline_ =
|
||||
-1 * segmentTemplate.presentationTimeOffset / segmentTemplate.timescale;
|
||||
}
|
||||
|
||||
if (mpd.minUpdatePeriod && (references.length > 0)) {
|
||||
if (mpd.type == 'dynamic' && references.length > 0) {
|
||||
var minBufferTime = this.manifestInfo.minBufferTime;
|
||||
var bestAvailableTimestamp =
|
||||
references[references.length - 1].startTime - minBufferTime;
|
||||
@@ -1097,7 +1097,7 @@ shaka.dash.MpdProcessor.prototype.buildStreamInfoFromSegmentDuration_ =
|
||||
// numbers are relative to the start of |period| unless marked otherwise.
|
||||
var earliestSegmentNumber;
|
||||
var currentSegmentNumber;
|
||||
if (mpd.minUpdatePeriod) {
|
||||
if (mpd.type == 'dynamic') {
|
||||
var pair = this.computeAvailableSegmentRange_(mpd, period, segmentTemplate);
|
||||
if (pair) {
|
||||
// Build the SegmentIndex starting from the earliest available segment.
|
||||
@@ -1195,7 +1195,7 @@ shaka.dash.MpdProcessor.prototype.buildStreamInfoFromSegmentDuration_ =
|
||||
-1 * segmentTemplate.presentationTimeOffset / segmentTemplate.timescale;
|
||||
}
|
||||
|
||||
if (mpd.minUpdatePeriod && (references.length > 0)) {
|
||||
if (mpd.type == 'dynamic' && references.length > 0) {
|
||||
shaka.asserts.assert(currentSegmentNumber);
|
||||
var scaledSegmentDuration =
|
||||
segmentTemplate.segmentDuration / segmentTemplate.timescale;
|
||||
|
||||
@@ -69,6 +69,9 @@ shaka.player.DashVideoSource =
|
||||
/** @private {string} */
|
||||
this.mpdUrl_ = mpdUrl;
|
||||
|
||||
/** @private {shaka.dash.mpd.Mpd} mpd */
|
||||
this.mpd_ = null;
|
||||
|
||||
/** @private {?shaka.player.DashVideoSource.ContentProtectionCallback} */
|
||||
this.interpretContentProtection_ = interpretContentProtection;
|
||||
|
||||
@@ -76,10 +79,10 @@ shaka.player.DashVideoSource =
|
||||
this.targetUpdateTime_ = null;
|
||||
|
||||
/**
|
||||
* The last time an MPD was fetched, in wall-clock time.
|
||||
* The last time the manifest was updated, in wall-clock time.
|
||||
* @private {?number}
|
||||
*/
|
||||
this.lastMpdFetchTime_ = null;
|
||||
this.lastUpdateTime_ = null;
|
||||
|
||||
/** @private {shaka.util.EWMA} */
|
||||
this.latencyEstimator_ = new shaka.util.EWMA(5 /* half-life in samples */);
|
||||
@@ -147,7 +150,7 @@ shaka.player.DashVideoSource.prototype.destroy = function() {
|
||||
|
||||
/** @override */
|
||||
shaka.player.DashVideoSource.prototype.load = function(preferredLanguage) {
|
||||
this.lastMpdFetchTime_ = Date.now() / 1000.0;
|
||||
this.lastUpdateTime_ = Date.now() / 1000.0;
|
||||
var mpdRequest = new shaka.dash.MpdRequest(this.mpdUrl_);
|
||||
|
||||
return mpdRequest.send().then(shaka.util.TypedBind(this,
|
||||
@@ -157,6 +160,7 @@ shaka.player.DashVideoSource.prototype.load = function(preferredLanguage) {
|
||||
new shaka.dash.MpdProcessor(this.interpretContentProtection_);
|
||||
mpdProcessor.process(mpd);
|
||||
|
||||
this.mpd_ = mpd;
|
||||
this.timeShiftBufferDepth_ = mpd.timeShiftBufferDepth || 0;
|
||||
this.manifestInfo = mpdProcessor.manifestInfo;
|
||||
|
||||
@@ -169,7 +173,7 @@ shaka.player.DashVideoSource.prototype.load = function(preferredLanguage) {
|
||||
this.sampleMpdLatency_();
|
||||
// Set a timer to call onUpdate_() so we update the manifest at
|
||||
// least every @minimumUpdatePeriod seconds.
|
||||
this.setUpdateTimer_(mpd.minUpdatePeriod || 0);
|
||||
this.setUpdateTimer_();
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -315,33 +319,45 @@ shaka.player.DashVideoSource.prototype.onPlay_ = function(event) {
|
||||
*/
|
||||
shaka.player.DashVideoSource.prototype.onUpdate_ = function() {
|
||||
shaka.asserts.assert(this.manifestInfo && this.manifestInfo.live);
|
||||
shaka.asserts.assert(this.mpd_);
|
||||
|
||||
this.cancelUpdateTimer_();
|
||||
|
||||
var currentTime = Date.now() / 1000.0;
|
||||
this.lastUpdateTime_ = currentTime;
|
||||
|
||||
var secondsSinceLastUpdate = currentTime - this.lastMpdFetchTime_;
|
||||
shaka.log.debug(
|
||||
'Requesting new MPD... last MPD was retrieved',
|
||||
secondsSinceLastUpdate,
|
||||
'seconds ago.');
|
||||
var p;
|
||||
if (this.mpd_.minUpdatePeriod == null) {
|
||||
// Do not fetch an updated MPD.
|
||||
p = Promise.resolve();
|
||||
} else {
|
||||
// Fetch a new MPD.
|
||||
var secondsSinceLastUpdate = currentTime - this.lastUpdateTime_;
|
||||
shaka.log.debug(
|
||||
'Requesting new MPD... last MPD was retrieved',
|
||||
secondsSinceLastUpdate,
|
||||
'seconds ago.');
|
||||
|
||||
this.lastMpdFetchTime_ = currentTime;
|
||||
var mpdRequest = new shaka.dash.MpdRequest(this.mpdUrl_);
|
||||
var mpdRequest = new shaka.dash.MpdRequest(this.mpdUrl_);
|
||||
p = mpdRequest.send().then(shaka.util.TypedBind(this,
|
||||
/** @param {!shaka.dash.mpd.Mpd} mpd */
|
||||
function(mpd) {
|
||||
this.mpd_ = mpd;
|
||||
}));
|
||||
}
|
||||
|
||||
mpdRequest.send().then(shaka.util.TypedBind(this,
|
||||
/** @param {!shaka.dash.mpd.Mpd} mpd */
|
||||
function(mpd) {
|
||||
p.then(shaka.util.TypedBind(this,
|
||||
function() {
|
||||
shaka.asserts.assert(this.mpd_);
|
||||
var mpdProcessor =
|
||||
new shaka.dash.MpdProcessor(this.interpretContentProtection_);
|
||||
mpdProcessor.process(mpd);
|
||||
|
||||
this.timeShiftBufferDepth_ = mpd.timeShiftBufferDepth || 0;
|
||||
mpdProcessor.process(/** @type {!shaka.dash.mpd.Mpd} */(this.mpd_));
|
||||
this.timeShiftBufferDepth_ = this.mpd_.timeShiftBufferDepth || 0;
|
||||
this.updateManifest(mpdProcessor.manifestInfo);
|
||||
this.evictSegmentReferences_();
|
||||
this.sampleMpdLatency_();
|
||||
|
||||
this.setUpdateTimer_(mpd.minUpdatePeriod || 0);
|
||||
this.setUpdateTimer_();
|
||||
this.setTargetUpdateTime_();
|
||||
})
|
||||
).catch(shaka.util.TypedBind(this,
|
||||
@@ -351,7 +367,7 @@ shaka.player.DashVideoSource.prototype.onUpdate_ = function() {
|
||||
this.dispatchEvent(event);
|
||||
|
||||
// In case the application wants to ignore errors, schedule a retry.
|
||||
this.setUpdateTimer_(0);
|
||||
this.setUpdateTimer_();
|
||||
this.setTargetUpdateTime_();
|
||||
})
|
||||
);
|
||||
@@ -382,16 +398,15 @@ shaka.player.DashVideoSource.prototype.evictSegmentReferences_ = function() {
|
||||
|
||||
/**
|
||||
* Sets the update timer.
|
||||
* @param {number} minUpdatePeriod
|
||||
* @private
|
||||
*/
|
||||
shaka.player.DashVideoSource.prototype.setUpdateTimer_ = function(
|
||||
minUpdatePeriod) {
|
||||
shaka.player.DashVideoSource.prototype.setUpdateTimer_ = function() {
|
||||
shaka.asserts.assert(this.manifestInfo && this.manifestInfo.live);
|
||||
shaka.asserts.assert(this.updateTimer_ == null);
|
||||
shaka.asserts.assert(this.mpd_ != null);
|
||||
|
||||
var updateInterval =
|
||||
Math.max(minUpdatePeriod,
|
||||
Math.max(this.mpd_.minUpdatePeriod || 0,
|
||||
shaka.player.DashVideoSource.MIN_UPDATE_INTERVAL_);
|
||||
shaka.log.debug('updateInterval', updateInterval);
|
||||
|
||||
@@ -489,9 +504,9 @@ shaka.player.DashVideoSource.prototype.onSeekRangeUpdate_ = function(
|
||||
// is large, we might run out of indexed segments if we don't do this.
|
||||
if (this.targetUpdateTime_ != null &&
|
||||
(this.seekEndTime_ >= this.targetUpdateTime_)) {
|
||||
var secondsSinceLastUpdate = (Date.now() / 1000.0) - this.lastMpdFetchTime_;
|
||||
var secondsSinceLastUpdate = (Date.now() / 1000.0) - this.lastUpdateTime_;
|
||||
// If we're not waiting for an update but we've hit our target update time,
|
||||
// we must be fetching an MPD. Don't fetch another one.
|
||||
// we must be processing an updating already. Don't start another one.
|
||||
var waitingForUpdate = this.updateTimer_ != null;
|
||||
var DashVideoSource = shaka.player.DashVideoSource;
|
||||
if (waitingForUpdate &&
|
||||
@@ -568,12 +583,12 @@ shaka.player.DashVideoSource.prototype.cancelSeekRangeUpdateTimer_ =
|
||||
/**
|
||||
* Measure the latency introduced by fetching, processing, and loading an MPD.
|
||||
* Should be called after a manifest is loaded or updated. Assumes that
|
||||
* |lastMpdFetchTime_| is always set when an MPD request begins.
|
||||
* |lastUpdateTime_| is always set when an update begins.
|
||||
* @private
|
||||
*/
|
||||
shaka.player.DashVideoSource.prototype.sampleMpdLatency_ = function() {
|
||||
var currentTime = Date.now() / 1000.0;
|
||||
var latencySample = currentTime - this.lastMpdFetchTime_;
|
||||
var latencySample = currentTime - this.lastUpdateTime_;
|
||||
this.latencyEstimator_.sample(1 /* weight */, latencySample);
|
||||
};
|
||||
|
||||
|
||||
@@ -535,7 +535,8 @@ describe('MpdProcessor', function() {
|
||||
m.timeShiftBufferDepth = 0;
|
||||
m.minBufferTime = 0;
|
||||
|
||||
// Set @minUpdatePeriod so that the MPD is treated as dynamic.
|
||||
// Set @minUpdatePeriod and @type so that the MPD is treated as dynamic.
|
||||
m.type = 'dynamic';
|
||||
m.minUpdatePeriod = 30;
|
||||
|
||||
processor.createManifestInfo_(m);
|
||||
|
||||
Reference in New Issue
Block a user