mirror of
https://github.com/androidx/media.git
synced 2025-05-09 16:40:55 +08:00
471 lines
17 KiB
JavaScript
471 lines
17 KiB
JavaScript
/**
|
|
* Copyright (C) 2018 The Android Open Source Project
|
|
*
|
|
* 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 Unit tests for playback methods.
|
|
*/
|
|
|
|
goog.module('exoplayer.cast.test');
|
|
goog.setTestOnly();
|
|
|
|
const ConfigurationFactory = goog.require('exoplayer.cast.ConfigurationFactory');
|
|
const Player = goog.require('exoplayer.cast.Player');
|
|
const mocks = goog.require('exoplayer.cast.test.mocks');
|
|
const testSuite = goog.require('goog.testing.testSuite');
|
|
const util = goog.require('exoplayer.cast.test.util');
|
|
|
|
let player;
|
|
let shakaFake;
|
|
|
|
testSuite({
|
|
setUp() {
|
|
mocks.setUp();
|
|
shakaFake = mocks.createShakaFake();
|
|
player = new Player(shakaFake, new ConfigurationFactory());
|
|
},
|
|
|
|
/** Tests the player initialisation */
|
|
testPlayerInitialisation() {
|
|
mocks.state().isSilent = true;
|
|
const states = [];
|
|
let stateCounter = 0;
|
|
let currentState;
|
|
player.addPlayerListener((playerState) => {
|
|
states.push(playerState);
|
|
});
|
|
|
|
// Dump the initial state manually.
|
|
player.invalidate();
|
|
stateCounter++;
|
|
assertEquals(stateCounter, states.length);
|
|
currentState = states[stateCounter - 1];
|
|
assertEquals(0, currentState.mediaQueue.length);
|
|
assertEquals(0, currentState.windowIndex);
|
|
assertNull(currentState.playbackPosition);
|
|
|
|
// Seek with uuid to prepare with later
|
|
const uuid = 'uuid1';
|
|
player.seekToUuid(uuid, 30 * 1000);
|
|
stateCounter++;
|
|
assertEquals(stateCounter, states.length);
|
|
currentState = states[stateCounter - 1];
|
|
assertEquals(30 * 1000, player.getCurrentPositionMs());
|
|
assertEquals(0, player.getCurrentWindowIndex());
|
|
assertEquals(-1, player.windowIndex_);
|
|
assertEquals(1, currentState.playbackPosition.periodId);
|
|
assertEquals(uuid, currentState.playbackPosition.uuid);
|
|
assertEquals(uuid, player.uuidToPrepare_);
|
|
|
|
// Add a DASH media item.
|
|
player.addQueueItems(0, util.queue.slice(0, 2));
|
|
stateCounter++;
|
|
assertEquals(stateCounter, states.length);
|
|
currentState = states[stateCounter - 1];
|
|
assertEquals('IDLE', currentState.playbackState);
|
|
assertNotNull(currentState.playbackPosition);
|
|
util.assertUuidIndexMap(player.queueUuidIndexMap_, currentState.mediaQueue);
|
|
|
|
// Prepare.
|
|
player.prepare();
|
|
stateCounter++;
|
|
assertEquals(stateCounter, states.length);
|
|
currentState = states[stateCounter - 1];
|
|
assertEquals(2, currentState.mediaQueue.length);
|
|
assertEquals('BUFFERING', currentState.playbackState);
|
|
assertEquals(
|
|
Player.DUMMY_MEDIA_ITEM_INFO, currentState.mediaItemsInfo[uuid]);
|
|
assertNull(player.uuidToPrepare_);
|
|
|
|
// The video element starts waiting.
|
|
mocks.state().isSilent = false;
|
|
mocks.notifyListeners('waiting');
|
|
// Nothing happens, masked buffering state after preparing.
|
|
assertEquals(stateCounter, states.length);
|
|
|
|
// The manifest arrives.
|
|
mocks.notifyListeners('streaming');
|
|
stateCounter++;
|
|
assertEquals(stateCounter, states.length);
|
|
currentState = states[stateCounter - 1];
|
|
assertEquals(2, currentState.mediaQueue.length);
|
|
assertEquals('BUFFERING', currentState.playbackState);
|
|
assertEquals(uuid, currentState.playbackPosition.uuid);
|
|
assertEquals(0, currentState.playbackPosition.periodId);
|
|
assertEquals(30 * 1000, currentState.playbackPosition.positionMs);
|
|
// The dummy media item info has been replaced by the real one.
|
|
assertEquals(20000000, currentState.mediaItemsInfo[uuid].windowDurationUs);
|
|
assertEquals(0, currentState.mediaItemsInfo[uuid].defaultStartPositionUs);
|
|
assertEquals(0, currentState.mediaItemsInfo[uuid].positionInFirstPeriodUs);
|
|
assertTrue(currentState.mediaItemsInfo[uuid].isSeekable);
|
|
assertFalse(currentState.mediaItemsInfo[uuid].isDynamic);
|
|
|
|
// Tracks have initially changed.
|
|
mocks.notifyListeners('trackschanged');
|
|
// Nothing happens because the media item info remains the same.
|
|
assertEquals(stateCounter, states.length);
|
|
|
|
// The video element reports the first frame rendered.
|
|
mocks.notifyListeners('loadeddata');
|
|
stateCounter++;
|
|
assertEquals(stateCounter, states.length);
|
|
currentState = states[stateCounter - 1];
|
|
assertEquals(2, currentState.mediaQueue.length);
|
|
assertEquals('READY', currentState.playbackState);
|
|
assertEquals(uuid, currentState.playbackPosition.uuid);
|
|
assertEquals(0, currentState.playbackPosition.periodId);
|
|
assertEquals(30 * 1000, currentState.playbackPosition.positionMs);
|
|
|
|
// Playback starts.
|
|
mocks.notifyListeners('playing');
|
|
// Nothing happens; we are ready already.
|
|
assertEquals(stateCounter, states.length);
|
|
|
|
// Add another queue item.
|
|
player.addQueueItems(1, util.queue.slice(3, 4));
|
|
stateCounter++;
|
|
assertEquals(stateCounter, states.length);
|
|
mocks.state().isSilent = true;
|
|
// Seek to the next queue item.
|
|
player.seekToWindow(1, 0);
|
|
stateCounter++;
|
|
assertEquals(stateCounter, states.length);
|
|
currentState = states[stateCounter - 1];
|
|
const uuid1 = currentState.mediaQueue[1].uuid;
|
|
assertEquals(
|
|
Player.DUMMY_MEDIA_ITEM_INFO, currentState.mediaItemsInfo[uuid1]);
|
|
util.assertUuidIndexMap(player.queueUuidIndexMap_, currentState.mediaQueue);
|
|
|
|
// The video element starts waiting.
|
|
mocks.state().isSilent = false;
|
|
mocks.notifyListeners('waiting');
|
|
// Nothing happens, masked buffering state after preparing.
|
|
assertEquals(stateCounter, states.length);
|
|
|
|
// The manifest arrives.
|
|
mocks.notifyListeners('streaming');
|
|
stateCounter++;
|
|
assertEquals(stateCounter, states.length);
|
|
currentState = states[stateCounter - 1];
|
|
// The dummy media item info has been replaced by the real one.
|
|
assertEquals(20000000, currentState.mediaItemsInfo[uuid].windowDurationUs);
|
|
assertEquals(0, currentState.mediaItemsInfo[uuid].defaultStartPositionUs);
|
|
assertEquals(0, currentState.mediaItemsInfo[uuid].positionInFirstPeriodUs);
|
|
assertTrue(currentState.mediaItemsInfo[uuid].isSeekable);
|
|
assertFalse(currentState.mediaItemsInfo[uuid].isDynamic);
|
|
},
|
|
|
|
/** Tests next and previous window when not yet prepared. */
|
|
testNextPreviousWindow_notPrepared() {
|
|
assertEquals(-1, player.getNextWindowIndex());
|
|
assertEquals(-1, player.getPreviousWindowIndex());
|
|
player.addQueueItems(0, util.queue.slice(0, 2));
|
|
assertEquals(-1, player.getNextWindowIndex());
|
|
assertEquals(-1, player.getPreviousWindowIndex());
|
|
},
|
|
|
|
/** Tests setting play when ready. */
|
|
testPlayWhenReady() {
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
let playWhenReady = false;
|
|
player.addPlayerListener((state) => {
|
|
playWhenReady = state.playWhenReady;
|
|
});
|
|
|
|
assertEquals(false, player.getPlayWhenReady());
|
|
assertEquals(false, playWhenReady);
|
|
|
|
player.setPlayWhenReady(true);
|
|
assertEquals(true, player.getPlayWhenReady());
|
|
assertEquals(true, playWhenReady);
|
|
|
|
player.setPlayWhenReady(false);
|
|
assertEquals(false, player.getPlayWhenReady());
|
|
assertEquals(false, playWhenReady);
|
|
},
|
|
|
|
/** Tests seeking to another position in the actual window. */
|
|
async testSeek_inWindow() {
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
await player.seekToWindow(0, 1000);
|
|
|
|
assertEquals(1, shakaFake.getMediaElement().currentTime);
|
|
assertEquals(1000, player.getCurrentPositionMs());
|
|
assertEquals(0, player.getCurrentWindowIndex());
|
|
},
|
|
|
|
/** Tests seeking to another window. */
|
|
async testSeek_nextWindow() {
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
await player.prepare();
|
|
assertEquals(util.queue[0].media.uri, shakaFake.getMediaElement().src);
|
|
assertEquals(-1, player.getPreviousWindowIndex());
|
|
assertEquals(1, player.getNextWindowIndex());
|
|
|
|
player.seekToWindow(1, 2000);
|
|
assertEquals(0, player.getPreviousWindowIndex());
|
|
assertEquals(2, player.getNextWindowIndex());
|
|
assertEquals(2000, player.getCurrentPositionMs());
|
|
assertEquals(1, player.getCurrentWindowIndex());
|
|
assertEquals(util.queue[1].media.uri, mocks.state().loadedUri);
|
|
},
|
|
|
|
/** Tests the repeat mode 'none' */
|
|
testRepeatMode_none() {
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.prepare();
|
|
assertEquals(Player.RepeatMode.OFF, player.getRepeatMode());
|
|
assertEquals(-1, player.getPreviousWindowIndex());
|
|
assertEquals(1, player.getNextWindowIndex());
|
|
|
|
player.seekToWindow(2, 0);
|
|
assertEquals(1, player.getPreviousWindowIndex());
|
|
assertEquals(-1, player.getNextWindowIndex());
|
|
},
|
|
|
|
/** Tests the repeat mode 'all'. */
|
|
testRepeatMode_all() {
|
|
let repeatMode;
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.prepare();
|
|
player.addPlayerListener((state) => {
|
|
repeatMode = state.repeatMode;
|
|
});
|
|
player.setRepeatMode(Player.RepeatMode.ALL);
|
|
assertEquals(Player.RepeatMode.ALL, repeatMode);
|
|
|
|
player.seekToWindow(0,0);
|
|
assertEquals(2, player.getPreviousWindowIndex());
|
|
assertEquals(1, player.getNextWindowIndex());
|
|
|
|
player.seekToWindow(2, 0);
|
|
assertEquals(1, player.getPreviousWindowIndex());
|
|
assertEquals(0, player.getNextWindowIndex());
|
|
},
|
|
|
|
/**
|
|
* Tests navigation within the queue when repeat mode and shuffle mode is on.
|
|
*/
|
|
testRepeatMode_all_inShuffleMode() {
|
|
const initialOrder = [2, 1, 0];
|
|
let shuffleOrder;
|
|
let windowIndex;
|
|
player.addQueueItems(0, util.queue.slice(0, 3), initialOrder);
|
|
player.prepare();
|
|
player.addPlayerListener((state) => {
|
|
shuffleOrder = state.shuffleOrder;
|
|
windowIndex = state.windowIndex;
|
|
});
|
|
player.setRepeatMode(Player.RepeatMode.ALL);
|
|
player.setShuffleModeEnabled(true);
|
|
assertEquals(windowIndex, player.shuffleOrder_[player.shuffleIndex_]);
|
|
assertArrayEquals(initialOrder, shuffleOrder);
|
|
|
|
player.seekToWindow(shuffleOrder[2], 0);
|
|
assertEquals(shuffleOrder[2], windowIndex);
|
|
assertEquals(shuffleOrder[0], player.getNextWindowIndex());
|
|
assertEquals(shuffleOrder[1], player.getPreviousWindowIndex());
|
|
|
|
player.seekToWindow(shuffleOrder[0], 0);
|
|
assertEquals(shuffleOrder[0], windowIndex);
|
|
},
|
|
|
|
/** Tests the repeat mode 'one' */
|
|
testRepeatMode_one() {
|
|
let repeatMode;
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.prepare();
|
|
player.addPlayerListener((state) => {
|
|
repeatMode = state.repeatMode;
|
|
});
|
|
player.setRepeatMode(Player.RepeatMode.ONE);
|
|
assertEquals(Player.RepeatMode.ONE, repeatMode);
|
|
assertEquals(0, player.getPreviousWindowIndex());
|
|
assertEquals(0, player.getNextWindowIndex());
|
|
|
|
player.seekToWindow(1, 0);
|
|
assertEquals(1, player.getPreviousWindowIndex());
|
|
assertEquals(1, player.getNextWindowIndex());
|
|
|
|
player.setShuffleModeEnabled(true);
|
|
assertEquals(1, player.getPreviousWindowIndex());
|
|
assertEquals(1, player.getNextWindowIndex());
|
|
},
|
|
|
|
/** Tests building a media item info from the manifest. */
|
|
testBuildMediaItemInfo_fromManifest() {
|
|
let mediaItemInfos = null;
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.addPlayerListener((state) => {
|
|
mediaItemInfos = state.mediaItemsInfo;
|
|
});
|
|
player.seekToWindow(1, 0);
|
|
player.prepare();
|
|
assertUndefined(mediaItemInfos['uuid0']);
|
|
const mediaItemInfo = mediaItemInfos['uuid1'];
|
|
assertNotUndefined(mediaItemInfo);
|
|
assertFalse(mediaItemInfo.isDynamic);
|
|
assertTrue(mediaItemInfo.isSeekable);
|
|
assertEquals(0, mediaItemInfo.defaultStartPositionUs);
|
|
assertEquals(20 * 1000 * 1000, mediaItemInfo.windowDurationUs);
|
|
assertEquals(1, mediaItemInfo.periods.length);
|
|
assertEquals(20 * 1000 * 1000, mediaItemInfo.periods[0].durationUs);
|
|
},
|
|
|
|
/** Tests building a media item info with multiple periods. */
|
|
testBuildMediaItemInfo_fromManifest_multiPeriod() {
|
|
let mediaItemInfos = null;
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.addPlayerListener((state) => {
|
|
mediaItemInfos = state.mediaItemsInfo;
|
|
});
|
|
// Setting manifest properties to emulate a multiperiod stream manifest.
|
|
mocks.state().getManifest().periods.push({startTime: 20});
|
|
mocks.state().manifestState.windowDuration = 50;
|
|
player.seekToWindow(1, 0);
|
|
player.prepare();
|
|
|
|
const mediaItemInfo = mediaItemInfos['uuid1'];
|
|
assertNotUndefined(mediaItemInfo);
|
|
assertFalse(mediaItemInfo.isDynamic);
|
|
assertTrue(mediaItemInfo.isSeekable);
|
|
assertEquals(0, mediaItemInfo.defaultStartPositionUs);
|
|
assertEquals(50 * 1000 * 1000, mediaItemInfo.windowDurationUs);
|
|
assertEquals(2, mediaItemInfo.periods.length);
|
|
assertEquals(20 * 1000 * 1000, mediaItemInfo.periods[0].durationUs);
|
|
assertEquals(30 * 1000 * 1000, mediaItemInfo.periods[1].durationUs);
|
|
},
|
|
|
|
/** Tests building a media item info from a live manifest. */
|
|
testBuildMediaItemInfo_fromManifest_live() {
|
|
let mediaItemInfos = null;
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.addPlayerListener((state) => {
|
|
mediaItemInfos = state.mediaItemsInfo;
|
|
});
|
|
// Setting manifest properties to emulate a live stream manifest.
|
|
mocks.state().manifestState.isLive = true;
|
|
mocks.state().manifestState.windowDuration = 30;
|
|
mocks.state().manifestState.delay = 10;
|
|
mocks.state().getManifest().periods.push({startTime: 20});
|
|
player.seekToWindow(1, 0);
|
|
player.prepare();
|
|
|
|
const mediaItemInfo = mediaItemInfos['uuid1'];
|
|
assertNotUndefined(mediaItemInfo);
|
|
assertTrue(mediaItemInfo.isDynamic);
|
|
assertTrue(mediaItemInfo.isSeekable);
|
|
assertEquals(20 * 1000 * 1000, mediaItemInfo.defaultStartPositionUs);
|
|
assertEquals(20 * 1000 * 1000, mediaItemInfo.windowDurationUs);
|
|
assertEquals(2, mediaItemInfo.periods.length);
|
|
assertEquals(20 * 1000 * 1000, mediaItemInfo.periods[0].durationUs);
|
|
assertEquals(Infinity, mediaItemInfo.periods[1].durationUs);
|
|
},
|
|
|
|
/** Tests whether the shaka request filter is set for life streams. */
|
|
testRequestFilterIsSetAndRemovedForLive() {
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
|
|
// Set manifest properties to emulate a live stream manifest.
|
|
mocks.state().manifestState.isLive = true;
|
|
mocks.state().manifestState.windowDuration = 30;
|
|
mocks.state().manifestState.delay = 10;
|
|
mocks.state().getManifest().periods.push({startTime: 20});
|
|
|
|
assertNull(mocks.state().responseFilter);
|
|
assertFalse(player.isManifestFilterRegistered_);
|
|
player.seekToWindow(1, 0);
|
|
player.prepare();
|
|
assertNotNull(mocks.state().responseFilter);
|
|
assertTrue(player.isManifestFilterRegistered_);
|
|
|
|
// Set manifest properties to emulate a non-live stream */
|
|
mocks.state().manifestState.isLive = false;
|
|
mocks.state().manifestState.windowDuration = 20;
|
|
mocks.state().manifestState.delay = 0;
|
|
mocks.state().getManifest().periods.push({startTime: 20});
|
|
|
|
player.seekToWindow(0, 0);
|
|
assertNull(mocks.state().responseFilter);
|
|
assertFalse(player.isManifestFilterRegistered_);
|
|
},
|
|
|
|
/** Tests whether the media info is removed when queue item is removed. */
|
|
testRemoveMediaItemInfo() {
|
|
let mediaItemInfos = null;
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.addPlayerListener((state) => {
|
|
mediaItemInfos = state.mediaItemsInfo;
|
|
});
|
|
player.seekToWindow(1, 0);
|
|
player.prepare();
|
|
assertNotUndefined(mediaItemInfos['uuid1']);
|
|
player.removeQueueItems(['uuid1']);
|
|
assertUndefined(mediaItemInfos['uuid1']);
|
|
},
|
|
|
|
/** Tests shuffling. */
|
|
testSetShuffeModeEnabled() {
|
|
let shuffleModeEnabled = false;
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.addPlayerListener((state) => {
|
|
shuffleModeEnabled = state.shuffleModeEnabled;
|
|
});
|
|
player.setShuffleModeEnabled(true);
|
|
assertTrue(shuffleModeEnabled);
|
|
|
|
player.setShuffleModeEnabled(false);
|
|
assertFalse(shuffleModeEnabled);
|
|
},
|
|
|
|
/** Tests setting a new playback order. */
|
|
async testSetShuffleOrder() {
|
|
const defaultOrder = [0, 1, 2];
|
|
let shuffleOrder;
|
|
player.addPlayerListener((state) => {
|
|
shuffleOrder = state.shuffleOrder;
|
|
});
|
|
await player.addQueueItems(0, util.queue.slice(0, 3), defaultOrder);
|
|
assertArrayEquals(defaultOrder, shuffleOrder);
|
|
|
|
player.setShuffleOrder_([2, 1, 0]);
|
|
assertArrayEquals([2, 1, 0], player.shuffleOrder_);
|
|
},
|
|
|
|
/** Tests setting a new playback order with incorrect length. */
|
|
async testSetShuffleOrder_incorrectLength() {
|
|
const defaultOrder = [0, 1, 2];
|
|
let shuffleOrder;
|
|
player.addPlayerListener((state) => {
|
|
shuffleOrder = state.shuffleOrder;
|
|
});
|
|
await player.addQueueItems(0, util.queue.slice(0, 3), defaultOrder);
|
|
assertArrayEquals(defaultOrder, shuffleOrder);
|
|
|
|
shuffleOrder = undefined;
|
|
player.setShuffleOrder_([2, 1]);
|
|
assertUndefined(shuffleOrder);
|
|
},
|
|
|
|
/** Tests falling into ENDED when prepared with empty queue. */
|
|
testPrepare_withEmptyQueue() {
|
|
player.seekToUuid('uuid1000', 1000);
|
|
assertEquals('uuid1000', player.uuidToPrepare_);
|
|
player.prepare();
|
|
assertEquals('ENDED', player.getPlaybackState());
|
|
assertNull(player.uuidToPrepare_);
|
|
player.seekToUuid('uuid1000', 1000);
|
|
assertNull(player.uuidToPrepare_);
|
|
},
|
|
});
|