Skip to content

Commit

Permalink
Merge pull request #136 from videojs/trytoresume
Browse files Browse the repository at this point in the history
Race canplay event with a setTimeout.
  • Loading branch information
dmlap committed Dec 11, 2015
2 parents 37fc412 + a0175da commit 9c3f83d
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 0 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ node_js:
- 'iojs-v1'
- '0.12'
- '0.10'
sudo: false
notifications:
hipchat:
rooms:
Expand Down
16 changes: 16 additions & 0 deletions src/videojs.ads.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,22 @@ var
// determine if the video element has loaded enough of the snapshot source
// to be ready to apply the rest of the state
tryToResume = function() {

// tryToResume can either have been called through the `contentcanplay`
// event or fired through setTimeout.
// When tryToResume is called, we should make sure to clear out the other
// way it could've been called by removing the listener and clearing out
// the timeout.
player.off('contentcanplay', tryToResume);
if (player.ads.tryToResumeTimeout_) {
player.clearTimeout(player.ads.tryToResumeTimeout_);
player.ads.tryToResumeTimeout_ = null;
}

// Tech may have changed depending on the differences in sources of the
// original video and that of the ad
tech = player.el().querySelector('.vjs-tech');

if (tech.readyState > 1) {
// some browsers and media aren't "seekable".
// readyState greater than 1 allows for seeking without exceptions
Expand Down Expand Up @@ -211,7 +224,10 @@ var
// safari requires a call to `load` to pick up a changed source
player.load();
// and then resume from the snapshots time once the original src has loaded
// in some browsers (firefox) `canplay` may not fire correctly.
// Reace the `canplay` event with a timeout.
player.one('contentcanplay', tryToResume);
player.ads.tryToResumeTimeout_ = player.setTimeout(tryToResume, 2000);
} else if (!player.ended() || !snapshot.ended) {
// if we didn't change the src, just restore the tracks
restoreTracks();
Expand Down
74 changes: 74 additions & 0 deletions test/videojs.ads.snapshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ QUnit.test('waits for the video to become seekable before restoring the time', f
// the ad resets the current time
this.video.currentTime = 0;
this.player.ads.endLinearAdMode();
setTimeoutSpy.reset(); // we call setTimeout an extra time restorePlayerSnapshot
this.player.trigger('canplay');
assert.strictEqual(setTimeoutSpy.callCount, 1, 'restoring the time should be delayed');
assert.strictEqual(this.video.currentTime, 0, 'currentTime is not modified');
Expand All @@ -72,6 +73,7 @@ QUnit.test('tries to restore the play state up to 20 times', function(assert) {
// the ad resets the current time
this.video.currentTime = 0;
this.player.ads.endLinearAdMode();
setTimeoutSpy.reset(); // we call setTimeout an extra time restorePlayerSnapshot
this.player.trigger('canplay');

// We expect 20 timeouts at 50ms each.
Expand Down Expand Up @@ -461,3 +463,75 @@ QUnit.test('player events during snapshot restoration are prefixed', function(as
this.player.trigger('loadedmetadata');
assert.strictEqual(spy.callCount, 2, 'fired "content" prefixed events');
});

QUnit.test('tryToResume is called through canplay, removes handler and timeout', function(assert) {
var setTimeoutSpy;
var offSpy;
var clearTimeoutSpy;

assert.expect(4);

this.video.seekable = [];
this.player.trigger('adsready');
this.player.trigger('play');

setTimeoutSpy = sinon.spy(window, 'setTimeout');
offSpy = sinon.spy(this.player, 'off');
clearTimeoutSpy = sinon.spy(this.player, 'clearTimeout');

// the video plays to time 100
this.video.currentTime = 100;
this.player.ads.startLinearAdMode();
this.player.src('//example.com/ad.mp4');

// the ad resets the current time
this.video.currentTime = 0;
this.player.ads.endLinearAdMode();
assert.strictEqual(setTimeoutSpy.callCount, 1, 'setTimeout is called to race against canplay');
setTimeoutSpy.reset(); // we call setTimeout an extra time restorePlayerSnapshot
this.player.trigger('canplay');

assert.strictEqual(setTimeoutSpy.callCount, 1, 'tryToResume is called');
assert.ok(offSpy.calledWith('contentcanplay'), 'we remove the contentcanplay handler');
assert.ok(clearTimeoutSpy.called, 'clearTimeout was called');

window.setTimeout.restore();
this.player.off.restore();
this.player.clearTimeout.restore();
});

QUnit.test('tryToResume is called through timeout, removes handler and timeout', function(assert) {
var setTimeoutSpy;
var offSpy;
var clearTimeoutSpy;

assert.expect(4);

this.video.seekable = [];
this.player.trigger('adsready');
this.player.trigger('play');

setTimeoutSpy = sinon.spy(window, 'setTimeout');
offSpy = sinon.spy(this.player, 'off');
clearTimeoutSpy = sinon.spy(this.player, 'clearTimeout');

// the video plays to time 100
this.video.currentTime = 100;
this.player.ads.startLinearAdMode();
this.player.src('//example.com/ad.mp4');

// the ad resets the current time
this.video.currentTime = 0;
this.player.ads.endLinearAdMode();
assert.strictEqual(setTimeoutSpy.callCount, 1, 'setTimeout is called to race against canplay');
setTimeoutSpy.reset(); // we call setTimeout an extra time restorePlayerSnapshot
this.clock.tick(2001);

assert.strictEqual(setTimeoutSpy.callCount, 1, 'tryToResume is called');
assert.ok(offSpy.calledWith('contentcanplay'), 'we remove the contentcanplay handler');
assert.ok(clearTimeoutSpy.called, 'clearTimeout was called');

window.setTimeout.restore();
this.player.off.restore();
this.player.clearTimeout.restore();
});

0 comments on commit 9c3f83d

Please sign in to comment.