Skip to content

Commit

Permalink
Use seek range in Playhead.
Browse files Browse the repository at this point in the history
Rather than using the availability window, Playhead should use the
seek range to restrict the playhead's position.

Closes #1224

Backported to v2.3.x

Change-Id: I8612bfafb53bbb214e32aae2d71af52d748a3aee
  • Loading branch information
TheModMaker authored and joeyparrish committed Feb 21, 2018
1 parent 532750b commit 6ae115a
Show file tree
Hide file tree
Showing 13 changed files with 170 additions and 95 deletions.
4 changes: 4 additions & 0 deletions lib/dash/dash_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ shaka.dash.DashParser.Context;
* start: number,
* duration: ?number,
* node: !Element,
* index: number,
* isLastPeriod: boolean
* }}
*
Expand All @@ -208,6 +209,8 @@ shaka.dash.DashParser.Context;
* will be non-null for all periods except the last.
* @property {!Element} node
* The XML Node for the Period.
* @property {number} index
* The 0-base index of this Period within the manifest.
* @property {boolean} isLastPeriod
* Whether this Period is the last one in the manifest.
*/
Expand Down Expand Up @@ -623,6 +626,7 @@ shaka.dash.DashParser.prototype.parsePeriods_ = function(
start: start,
duration: periodDuration,
node: elem,
index: i,
isLastPeriod: periodDuration == null || i == periodNodes.length - 1
};
var period = this.parsePeriod_(context, baseUris, info);
Expand Down
10 changes: 5 additions & 5 deletions lib/dash/segment_base.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,10 @@ shaka.dash.SegmentBase.createStream = function(context, requestInitSegment) {
shaka.dash.SegmentBase.createSegmentIndexFromUris = function(
context, requestInitSegment, init, uris,
startByte, endByte, containerType, scaledPresentationTimeOffset) {
var presentationTimeline = context.presentationTimeline;
var fitLast = !context.dynamic || !context.periodInfo.isLastPeriod;
var periodStartTime = context.periodInfo.start;
var periodDuration = context.periodInfo.duration;
let presentationTimeline = context.presentationTimeline;
let fitLast = !context.dynamic || !context.periodInfo.isLastPeriod;
let periodIndex = context.periodInfo.index;
let periodDuration = context.periodInfo.duration;

// Create a local variable to bind to so we can set to null to help the GC.
var localRequest = requestInitSegment;
Expand Down Expand Up @@ -165,7 +165,7 @@ shaka.dash.SegmentBase.createSegmentIndexFromUris = function(
scaledPresentationTimeOffset);
}

presentationTimeline.notifySegments(periodStartTime, references);
presentationTimeline.notifySegments(references, periodIndex == 0);

// Since containers are never updated, we don't need to store the
// segmentIndex in the map.
Expand Down
2 changes: 1 addition & 1 deletion lib/dash/segment_list.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ shaka.dash.SegmentList.createStream = function(context, segmentIndexMap) {
segmentIndex.evict(start - context.periodInfo.start);
} else {
context.presentationTimeline.notifySegments(
context.periodInfo.start, references);
references, context.periodInfo.index == 0);
segmentIndex = new shaka.media.SegmentIndex(references);
if (id && context.dynamic)
segmentIndexMap[id] = segmentIndex;
Expand Down
2 changes: 1 addition & 1 deletion lib/dash/segment_template.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ shaka.dash.SegmentTemplate.createStream = function(
segmentIndex.evict(start - context.periodInfo.start);
} else {
context.presentationTimeline.notifySegments(
context.periodInfo.start, references);
references, context.periodInfo.index == 0);
segmentIndex = new shaka.media.SegmentIndex(references);
if (id && context.dynamic)
segmentIndexMap[id] = segmentIndex;
Expand Down
31 changes: 15 additions & 16 deletions lib/media/playhead.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ shaka.media.Playhead.prototype.getStartTime_ = function(startTime) {
if (timeline.getDuration() < Infinity) {
// If the presentation is VOD, or if the presentation is live but has
// finished broadcasting, then start from the beginning.
startTime = timeline.getSegmentAvailabilityStart();
startTime = timeline.getSeekRangeStart();
} else {
// Otherwise, start near the live-edge.
startTime = timeline.getSeekRangeEnd();
Expand Down Expand Up @@ -219,13 +219,12 @@ shaka.media.Playhead.prototype.onPollWindow_ = function() {

let currentTime = this.video_.currentTime;
let timeline = this.manifest_.presentationTimeline;
let availabilityStart = timeline.getSegmentAvailabilityStart();
if (currentTime < availabilityStart) {
// The availability window has moved past the playhead.
// Move ahead to catch up.
let seekStart = timeline.getSeekRangeStart();
if (currentTime < seekStart) {
// The seek range has moved past the playhead. Move ahead to catch up.
let targetTime = this.reposition_(currentTime);
shaka.log.info('Jumping forward ' + (targetTime - currentTime) +
' seconds to catch up with the availability window.');
' seconds to catch up with the seek range.');
this.video_.currentTime = targetTime;
}
};
Expand Down Expand Up @@ -293,16 +292,16 @@ shaka.media.Playhead.prototype.reposition_ = function(currentTime) {
var rebufferingGoal = shaka.util.StreamUtils.getRebufferingGoal(
this.manifest_, this.config_, 1 /* scaleFactor */);

var timeline = this.manifest_.presentationTimeline;
var start = timeline.getSafeAvailabilityStart(0);
var end = timeline.getSegmentAvailabilityEnd();
var duration = timeline.getDuration();
let timeline = this.manifest_.presentationTimeline;
let start = timeline.getSafeSeekRangeStart(0);
let end = timeline.getSeekRangeEnd();
let duration = timeline.getDuration();

// With live content, the beginning of the availability window is moving
// forward. This means we cannot seek to it since we will "fall" outside the
// window while we buffer. So we define a "safe" region that is far enough
// away. For VOD, |safe == start|.
var safe = timeline.getSafeAvailabilityStart(rebufferingGoal);
let safe = timeline.getSafeSeekRangeStart(rebufferingGoal);

// These are the times to seek to rather than the exact destinations. When
// we seek, we will get another event (after a slight delay) and these steps
Expand All @@ -311,8 +310,8 @@ shaka.media.Playhead.prototype.reposition_ = function(currentTime) {
//
// Offset by 5 seconds since Chromecast takes a few seconds to start playing
// after a seek, even when buffered.
var seekStart = timeline.getSafeAvailabilityStart(5);
var seekSafe = timeline.getSafeAvailabilityStart(rebufferingGoal + 5);
let seekStart = timeline.getSafeSeekRangeStart(5);
let seekSafe = timeline.getSafeSeekRangeStart(rebufferingGoal + 5);


if (currentTime >= duration) {
Expand Down Expand Up @@ -346,17 +345,17 @@ shaka.media.Playhead.prototype.reposition_ = function(currentTime) {


/**
* Clamps the given time to the segment availability window.
* Clamps the given time to the seek range.
*
* @param {number} time The time in seconds.
* @return {number} The clamped time in seconds.
* @private
*/
shaka.media.Playhead.prototype.clampTime_ = function(time) {
var start = this.manifest_.presentationTimeline.getSegmentAvailabilityStart();
let start = this.manifest_.presentationTimeline.getSeekRangeStart();
if (time < start) return start;

var end = this.manifest_.presentationTimeline.getSegmentAvailabilityEnd();
let end = this.manifest_.presentationTimeline.getSeekRangeEnd();
if (time > end) return end;

return time;
Expand Down
65 changes: 46 additions & 19 deletions lib/media/presentation_timeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ shaka.media.PresentationTimeline = function(
/** @private {number} */
this.maxSegmentDuration_ = 1;

/** @private {number} */
this.maxFirstSegmentStartTime_ = 0;

/** @private {number} */
this.clockOffset_ = 0;

Expand Down Expand Up @@ -167,15 +170,20 @@ shaka.media.PresentationTimeline.prototype.setDelay = function(delay) {
* information. This function should be called once for each Stream (no more,
* no less).
*
* @param {number} periodStartTime
* @param {!Array.<!shaka.media.SegmentReference>} references
* @param {boolean} isFirstPeriod
* @export
*/
shaka.media.PresentationTimeline.prototype.notifySegments = function(
periodStartTime, references) {
references, isFirstPeriod) {
if (references.length == 0)
return;

if (isFirstPeriod) {
this.maxFirstSegmentStartTime_ =
Math.max(this.maxFirstSegmentStartTime_, references[0].startTime);
}

this.maxSegmentDuration_ = references.reduce(
function(max, r) { return Math.max(max, r.endTime - r.startTime); },
this.maxSegmentDuration_);
Expand Down Expand Up @@ -237,27 +245,14 @@ shaka.media.PresentationTimeline.prototype.isInProgress = function() {
*/
shaka.media.PresentationTimeline.prototype.getSegmentAvailabilityStart =
function() {
return this.getSafeAvailabilityStart(0 /* delay */);
};
goog.asserts.assert(this.segmentAvailabilityDuration_ >= 0,
'The availability duration should be positive');


/**
* Gets the presentation's current segment availability start time, offset by
* the given amount. This is used to ensure that we don't "fall" back out of
* the availability window while we are buffering.
*
* @param {number} offset The offset to add to the start time.
* @return {number} The current segment availability start time, in seconds,
* relative to the start of the presentation.
* @export
*/
shaka.media.PresentationTimeline.prototype.getSafeAvailabilityStart =
function(offset) {
if (this.segmentAvailabilityDuration_ == Infinity)
return this.segmentAvailabilityStart_;

var end = this.getSegmentAvailabilityEnd();
var start = Math.min(end - this.segmentAvailabilityDuration_ + offset, end);
let end = this.getSegmentAvailabilityEnd();
let start = end - this.segmentAvailabilityDuration_;
return Math.max(this.segmentAvailabilityStart_, start);
};

Expand Down Expand Up @@ -292,6 +287,38 @@ shaka.media.PresentationTimeline.prototype.getSegmentAvailabilityEnd =
};


/**
* Gets the seek range start time, offset by the given amount. This is used to
* ensure that we don't "fall" back out of the seek window while we are
* buffering.
*
* @param {number} offset The offset to add to the start time.
* @return {number} The current seek start time, in seconds, relative to the
* start of the presentation.
* @export
*/
shaka.media.PresentationTimeline.prototype.getSafeSeekRangeStart = function(
offset) {
if (this.segmentAvailabilityDuration_ == Infinity)
offset = 0;

let end = this.getSegmentAvailabilityEnd();
let start = Math.min(this.maxFirstSegmentStartTime_ + offset, end);
return Math.max(this.getSegmentAvailabilityStart(), start);
};


/**
* Gets the seek range start time.
*
* @return {number}
* @export
*/
shaka.media.PresentationTimeline.prototype.getSeekRangeStart = function() {
return this.getSafeSeekRangeStart(/* offset */ 0);
};


/**
* Gets the seek range end.
*
Expand Down
4 changes: 2 additions & 2 deletions lib/offline/offline_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,13 @@ shaka.offline.OfflineUtils.reconstructPeriod = function(
.filter(OfflineUtils.isText_)
.map(OfflineUtils.createStream_);

period.streams.forEach(function(stream) {
period.streams.forEach(function(stream, i) {
/** @type {!Array.<shaka.media.SegmentReference>} */
var refs = stream.segments.map(function(segment, index) {
return OfflineUtils.segmentDBToSegmentReference_(index, segment);
});

timeline.notifySegments(period.startTime, refs);
timeline.notifySegments(refs, i == 0);
});

return {
Expand Down
4 changes: 2 additions & 2 deletions lib/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -1079,8 +1079,8 @@ shaka.Player.prototype.seekRange = function() {
var start = 0;
var end = 0;
if (this.manifest_) {
var timeline = this.manifest_.presentationTimeline;
start = timeline.getSegmentAvailabilityStart();
let timeline = this.manifest_.presentationTimeline;
start = timeline.getSeekRangeStart();
end = timeline.getSeekRangeEnd();
}
return {'start': start, 'end': end};
Expand Down
Loading

0 comments on commit 6ae115a

Please sign in to comment.