Skip to content

Commit

Permalink
Simplify Playhead repositioning.
Browse files Browse the repository at this point in the history
Change-Id: Ie171082539e72465155b30d4d6e11eedd1244266
  • Loading branch information
TheModMaker committed Mar 31, 2017
1 parent db909b0 commit 9a05e26
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 58 deletions.
82 changes: 35 additions & 47 deletions lib/media/playhead.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,66 +317,54 @@ shaka.media.Playhead.prototype.onPlaying_ = function() {
* @private
*/
shaka.media.Playhead.prototype.reposition_ = function(currentTime) {
var isBuffered = (function(time) {
return shaka.media.TimeRangesUtils.bufferedAheadOf(
this.video_.buffered, time) > 0;
}.bind(this));

var timeline = this.timeline_;
// TODO(modmaker): |start| uses getEarliestStart to support gaps at the
// beginning of the media. Once gap jumping is added, this should be:
// var start = timeline.getSafeAvailabilityStart(0);
var start = timeline.getEarliestStart();
var end = timeline.getSegmentAvailabilityEnd();

if (!timeline.isLive() ||
timeline.getSegmentAvailabilityDuration() == Infinity) {
// If the presentation is live but has an infinite segment availability
// duration then we can treat it as VOD since the start of the window is
// not moving.
if (currentTime < start) {
shaka.log.v1('Playhead before start.');
return start;
} else if (currentTime > end) {
shaka.log.v1('Playhead past end.');
return end;
}
return currentTime;
}

// TODO: Link to public doc that explains the following code.

// 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(this.rebufferingGoal_);

if (currentTime >= safe && currentTime <= end) {
shaka.log.v1('Playhead in safe region.');
return currentTime;
}
// 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
// will run again. So if we seeked directly to |start|, |start| would move
// on the next call and we would loop forever.
var seekStart = timeline.getSafeAvailabilityStart(1);
var seekSafe = timeline.getSafeAvailabilityStart(this.rebufferingGoal_ + 1);

var bufferedAhead = shaka.media.TimeRangesUtils.bufferedAheadOf(
this.video_.buffered, currentTime);
if ((bufferedAhead != 0) && (currentTime >= start && currentTime <= end)) {
shaka.log.v1('Playhead outside safe region & in buffered region.');
return currentTime;
} else if (currentTime > end) {

if (currentTime > end) {
shaka.log.v1('Playhead past end.');
return end;
} else if ((end < safe) && (currentTime >= start && currentTime <= end)) {
// The segment availability window is so small we cannot reposition the
// playhead normally; however, since |currentTime| is within the window, we
// don't have to do anything.
shaka.log.v1('Playhead outside safe region & in unbuffered region,',
'but cannot reposition the playhead.');
return currentTime;
}

// If we have buffered near |start|, then we can still seek to that even if
// we are not buffered at |currentTime|.
bufferedAhead = shaka.media.TimeRangesUtils.bufferedAheadOf(
this.video_.buffered, start);
if (bufferedAhead != 0) {
shaka.log.v1('Playhead outside safe region & start is buffered');
// Offset by 1 so we don't repeatedly move the playhead because of a slight
// delay.
return timeline.getSafeAvailabilityStart(1);
if (currentTime < start) {
if (isBuffered(seekStart)) {
shaka.log.v1('Playhead before start & start is buffered');
return seekStart;
} else {
shaka.log.v1('Playhead before start & start is unbuffered');
return seekSafe;
}
}

// It's not safe to buffer from |currentTime|, so reposition the playhead.
shaka.log.v1('Playhead outside safe region & in unbuffered region,',
'or playhead before start');
return Math.min(safe + 2, end);
if (currentTime >= safe || isBuffered(currentTime)) {
shaka.log.v1('Playhead in safe region or in buffered region.');
return currentTime;
} else {
shaka.log.v1('Playhead outside safe region & in unbuffered region.');
return seekSafe;
}
};


Expand Down
28 changes: 17 additions & 11 deletions test/media/playhead_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ describe('Playhead', function() {
expect(video.currentTime).toBe(5);
expect(playhead.getTime()).toBe(5);

// left = start + 1 = 5 + 1 = 6
// safe = left + rebufferingGoal = 6 + 10 = 16
// safe = start + rebufferingGoal = 5 + 10 = 15
// safeSeek = safeSeek + 1 = 15 + 1 = 16

// Seek in safe region & in buffered region.
video.currentTime = 26;
Expand All @@ -158,8 +158,8 @@ describe('Playhead', function() {
// region).
video.currentTime = 5.5;
video.on['seeking']();
expect(video.currentTime).toBe(17);
expect(playhead.getTime()).toBe(17);
expect(video.currentTime).toBe(16);
expect(playhead.getTime()).toBe(16);
expect(onSeek).not.toHaveBeenCalled();
video.on['seeking']();
expect(onSeek).toHaveBeenCalled();
Expand All @@ -178,8 +178,8 @@ describe('Playhead', function() {
// Seek outside safe region & in unbuffered region.
video.currentTime = 9;
video.on['seeking']();
expect(video.currentTime).toBe(17);
expect(playhead.getTime()).toBe(17);
expect(video.currentTime).toBe(16);
expect(playhead.getTime()).toBe(16);
expect(onSeek).not.toHaveBeenCalled();
video.on['seeking']();
expect(onSeek).toHaveBeenCalled();
Expand All @@ -200,16 +200,17 @@ describe('Playhead', function() {
// Seek before start.
video.currentTime = 1;
video.on['seeking']();
expect(video.currentTime).toBe(17);
expect(playhead.getTime()).toBe(17);
expect(video.currentTime).toBe(16);
expect(playhead.getTime()).toBe(16);
expect(onSeek).not.toHaveBeenCalled();
video.on['seeking']();
expect(onSeek).toHaveBeenCalled();

onSeek.calls.reset();

// Seek with end < safe (note: safe == 16).
// Seek with safe == end
timeline.getSegmentAvailabilityEnd.and.returnValue(12);
timeline.getSafeAvailabilityStart.and.returnValue(12);

// Seek before start
video.currentTime = 4;
Expand All @@ -225,8 +226,10 @@ describe('Playhead', function() {
// Seek in window.
video.currentTime = 8;
video.on['seeking']();
expect(video.currentTime).toBe(8);
expect(playhead.getTime()).toBe(8);
expect(video.currentTime).toBe(12);
expect(playhead.getTime()).toBe(12);
expect(onSeek).not.toHaveBeenCalled();
video.on['seeking']();
expect(onSeek).toHaveBeenCalled();

onSeek.calls.reset();
Expand All @@ -249,6 +252,7 @@ describe('Playhead', function() {
timeline.isLive.and.returnValue(false);
timeline.getEarliestStart.and.returnValue(5);
timeline.getSegmentAvailabilityStart.and.returnValue(5);
timeline.getSafeAvailabilityStart.and.returnValue(5);
timeline.getSegmentAvailabilityEnd.and.returnValue(60);
timeline.getSegmentAvailabilityDuration.and.returnValue(null);

Expand Down Expand Up @@ -328,6 +332,7 @@ describe('Playhead', function() {
timeline.isLive.and.returnValue(false);
timeline.getEarliestStart.and.returnValue(5);
timeline.getSegmentAvailabilityStart.and.returnValue(5);
timeline.getSafeAvailabilityStart.and.returnValue(5);
timeline.getSegmentAvailabilityEnd.and.returnValue(60);
timeline.getSegmentAvailabilityDuration.and.returnValue(30);

Expand All @@ -345,6 +350,7 @@ describe('Playhead', function() {
// Simulate pausing.
timeline.getEarliestStart.and.returnValue(10);
timeline.getSegmentAvailabilityStart.and.returnValue(10);
timeline.getSafeAvailabilityStart.and.returnValue(10);
timeline.getSegmentAvailabilityEnd.and.returnValue(70);
timeline.getSegmentAvailabilityDuration.and.returnValue(30);

Expand Down

0 comments on commit 9a05e26

Please sign in to comment.