mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-25 17:45:03 +03:00
9c0d2db7f6
Also allows application developers to inject new bandwidth estimators instead of using ours. Change-Id: Ieef49df05f45dc3ec0f840f9ab12b63824e08884
797 lines
26 KiB
JavaScript
797 lines
26 KiB
JavaScript
/**
|
|
* Copyright 2014 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.
|
|
*
|
|
* @fileoverview Player integration tests.
|
|
*/
|
|
|
|
goog.require('shaka.player.DashVideoSource');
|
|
goog.require('shaka.player.Player');
|
|
goog.require('shaka.polyfill.MediaKeys');
|
|
goog.require('shaka.polyfill.VideoPlaybackQuality');
|
|
goog.require('shaka.util.EWMABandwidthEstimator');
|
|
|
|
describe('Player', function() {
|
|
var originalAsserts;
|
|
var originalTimeout;
|
|
var video;
|
|
var player;
|
|
var eventManager;
|
|
var estimator;
|
|
|
|
const plainManifest = 'assets/car-20120827-manifest.mpd';
|
|
const encryptedManifest = 'assets/car_cenc-20120827-manifest.mpd';
|
|
const languagesManifest = 'assets/angel_one.mpd';
|
|
const webmManifest = 'assets/feelings_vp9-20130806-manifest.mpd';
|
|
const bogusManifest = 'assets/does_not_exist';
|
|
const highBitrateManifest =
|
|
'//storage.googleapis.com/widevine-demo-media/sintel-1080p/dash.mpd';
|
|
const FUDGE_FACTOR = 0.3;
|
|
|
|
beforeAll(function() {
|
|
// Hijack assertions and convert failed assertions into failed tests.
|
|
assertsToFailures.install();
|
|
|
|
// Change the timeout.
|
|
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
|
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000; // ms
|
|
|
|
// Install polyfills.
|
|
shaka.polyfill.MediaKeys.install();
|
|
shaka.polyfill.VideoPlaybackQuality.install();
|
|
|
|
// Create a video tag. This will be visible so that long tests do not
|
|
// create the illusion of the test-runner being hung.
|
|
video = document.createElement('video');
|
|
video.crossOrigin = 'anonymous';
|
|
video.width = 600;
|
|
video.height = 400;
|
|
// Add it to the DOM.
|
|
document.body.appendChild(video);
|
|
});
|
|
|
|
beforeEach(function() {
|
|
// Create a new player.
|
|
player = new shaka.player.Player(video);
|
|
player.addEventListener('error', convertErrorToTestFailure, false);
|
|
|
|
// Disable automatic adaptation unless it is needed for a test.
|
|
// This makes test results more reproducible.
|
|
player.enableAdaptation(false);
|
|
|
|
eventManager = new shaka.util.EventManager();
|
|
});
|
|
|
|
afterEach(function() {
|
|
eventManager.destroy();
|
|
eventManager = null;
|
|
|
|
player.destroy();
|
|
player = null;
|
|
});
|
|
|
|
afterAll(function() {
|
|
// Remove the video tag from the DOM.
|
|
document.body.removeChild(video);
|
|
|
|
// Restore the timeout.
|
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
|
|
|
// Restore normal assertion behavior.
|
|
assertsToFailures.uninstall();
|
|
});
|
|
|
|
describe('load', function() {
|
|
// This covers basic player re-use.
|
|
it('can be used multiple times without EME', function(done) {
|
|
player.load(newSource(plainManifest)).then(function() {
|
|
player.play();
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
return player.load(newSource(plainManifest));
|
|
}).then(function() {
|
|
player.play();
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
expect(video.currentTime).toBeGreaterThan(0.0);
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
// This covers bug #18614098. A presumed bug in Chrome can cause mediaKeys
|
|
// to be unset on the second use of a video tag.
|
|
it('can be used multiple times with EME', function(done) {
|
|
player.load(newSource(encryptedManifest)).then(function() {
|
|
player.play();
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
return player.load(newSource(encryptedManifest));
|
|
}).then(function() {
|
|
player.play();
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
expect(video.currentTime).toBeGreaterThan(0.0);
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('selectVideoTrack', function() {
|
|
it('can set resolution before beginning playback', function(done) {
|
|
player.load(newSource(plainManifest)).then(function() {
|
|
var track = getVideoTrackByHeight(720);
|
|
player.selectVideoTrack(track.id);
|
|
player.play();
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
return delay(5.5); // adapts when it crosses a segment boundary.
|
|
}).then(function() {
|
|
expect(video.videoHeight).toEqual(720);
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('can be called multiple times', function(done) {
|
|
player.load(newSource(plainManifest)).then(function() {
|
|
var track = getVideoTrackByHeight(720);
|
|
player.selectVideoTrack(track.id);
|
|
player.play();
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
return delay(5.5); // adapts when it crosses a segment boundary.
|
|
}).then(function() {
|
|
expect(video.videoHeight).toEqual(720);
|
|
|
|
var track = getVideoTrackByHeight(360);
|
|
player.selectVideoTrack(track.id);
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
expect(video.videoHeight).toEqual(360);
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('pause', function() {
|
|
it('pauses playback', function(done) {
|
|
var timestamp;
|
|
player.load(newSource(plainManifest)).then(function() {
|
|
player.play();
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
expect(video.paused).toBe(false);
|
|
player.pause();
|
|
timestamp = video.currentTime;
|
|
return delay(1.0);
|
|
}).then(function() {
|
|
expect(video.paused).toBe(true);
|
|
expect(video.currentTime).toEqual(timestamp);
|
|
player.play();
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
expect(video.paused).toBe(false);
|
|
expect(video.currentTime).toBeGreaterThan(timestamp);
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('seek', function() {
|
|
// 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(plainManifest)).then(function() {
|
|
player.play();
|
|
return waitForMovement(); // gets the player out of INIT state
|
|
}).then(function() {
|
|
player.seek(40.0); // <0.1s before end of segment N (5).
|
|
return delay(2.0);
|
|
}).then(function() {
|
|
player.seek(30.0); // <0.1s before end of segment N-2 (3).
|
|
return delay(5.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 35.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(plainManifest)).then(function() {
|
|
player.play();
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
player.seek(33.0);
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
return delay(1.0);
|
|
}).then(function() {
|
|
player.seek(28.0);
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
return delay(7.5);
|
|
}).then(function() {
|
|
// 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.
|
|
expect(video.currentTime).toBeGreaterThan(35.0);
|
|
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(highBitrateManifest);
|
|
|
|
// Create a temporary shim to intercept and modify manifest info.
|
|
var originalLoad = shaka.player.StreamVideoSource.prototype.load;
|
|
shaka.player.StreamVideoSource.prototype.load = function(
|
|
preferredLanguage) {
|
|
// 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.
|
|
const minBufferTime = 300;
|
|
var sets = this.manifestInfo.periodInfos[0].streamSetInfos;
|
|
var audioSet = sets[0].contentType == 'audio' ? sets[0] : sets[1];
|
|
expect(audioSet.contentType).toBe('audio');
|
|
audioSet.streamInfos[0].minBufferTime = minBufferTime;
|
|
// Remove the video set to speed things up.
|
|
this.manifestInfo.periodInfos[0].streamSetInfos = [audioSet];
|
|
return originalLoad.call(this, preferredLanguage);
|
|
};
|
|
|
|
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 delay(8.0);
|
|
}).then(function() {
|
|
// The content is now buffered.
|
|
expect(audioStreamBuffer.buffered.length).toBe(1);
|
|
// Power through and consume the audio data quickly.
|
|
player.play();
|
|
player.setPlaybackRate(8);
|
|
return delay(4.0);
|
|
}).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);
|
|
// Seek to the beginning, which is data we will have to re-download.
|
|
player.seek(0);
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
return delay(1.0);
|
|
}).then(function() {
|
|
// Expect that we've been able to play some.
|
|
expect(video.currentTime).toBeGreaterThan(0.5);
|
|
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 targetTime;
|
|
var source = newSource(plainManifest);
|
|
|
|
player.load(source).then(function() {
|
|
player.play();
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
// Move quickly past the first two segments.
|
|
player.setPlaybackRate(5.0);
|
|
return delay(2.5);
|
|
}).then(function() {
|
|
var track = getVideoTrackByHeight(480);
|
|
expect(track.active).toBe(false);
|
|
var ok = player.selectVideoTrack(track.id, false);
|
|
expect(ok).toBe(true);
|
|
|
|
// 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.
|
|
targetTime = video.currentTime + 10.0;
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
return delay(2.5);
|
|
}).then(function() {
|
|
expect(video.currentTime).toBeGreaterThan(targetTime);
|
|
player.seek(0);
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
return delay(0.5 + (targetTime / 5.0));
|
|
}).then(function() {
|
|
// Expect that we've been able to play past our target point.
|
|
expect(video.currentTime).toBeGreaterThan(targetTime);
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('can be used during stream switching', function(done) {
|
|
var source = newSource(plainManifest);
|
|
|
|
player.load(source).then(function() {
|
|
player.play();
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
var Stream = shaka.media.Stream;
|
|
var videoStream = source.streamsByType_['video'];
|
|
expect(videoStream.state_).toBe(Stream.State_.UPDATING);
|
|
|
|
var track = getVideoTrackByHeight(480);
|
|
var ok = player.selectVideoTrack(track.id);
|
|
expect(ok).toBe(true);
|
|
expect(videoStream.state_).toBe(Stream.State_.SWITCHING);
|
|
|
|
player.seek(30.0);
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
return delay(4.0);
|
|
}).then(function() {
|
|
expect(video.currentTime).toBeGreaterThan(33.0);
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('enableTextTrack', function() {
|
|
it('enables the active track', function(done) {
|
|
player.load(newSource(languagesManifest)).then(function() {
|
|
var activeTrack = getActiveTextTrack();
|
|
expect(activeTrack.enabled).toBe(false);
|
|
|
|
player.enableTextTrack(true);
|
|
|
|
activeTrack = getActiveTextTrack();
|
|
expect(activeTrack.enabled).toBe(true);
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('selectTextTrack', function() {
|
|
it('activates the correct track', function(done) {
|
|
player.load(newSource(languagesManifest)).then(function() {
|
|
var tracks = player.getTextTracks();
|
|
var activeTrack = getActiveTextTrack();
|
|
// Ensure that it is the first track, so that we know our selection
|
|
// of the second track is affecting a real change.
|
|
expect(activeTrack.id).toBe(tracks[0].id);
|
|
|
|
player.selectTextTrack(tracks[1].id);
|
|
activeTrack = getActiveTextTrack();
|
|
expect(activeTrack.id).toBe(tracks[1].id);
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('does not disable subtitles', function(done) {
|
|
player.load(newSource(languagesManifest)).then(function() {
|
|
var tracks = player.getTextTracks();
|
|
player.selectTextTrack(tracks[0].id);
|
|
player.enableTextTrack(true);
|
|
|
|
var activeTrack = getActiveTextTrack();
|
|
expect(activeTrack.enabled).toBe(true);
|
|
expect(activeTrack.id).toBe(tracks[0].id);
|
|
|
|
player.selectTextTrack(tracks[1].id);
|
|
|
|
activeTrack = getActiveTextTrack();
|
|
expect(activeTrack.enabled).toBe(true);
|
|
expect(activeTrack.id).toBe(tracks[1].id);
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('does not re-enable subtitles', function(done) {
|
|
player.load(newSource(languagesManifest)).then(function() {
|
|
var tracks = player.getTextTracks();
|
|
player.selectTextTrack(tracks[0].id);
|
|
player.enableTextTrack(false);
|
|
|
|
var activeTrack = getActiveTextTrack();
|
|
expect(activeTrack.enabled).toBe(false);
|
|
expect(activeTrack.id).toBe(tracks[0].id);
|
|
|
|
player.selectTextTrack(tracks[1].id);
|
|
|
|
activeTrack = getActiveTextTrack();
|
|
expect(activeTrack.enabled).toBe(false);
|
|
expect(activeTrack.id).toBe(tracks[1].id);
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('setPlaybackRate', function() {
|
|
it('plays faster for rates above 1', function(done) {
|
|
var timestamp;
|
|
player.load(newSource(plainManifest)).then(function() {
|
|
player.play();
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
expect(video.currentTime).toBeGreaterThan(0.0);
|
|
timestamp = video.currentTime;
|
|
player.setPlaybackRate(2.0);
|
|
return delay(3.0);
|
|
}).then(function() {
|
|
expect(video.currentTime).toBeGreaterThan(
|
|
timestamp + 6.0 - FUDGE_FACTOR);
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('plays in reverse for negative rates', function(done) {
|
|
var timestamp;
|
|
player.load(newSource(plainManifest)).then(function() {
|
|
player.play();
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
return delay(3.5);
|
|
}).then(function() {
|
|
expect(video.currentTime).toBeGreaterThan(3.0);
|
|
timestamp = video.currentTime;
|
|
player.setPlaybackRate(-1.0);
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
return delay(2.0);
|
|
}).then(function() {
|
|
expect(video.currentTime).toBeLessThan(timestamp - 2.0 + FUDGE_FACTOR);
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('getStats', function() {
|
|
it('updates playTime', function(done) {
|
|
var oldPlayTime;
|
|
player.load(newSource(plainManifest)).then(function() {
|
|
player.play();
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
oldPlayTime = player.getStats().playTime;
|
|
return delay(1.0);
|
|
}).then(function() {
|
|
expect(player.getStats().playTime).toBeGreaterThan(
|
|
oldPlayTime + 1.0 - FUDGE_FACTOR);
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('setPreferredLanguage', function() {
|
|
it('changes the default tracks', function(done) {
|
|
var originalAudioId;
|
|
var originalTextId;
|
|
|
|
player.load(newSource(languagesManifest)).then(function() {
|
|
var activeAudioTrack = getActiveAudioTrack();
|
|
expect(activeAudioTrack.lang).toBe('en');
|
|
originalAudioId = activeAudioTrack.id;
|
|
|
|
var activeTextTrack = getActiveTextTrack();
|
|
expect(activeTextTrack.lang).toBe('en');
|
|
originalTextId = activeTextTrack.id;
|
|
|
|
player.setPreferredLanguage('fr');
|
|
return player.load(newSource(languagesManifest));
|
|
}).then(function() {
|
|
var activeAudioTrack = getActiveAudioTrack();
|
|
expect(activeAudioTrack.lang).toBe('fr');
|
|
expect(activeAudioTrack.id).not.toBe(originalAudioId);
|
|
|
|
var activeTextTrack = getActiveTextTrack();
|
|
expect(activeTextTrack.lang).toBe('fr');
|
|
expect(activeTextTrack.id).not.toBe(originalTextId);
|
|
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('enables text tracks when no matching audio is found', function(done) {
|
|
player.setPreferredLanguage('el');
|
|
player.load(newSource(languagesManifest)).then(function() {
|
|
var activeAudioTrack = getActiveAudioTrack();
|
|
expect(activeAudioTrack.lang).toBe('en');
|
|
|
|
var activeTextTrack = getActiveTextTrack();
|
|
expect(activeTextTrack.lang).toBe('el');
|
|
expect(activeTextTrack.enabled).toBe(true);
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('disables text tracks when matching audio is found', function(done) {
|
|
player.setPreferredLanguage('fr');
|
|
player.load(newSource(languagesManifest)).then(function() {
|
|
var activeAudioTrack = getActiveAudioTrack();
|
|
expect(activeAudioTrack.lang).toBe('fr');
|
|
|
|
var activeTextTrack = getActiveTextTrack();
|
|
expect(activeTextTrack.lang).toBe('fr');
|
|
expect(activeTextTrack.enabled).toBe(false);
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('setRestrictions', function() {
|
|
it('ignores video tracks above the maximum height', function(done) {
|
|
player.load(newSource(encryptedManifest)).then(function() {
|
|
var hdVideoTrack = getVideoTrackByHeight(720);
|
|
var sdVideoTrack = getVideoTrackByHeight(480);
|
|
expect(hdVideoTrack).not.toBe(null);
|
|
expect(sdVideoTrack).not.toBe(null);
|
|
|
|
var restrictions = player.getRestrictions();
|
|
restrictions.maxHeight = 480;
|
|
player.setRestrictions(restrictions);
|
|
|
|
hdVideoTrack = getVideoTrackByHeight(720);
|
|
sdVideoTrack = getVideoTrackByHeight(480);
|
|
expect(hdVideoTrack).toBe(null);
|
|
expect(sdVideoTrack).not.toBe(null);
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('ignores video tracks above the maximum width', function(done) {
|
|
player.load(newSource(encryptedManifest)).then(function() {
|
|
var hdVideoTrack = getVideoTrackByHeight(720);
|
|
var sdVideoTrack = getVideoTrackByHeight(480);
|
|
expect(hdVideoTrack).not.toBe(null);
|
|
expect(sdVideoTrack).not.toBe(null);
|
|
|
|
var restrictions = player.getRestrictions();
|
|
restrictions.maxWidth = 854;
|
|
player.setRestrictions(restrictions);
|
|
|
|
hdVideoTrack = getVideoTrackByHeight(720);
|
|
sdVideoTrack = getVideoTrackByHeight(480);
|
|
expect(hdVideoTrack).toBe(null);
|
|
expect(sdVideoTrack).not.toBe(null);
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('takes effect before the source is loaded', function(done) {
|
|
var restrictions = player.getRestrictions();
|
|
restrictions.maxHeight = 480;
|
|
player.setRestrictions(restrictions);
|
|
|
|
player.load(newSource(encryptedManifest)).then(function() {
|
|
var hdVideoTrack = getVideoTrackByHeight(720);
|
|
var sdVideoTrack = getVideoTrackByHeight(480);
|
|
expect(hdVideoTrack).toBe(null);
|
|
expect(sdVideoTrack).not.toBe(null);
|
|
|
|
var restrictions = player.getRestrictions();
|
|
restrictions.maxHeight = null;
|
|
player.setRestrictions(restrictions);
|
|
|
|
hdVideoTrack = getVideoTrackByHeight(720);
|
|
sdVideoTrack = getVideoTrackByHeight(480);
|
|
expect(hdVideoTrack).not.toBe(null);
|
|
expect(sdVideoTrack).not.toBe(null);
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('plays VP9 WebM', function(done) {
|
|
player.load(newSource(webmManifest)).then(function() {
|
|
player.play();
|
|
return waitForMovement();
|
|
}).then(function() {
|
|
expect(video.currentTime).toBeGreaterThan(0.0);
|
|
done();
|
|
}).catch(function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('dispatches errors on failure', function(done) {
|
|
player.removeEventListener('error', convertErrorToTestFailure, false);
|
|
var onError = jasmine.createSpy('onError');
|
|
player.addEventListener('error', onError, false);
|
|
|
|
// Ignore any errors in the promise chain.
|
|
player.load(newSource(bogusManifest)).catch(function(error) {});
|
|
|
|
// Expect the error handler to have been called.
|
|
delay(0.5).then(function() {
|
|
expect(onError.calls.any()).toBe(true);
|
|
done();
|
|
});
|
|
});
|
|
|
|
// TODO(story 1970528): add tests which exercise PSSH parsing,
|
|
// SegmentTemplate resolution, and SegmentList generation.
|
|
|
|
/**
|
|
* @param {!Event} event
|
|
*/
|
|
function convertErrorToTestFailure(event) {
|
|
// Treat all player errors as test failures.
|
|
var error = event.detail;
|
|
fail(error);
|
|
}
|
|
|
|
/**
|
|
* @param {string} manifest
|
|
* @return {!shaka.player.DashVideoSource}
|
|
*/
|
|
function newSource(manifest) {
|
|
var estimator = new shaka.util.EWMABandwidthEstimator();
|
|
return new shaka.player.DashVideoSource(manifest,
|
|
interpretContentProtection,
|
|
estimator);
|
|
}
|
|
|
|
/**
|
|
* @param {number} targetHeight
|
|
* @return {shaka.player.VideoTrack} or null if not found.
|
|
*/
|
|
function getVideoTrackByHeight(targetHeight) {
|
|
var tracks = player.getVideoTracks();
|
|
for (var i = 0; i < tracks.length; i++) {
|
|
if (tracks[i].height == targetHeight) {
|
|
return tracks[i];
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @return {shaka.player.TextTrack} or null if not found.
|
|
*/
|
|
function getActiveTextTrack() {
|
|
var tracks = player.getTextTracks();
|
|
for (var i = 0; i < tracks.length; ++i) {
|
|
if (tracks[i].active) {
|
|
return tracks[i];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @return {shaka.player.AudioTrack} or null if not found.
|
|
*/
|
|
function getActiveAudioTrack() {
|
|
var tracks = player.getAudioTracks();
|
|
for (var i = 0; i < tracks.length; ++i) {
|
|
if (tracks[i].active) {
|
|
return tracks[i];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @return {!Promise} resolved when the video's currentTime changes.
|
|
*/
|
|
function waitForMovement() {
|
|
var promise = new shaka.util.PublicPromise;
|
|
var originalTime = video.currentTime;
|
|
eventManager.listen(video, 'timeupdate', function() {
|
|
if (video.currentTime != originalTime) {
|
|
eventManager.unlisten(video, 'timeupdate');
|
|
promise.resolve();
|
|
}
|
|
});
|
|
return promise;
|
|
}
|
|
});
|
|
|