mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-24 17:35:10 +03:00
1f88c9dae1
* Split player_integration into several files. * Moved some helper methods from player_integration to integration_util. Closes #188 Change-Id: I14cb2eda8375fc24ef0419e7993027b90e881d5b
304 lines
12 KiB
JavaScript
304 lines
12 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2015 Google Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
goog.require('shaka.player.StreamVideoSource');
|
|
goog.require('shaka.util.EventManager');
|
|
|
|
describe('Seek', function() {
|
|
var video;
|
|
var player;
|
|
var eventManager;
|
|
var estimator;
|
|
|
|
beforeAll(integration.setUp);
|
|
afterAll(integration.tearDown);
|
|
|
|
beforeEach(function() {
|
|
// Create a video tag. This will be visible so that long tests do not
|
|
// create the illusion of the test-runner being hung.
|
|
video = createVideo();
|
|
document.body.appendChild(video);
|
|
|
|
player = createPlayer(video);
|
|
eventManager = new shaka.util.EventManager();
|
|
});
|
|
|
|
afterEach(function(done) {
|
|
eventManager.destroy();
|
|
eventManager = null;
|
|
|
|
player.destroy().then(function() {
|
|
player = null;
|
|
done();
|
|
});
|
|
|
|
// Remove the video tag from the DOM.
|
|
document.body.removeChild(video);
|
|
});
|
|
|
|
// This covers bug #18597152. Completely clearing the buffers after a seek
|
|
// can cause the media pipeline in Chrome to get stuck. This seemed to
|
|
// happen when certain seek intervals were used.
|
|
it('does not lock up on segment boundaries', function(done) {
|
|
player.load(newSource(manifests.plainManifest)).then(function() {
|
|
video.play();
|
|
// gets the player out of INIT state
|
|
return waitForMovement(video, eventManager);
|
|
}).then(function() {
|
|
video.currentTime = 40.0; // <0.1s before end of segment N (5).
|
|
return delay(2.0);
|
|
}).then(function() {
|
|
video.currentTime = 30.0; // <0.1s before end of segment N-2 (3).
|
|
return delay(8.0);
|
|
}).then(function() {
|
|
// Typically this bug manifests with seeking == true.
|
|
expect(video.seeking).toBe(false);
|
|
// Typically this bug manifests with readyState == HAVE_METADATA.
|
|
expect(video.readyState).not.toBe(HTMLVideoElement.HAVE_METADATA);
|
|
expect(video.readyState).not.toBe(HTMLVideoElement.HAVE_NOTHING);
|
|
// We can't expect to get all the way to 38.0 unless the seek is
|
|
// instantaneous. We use 32.0 because it leaves plenty of wiggle room
|
|
// for various delays (including network delay), and because in this
|
|
// particular bug, the video gets stuck at exactly the seek time (30).
|
|
expect(video.currentTime).toBeGreaterThan(32.0);
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
// This covers bug #18597156. Seeking around without removing any data
|
|
// from the buffers can cause the media pipeline in Chrome to manifest gaps
|
|
// in the buffered data ranges. Such a gap will move forward as data is
|
|
// replaced in buffer, but the gap will never close until the entire range
|
|
// has been replaced. It is therefore SourceBufferManager's job to work
|
|
// around this peculiar behavior from Chrome's SourceBuffer. If this is
|
|
// not done, playback gets "stuck" when the playhead enters such a gap.
|
|
it('does not create unclosable gaps in the buffer', function(done) {
|
|
player.load(newSource(manifests.plainManifest)).then(function() {
|
|
video.play();
|
|
return waitForMovement(video, eventManager);
|
|
}).then(function() {
|
|
video.currentTime = 33.0;
|
|
return waitForMovement(video, eventManager);
|
|
}).then(function() {
|
|
return delay(1.0);
|
|
}).then(function() {
|
|
video.currentTime = 28.0;
|
|
// We don't expect 38.0 because of the uncertainty of network and other
|
|
// delays. This is a safe number which will not cause false failures.
|
|
// When this bug manifests, the playhead typically gets stuck around
|
|
// 32.9, so we expect that 35.0 is a safe indication that the bug is
|
|
// not manifesting.
|
|
return waitForTargetTime(video, eventManager, 35.0, 12.0);
|
|
}).then(function() {
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
// This covers github issue #15, in which seeking to evicted data hangs
|
|
// playback.
|
|
it('does not hang when seeking to evicted data', function(done) {
|
|
var source = newSource(manifests.highBitrateManifest);
|
|
|
|
// This should force Chrome to evict data quickly after it is played.
|
|
// At this asset's bitrate, Chrome should only have enough buffer for
|
|
// 310 seconds of data. Tweak the buffer time for audio, since this
|
|
// will take much less time and bandwidth to buffer.
|
|
player.configure({'streamBufferSize': 300});
|
|
|
|
// Create a temporary shim to intercept and modify manifest info.
|
|
var originalLoad = shaka.player.StreamVideoSource.prototype.load;
|
|
shaka.player.StreamVideoSource.prototype.load = function() {
|
|
var sets = this.manifestInfo.periodInfos[0].streamSetInfos;
|
|
var audioSet = sets[0].contentType == 'audio' ? sets[0] : sets[1];
|
|
expect(audioSet.contentType).toBe('audio');
|
|
// Remove the video set to speed things up.
|
|
this.manifestInfo.periodInfos[0].streamSetInfos = [audioSet];
|
|
return originalLoad.call(this);
|
|
};
|
|
|
|
var audioStreamBuffer;
|
|
player.load(source).then(function() {
|
|
// Replace the StreamVideoSource shim.
|
|
shaka.player.StreamVideoSource.prototype.load = originalLoad;
|
|
// Locate the audio stream buffer.
|
|
var audioStream = source.streamsByType_['audio'];
|
|
audioStreamBuffer = audioStream.sbm_.sourceBuffer_;
|
|
// Nothing has buffered yet.
|
|
expect(audioStreamBuffer.buffered.length).toBe(0);
|
|
// Give the audio time to buffer.
|
|
return waitUntilBuffered(audioStreamBuffer, 290, 50);
|
|
}).then(function() {
|
|
// The content is now buffered, and none has been evicted yet.
|
|
expect(audioStreamBuffer.buffered.length).toBe(1);
|
|
expect(audioStreamBuffer.buffered.start(0)).toBe(0);
|
|
video.play();
|
|
return waitForMovement(video, eventManager);
|
|
}).then(function() {
|
|
// Power through and consume the audio data quickly.
|
|
player.setPlaybackRate(4);
|
|
return waitForTargetTime(video, eventManager, 30, 30);
|
|
}).then(function() {
|
|
// Ensure that the browser has evicted the beginning of the stream.
|
|
// Otherwise, this test hasn't reproduced the circumstances correctly.
|
|
expect(audioStreamBuffer.buffered.start(0)).toBeGreaterThan(0);
|
|
expect(audioStreamBuffer.buffered.end(0)).toBeGreaterThan(310);
|
|
expect(video.currentTime).toBeGreaterThan(0);
|
|
// Seek to the beginning, which is data we will have to re-download.
|
|
player.configure({'streamBufferSize': 10});
|
|
player.setPlaybackRate(1.0);
|
|
video.currentTime = 0;
|
|
// Expect to play some.
|
|
return waitForTargetTime(video, eventManager, 0.5, 2.0);
|
|
}).then(function() {
|
|
done();
|
|
}).catch(function(error) {
|
|
// Replace the StreamVideoSource shim.
|
|
shaka.player.StreamVideoSource.prototype.load = originalLoad;
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
// This covers github issue #26.
|
|
it('does not hang when seeking to pre-adaptation data', function(done) {
|
|
var source = newSource(manifests.plainManifest);
|
|
|
|
player.load(source).then(function() {
|
|
video.play();
|
|
return waitForMovement(video, eventManager);
|
|
}).then(function() {
|
|
// Move quickly past the first two segments.
|
|
player.setPlaybackRate(3.0);
|
|
return waitForTargetTime(video, eventManager, 11.0, 6.0);
|
|
}).then(function() {
|
|
var track = getVideoTrackByHeight(player, 480);
|
|
expect(track.active).toBe(false);
|
|
var ok = player.selectVideoTrack(track.id, false);
|
|
expect(ok).toBe(true);
|
|
return waitForMovement(video, eventManager);
|
|
}).then(function() {
|
|
// This bug manifests within two segments of the adaptation point. To
|
|
// prove that we are not hung, we need to get to a point two segments
|
|
// later than where we adapted.
|
|
return waitForTargetTime(video, eventManager, 22.0, 6.0);
|
|
}).then(function() {
|
|
video.currentTime = 0;
|
|
return waitForMovement(video, eventManager);
|
|
}).then(function() {
|
|
return waitForTargetTime(video, eventManager, 21.0, 12.0);
|
|
}).then(function() {
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('can be used during stream switching', function(done) {
|
|
var source = newSource(manifests.plainManifest);
|
|
|
|
player.load(source).then(function() {
|
|
video.play();
|
|
return waitForMovement(video, eventManager);
|
|
}).then(function() {
|
|
var videoStream = source.streamsByType_['video'];
|
|
|
|
var track = getVideoTrackByHeight(player, 480);
|
|
var ok = player.selectVideoTrack(track.id);
|
|
expect(ok).toBe(true);
|
|
|
|
video.currentTime = 30.0;
|
|
return waitForTargetTime(video, eventManager, 33.0, 8.0);
|
|
}).then(function() {
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
// Starts the video 25 seconds in and then seeks back near the beginning
|
|
// before stream startup (initial buffering) has completed. Playback should
|
|
// begin from the seeked-to location and not hang.
|
|
it('can be used during stream startup w/ large < 0 seek', function(done) {
|
|
streamStartupTest(25, 3, done);
|
|
});
|
|
|
|
// The same as the above test, but tests on the boundary.
|
|
it('can be used during stream startup w/ small < 0 seek)', function(done) {
|
|
var tolerance = shaka.player.StreamVideoSource.SEEK_TOLERANCE_;
|
|
streamStartupTest(25, 25 - (tolerance / 2), done);
|
|
});
|
|
|
|
it('can be used during stream startup w/ large > 0 seek', function(done) {
|
|
streamStartupTest(25, 35, done);
|
|
});
|
|
|
|
function streamStartupTest(playbackStartTime, seekTarget, done) {
|
|
var source = newSource(manifests.plainManifest);
|
|
video.autoplay = true;
|
|
|
|
player.setPlaybackStartTime(playbackStartTime);
|
|
|
|
// Force @minBufferTime to a large value so we have enough time to seek
|
|
// during startup.
|
|
var originalLoad = shaka.player.StreamVideoSource.prototype.load;
|
|
shaka.player.StreamVideoSource.prototype.load = function() {
|
|
expect(this.manifestInfo).not.toBe(null);
|
|
this.manifestInfo.minBufferTime = 80;
|
|
return originalLoad.call(this);
|
|
};
|
|
|
|
player.load(source).then(function() {
|
|
// Continue once we've buffered at least one segment.
|
|
return waitFor(30, function() { return video.buffered.length == 1; });
|
|
}).then(function() {
|
|
// Ensure we have buffered at least one segment but have not yet
|
|
// started playback.
|
|
expect(video.buffered.length).toBe(1);
|
|
expect(video.playbackRate).toBe(0);
|
|
// Now seek back near the beginning.
|
|
video.currentTime = seekTarget;
|
|
|
|
// Once it seeks to the beginning, it will buffer the video and then
|
|
// start playing once it reaches its buffer goal.
|
|
return waitFor(30, function() { return video.playbackRate != 0; });
|
|
}).then(function() {
|
|
// Ensure that the video is actually moving.
|
|
return delay(1);
|
|
}).then(function() {
|
|
expect(video.buffered.length).toBe(1);
|
|
expect(video.playbackRate).toBe(1);
|
|
expect(video.currentTime).toBeGreaterThan(seekTarget);
|
|
shaka.player.StreamVideoSource.prototype.load = originalLoad;
|
|
done();
|
|
}).catch(function(error) {
|
|
shaka.player.StreamVideoSource.prototype.load = originalLoad;
|
|
|
|
fail(error);
|
|
done();
|
|
});
|
|
}
|
|
});
|
|
|