Skip to content

Commit

Permalink
Improve live end-of-stream behavior.
Browse files Browse the repository at this point in the history
* Supports live streams of a fixed length.
* Live streams that stop now correctly end the video.
* Only uses local MPD when @availabilityStartTime is in the future.
* Added configuration option liveStreamEndTimeout.

A live stream is considered "ended" when the play-head moves past
the last segment for liveStreamEndTimeout amount of time.  This allows
the MPD to be updated with new segments.

Closes #139
Closes #140
Closes #145
Issue #172

Change-Id: I0675203931f35ca3e0dee60a87c54ba74cbdbaea
  • Loading branch information
TheModMaker authored and Gerrit Code Review committed Oct 13, 2015
1 parent 9befe62 commit f346ece
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 28 deletions.
1 change: 1 addition & 0 deletions lib/dash/dynamic_live_segment_index.js
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ shaka.dash.DynamicLiveSegmentIndex.prototype.integrate = function(
other.originalLatestAvailableSegmentEndTime_;
this.nextSegmentNumber_ = other.nextSegmentNumber_;
this.manifestCreationTime = other.manifestCreationTime;
this.duration = other.duration;

this.merge(segmentIndex);
this.generateSegmentReferences_(shaka.util.Clock.now() / 1000.0);
Expand Down
66 changes: 64 additions & 2 deletions lib/dash/live_segment_index.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ shaka.dash.LiveSegmentIndex = function(
/** @protected {number} */
this.manifestCreationTime = manifestCreationTime;

/** @protected {?number} */
this.duration = this.mpd.mediaPresentationDuration ||
this.mpd.periods.reduce(function(all, part) {
if (part.duration == null) {
return NaN;
} else {
return all + part.duration;
}
}, 0) || 0;

/**
* Either the current presentation time when the manifest was created, in
* seconds, or null if this SegmentIndex has never contained any
Expand Down Expand Up @@ -143,12 +153,16 @@ shaka.dash.LiveSegmentIndex.prototype.integrate = function(segmentIndex) {
this);
return false;
}
var temp = /** @type {shaka.dash.LiveSegmentIndex} */ (segmentIndex);

this.merge(segmentIndex);
this.duration = Math.max(this.duration, temp.duration);

if (this.originalPresentationTime_ == null) {
var temp = /** @type {shaka.dash.LiveSegmentIndex} */ (segmentIndex);
this.manifestCreationTime = temp.manifestCreationTime;
this.initializeSeekWindow();
} else {
this.evictEnd_();
}
return true;
};
Expand All @@ -165,6 +179,7 @@ shaka.dash.LiveSegmentIndex.prototype.initializeSeekWindow = function() {
shaka.asserts.assert(this.originalLiveEdge_ == null);
shaka.asserts.assert(this.seekStartTime_ == null);

this.evictEnd_();
if (this.length() == 0) {
return;
}
Expand Down Expand Up @@ -286,6 +301,11 @@ shaka.dash.LiveSegmentIndex.prototype.getSeekRangeInternal = function(
return { start: 0, end: 0 };
}

var streamEnd = Number.POSITIVE_INFINITY;
if (this.duration) {
streamEnd = this.duration;
}

var manifestAge = wallTime - this.manifestCreationTime;
var currentPresentationTime = this.originalPresentationTime_ + manifestAge;

Expand All @@ -301,6 +321,7 @@ shaka.dash.LiveSegmentIndex.prototype.getSeekRangeInternal = function(
this.seekStartTime_ += seekWindowSurplus;
}
}
this.seekStartTime_ = Math.min(this.seekStartTime_, streamEnd);

if (!COMPILED) {
if (this.length() > 0) {
Expand All @@ -312,8 +333,14 @@ shaka.dash.LiveSegmentIndex.prototype.getSeekRangeInternal = function(
}
}


var currentLiveEdge = this.originalLiveEdge_ + manifestAge;
shaka.asserts.assert(currentLiveEdge >= this.seekStartTime_);
if (currentLiveEdge < this.seekStartTime_) {
// The seek window has moved past the last segment; the stream may have
// stopped broadcasting or the manifest may be malformed (e.g.
// @availabilityStartTime may be large compared to the segment start/end).
return { start: this.seekStartTime_, end: this.seekStartTime_ };
}

// Compute the seek end time. Allow the seek end time to move into the last
// segment (in the usual case), so we can play-out the last segment.
Expand All @@ -340,6 +367,7 @@ shaka.dash.LiveSegmentIndex.prototype.getSeekRangeInternal = function(
}
}
var seekEndTime = Math.max(targetSeekEndTime, this.seekStartTime_);
seekEndTime = Math.min(seekEndTime, streamEnd);

return { start: this.seekStartTime_, end: seekEndTime };
};
Expand All @@ -352,6 +380,9 @@ shaka.dash.LiveSegmentIndex.prototype.getSeekRangeInternal = function(
* @private
*/
shaka.dash.LiveSegmentIndex.prototype.evict_ = function(wallTime) {
// Always evict segments at the end.
this.evictEnd_();

if (this.mpd.timeShiftBufferDepth == null) {
return;
}
Expand Down Expand Up @@ -407,3 +438,34 @@ shaka.dash.LiveSegmentIndex.prototype.evict_ = function(wallTime) {
}
};


/**
* Evicts segments that are past the end of the stream.
*
* @private
*/
shaka.dash.LiveSegmentIndex.prototype.evictEnd_ = function() {
if (!this.duration) {
return;
}

// Check to remove references past |duration|.
var end = 0;
for (var i = this.references.length - 1; i >= 0; --i) {
var startTime = this.references[i].startTime;
if (startTime > this.duration) {
++end;
} else {
break;
}
}

if (end > 0) {
this.references.splice(-end);
shaka.log.warning(
'Segments found after stream end, evicted', end,
'SegmentReference(s),', this.references.length,
'SegmentReference(s) remain.');
}
};

27 changes: 20 additions & 7 deletions lib/player/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ shaka.player.Player = function(video) {
this.videoSourceConfig_ = {
'enableAdaptation': true,
'streamBufferSize': shaka.player.Defaults.STREAM_BUFFER_SIZE,
'liveStreamEndTimeout': shaka.player.Defaults.STREAM_BUFFER_SIZE,
'licenseRequestTimeout': shaka.player.Defaults.LICENSE_REQUEST_TIMEOUT,
'mpdRequestTimeout': shaka.player.Defaults.MPD_REQUEST_TIMEOUT,
'segmentRequestTimeout': shaka.player.Defaults.SEGMENT_REQUEST_TIMEOUT,
Expand Down Expand Up @@ -786,6 +787,11 @@ shaka.player.Player.prototype.isLive = function() {
* where N equals the 'minBufferTime' attribute from the MPD.
*
* <li>
* <b>liveStreamEndTimeout</b>: number <br>
* Sets the amount of time that the player will wait after the last segment
* to determine if a live stream has ended.
*
* <li>
* <b>licenseRequestTimeout</b>: number <br>
* Sets the license request timeout in seconds. A value of zero indicates
* no timeout.
Expand Down Expand Up @@ -842,6 +848,12 @@ shaka.player.Player.prototype.configure = function(config) {
this.videoSourceConfig_['streamBufferSize'] = streamBufferSize;
}

var liveStreamEndedTimeout =
MapUtils.getNumber(config, 'liveStreamEndTimeout', 0);
if (liveStreamEndedTimeout != null) {
this.videoSourceConfig_['liveStreamEndTimeout'] = liveStreamEndedTimeout;
}

var licenseRequestTimeout =
MapUtils.getNumber(config, 'licenseRequestTimeout', 0);
if (licenseRequestTimeout != null) {
Expand Down Expand Up @@ -1006,21 +1018,22 @@ shaka.player.Player.prototype.onWatchdogTimer_ = function() {
bufferedProp.length ? bufferedProp.end(bufferedProp.length - 1) : 0;
var buffered = Math.max(bufferEnd - this.video_.currentTime, 0);

if (!this.buffering_) {
var fudgeFactor = shaka.player.Player.BUFFERED_FUDGE_FACTOR_;
var threshold = shaka.player.Player.UNDERFLOW_THRESHOLD_;
var fudgeFactor = shaka.player.Player.BUFFERED_FUDGE_FACTOR_;
var threshold = shaka.player.Player.UNDERFLOW_THRESHOLD_;

var durationProp = this.video_.duration;
var d = isNaN(durationProp) ? 0 : Math.max(durationProp - fudgeFactor, 0);
var atEnd = (bufferEnd >= d) || (this.video_.currentTime >= d);

if (!this.buffering_) {
// If there are no buffered ranges but the playhead is at the end of
// the video then we shouldn't enter a buffering state.
var durationProp = this.video_.duration;
var d = isNaN(durationProp) ? 0 : Math.max(durationProp - fudgeFactor, 0);
var atEnd = (bufferEnd >= d) || (this.video_.currentTime >= d);
if (!this.video_.paused && !atEnd && buffered < threshold) {
this.enterBufferingState_();
}
} else {
var bufferingGoal = this.videoSource_.getBufferingGoal();
if (buffered > bufferingGoal) {
if (atEnd || buffered > bufferingGoal) {
this.endBufferingState_();
this.video_.play();
}
Expand Down
Loading

0 comments on commit f346ece

Please sign in to comment.