mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-15 16:06:41 +03:00
c7368024ae
I went ahead and implemented the full structured preference system that was discussed in https://github.com/shaka-project/shaka-player/issues/1591. Instead of just expanding languages to arrays, I replaced all 14 individual preference fields with 3 structured arrays: ```tsx preferredAudio (language, role, label, channelCount, codec, spatialAudio) preferredText (language, role, format, forced) preferredVideo (label, role, codec, hdrLevel, layout) ``` Each array entry works as an AND filter - so you can say things like "I want Korean with 5.1 surround, but if not available, English is fine too": ```tsx player.configure('preferredAudio', [ {language: 'ko', channelCount: 6}, {language: 'ko'}, {language: 'en'}, ]); ``` <img width="1728" height="965" alt="image" src="https://github.com/user-attachments/assets/7b088150-139b-475e-bdba-5bc77dd4e524" /> **Config** - Replaced the 14 individual fields with 3 arrays of typed preference objects (AudioPreference, TextPreference, VideoPreference). The old fields still work at runtime with a deprecation warning, so existing apps won't break immediately. **Demo** - The demo config UI now shows inline expandable preference lists instead of flat text inputs. You can add/remove entries and configure each field per entry. URL hash serialization was updated to use JSON format, with legacy param fallbacks preserved.
414 lines
13 KiB
JavaScript
414 lines
13 KiB
JavaScript
/*! @license
|
|
* Shaka Player
|
|
* Copyright 2016 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
describe('Codec Switching', () => {
|
|
const Util = shaka.test.Util;
|
|
|
|
/** @type {!HTMLVideoElement} */
|
|
let video;
|
|
/** @type {shaka.Player} */
|
|
let player;
|
|
/** @type {!shaka.util.EventManager} */
|
|
let eventManager;
|
|
|
|
let compiledShaka;
|
|
|
|
/** @type {!shaka.test.Waiter} */
|
|
let waiter;
|
|
|
|
beforeAll(async () => {
|
|
video = shaka.test.UiUtils.createVideoElement();
|
|
document.body.appendChild(video);
|
|
compiledShaka =
|
|
await shaka.test.Loader.loadShaka(getClientArg('uncompiled'));
|
|
});
|
|
|
|
beforeEach(async () => {
|
|
await shaka.test.TestScheme.createManifests(compiledShaka, '_compiled');
|
|
player = new compiledShaka.Player();
|
|
await player.attach(video);
|
|
|
|
// Disable allow MediaSource recoveries, which can interfere with playback
|
|
// tests.
|
|
player.configure('streaming.allowMediaSourceRecoveries', false);
|
|
|
|
// Disable stall detection, which can interfere with playback tests.
|
|
player.configure('streaming.stallEnabled', false);
|
|
|
|
eventManager = new shaka.util.EventManager();
|
|
waiter = new shaka.test.Waiter(eventManager);
|
|
waiter.setPlayer(player);
|
|
|
|
const onErrorSpy = jasmine.createSpy('onError');
|
|
onErrorSpy.and.callFake((event) => fail(event.detail));
|
|
eventManager.listen(player, 'error', Util.spyFunc(onErrorSpy));
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await player.unload();
|
|
eventManager.release();
|
|
await player.destroy();
|
|
});
|
|
|
|
afterAll(() => {
|
|
document.body.removeChild(video);
|
|
});
|
|
|
|
describe('for audio and only-audio content aac -> opus', () => {
|
|
it('can switch codecs RELOAD', async () => {
|
|
if (!await Util.isTypeSupported('audio/webm; codecs="opus"')) {
|
|
pending('Codec OPUS in WEBM is not supported by the platform.');
|
|
}
|
|
|
|
// English is AAC MP4.
|
|
const preferredAudioLanguage = 'en';
|
|
player.configure({
|
|
preferredAudio: [{
|
|
language: preferredAudioLanguage,
|
|
role: '',
|
|
label: '',
|
|
channelCount: 0,
|
|
codec: '',
|
|
}],
|
|
});
|
|
player.configure('manifest.disableVideo', true);
|
|
player.configure('mediaSource.codecSwitchingStrategy',
|
|
shaka.config.CodecSwitchingStrategy.RELOAD);
|
|
|
|
await player.load('/base/test/test/assets/dash-multi-codec/dash.mpd', 9);
|
|
await video.play();
|
|
await waiter.waitForMovementOrFailOnTimeout(video, 10);
|
|
|
|
expect(player.isLive()).toBe(false);
|
|
|
|
let audioTracks = player.getAudioTracks();
|
|
|
|
expect(audioTracks.length).toBe(2);
|
|
expect(audioTracks.find((t) => !!t.active).language).toBe('en');
|
|
|
|
// Spanish is Opus WebM.
|
|
const newAudioTrack = audioTracks.find((t) => t.language == 'es');
|
|
goog.asserts.assert(newAudioTrack, 'audio track must be non-null');
|
|
player.selectAudioTrack(newAudioTrack);
|
|
await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 10, 45);
|
|
|
|
audioTracks = player.getAudioTracks();
|
|
|
|
expect(audioTracks.find((t) => !!t.active).language).toBe('es');
|
|
});
|
|
|
|
it('can switch codecs SMOOTH', async () => {
|
|
if (!deviceDetected.supportsSmoothCodecSwitching('')) {
|
|
pending('SourceBuffer.changeType is not supported');
|
|
}
|
|
if (!await Util.isTypeSupported('audio/webm; codecs="opus"')) {
|
|
pending('Codec OPUS in WEBM is not supported by the platform.');
|
|
}
|
|
|
|
// English is AAC MP4.
|
|
const preferredAudioLanguage = 'en';
|
|
player.configure({
|
|
preferredAudio: [{
|
|
language: preferredAudioLanguage,
|
|
role: '',
|
|
label: '',
|
|
channelCount: 0,
|
|
codec: '',
|
|
}],
|
|
});
|
|
player.configure('manifest.disableVideo', true);
|
|
player.configure('mediaSource.codecSwitchingStrategy',
|
|
shaka.config.CodecSwitchingStrategy.SMOOTH);
|
|
|
|
await player.load('/base/test/test/assets/dash-multi-codec/dash.mpd', 9);
|
|
await video.play();
|
|
await waiter.waitForMovementOrFailOnTimeout(video, 10);
|
|
|
|
expect(player.isLive()).toBe(false);
|
|
|
|
let audioTracks = player.getAudioTracks();
|
|
|
|
expect(audioTracks.length).toBe(2);
|
|
expect(audioTracks.find((t) => !!t.active).language).toBe('en');
|
|
|
|
// Spanish is Opus WebM.
|
|
const newAudioTrack = audioTracks.find((t) => t.language == 'es');
|
|
goog.asserts.assert(newAudioTrack, 'audio track must be non-null');
|
|
player.selectAudioTrack(newAudioTrack);
|
|
await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 10, 45);
|
|
|
|
audioTracks = player.getAudioTracks();
|
|
|
|
expect(audioTracks.find((t) => !!t.active).language).toBe('es');
|
|
});
|
|
});
|
|
|
|
describe('for audio opus -> aac', () => {
|
|
it('can switch codecs RELOAD', async () => {
|
|
if (!await Util.isTypeSupported('audio/webm; codecs="opus"')) {
|
|
pending('Codec OPUS in WEBM is not supported by the platform.');
|
|
}
|
|
|
|
// English is AAC MP4.
|
|
const preferredAudioLanguage = 'en';
|
|
player.configure({
|
|
preferredAudio: [{
|
|
language: preferredAudioLanguage,
|
|
role: '',
|
|
label: '',
|
|
channelCount: 0,
|
|
codec: '',
|
|
}],
|
|
});
|
|
player.configure('mediaSource.codecSwitchingStrategy',
|
|
shaka.config.CodecSwitchingStrategy.RELOAD);
|
|
|
|
await player.load('/base/test/test/assets/dash-multi-codec/dash.mpd', 9);
|
|
await video.play();
|
|
await waiter.waitForMovementOrFailOnTimeout(video, 10);
|
|
|
|
expect(player.isLive()).toBe(false);
|
|
|
|
let audioTracks = player.getAudioTracks();
|
|
|
|
expect(audioTracks.length).toBe(2);
|
|
expect(audioTracks.find((t) => !!t.active).language).toBe('en');
|
|
|
|
// Spanish is Opus WebM.
|
|
const newAudioTrack = audioTracks.find((t) => t.language == 'es');
|
|
goog.asserts.assert(newAudioTrack, 'audio track must be non-null');
|
|
player.selectAudioTrack(newAudioTrack);
|
|
await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 10, 45);
|
|
|
|
audioTracks = player.getAudioTracks();
|
|
|
|
expect(audioTracks.find((t) => !!t.active).language).toBe('es');
|
|
});
|
|
|
|
it('can switch codecs SMOOTH', async () => {
|
|
if (!deviceDetected.supportsSmoothCodecSwitching('')) {
|
|
pending('SourceBuffer.changeType is not supported');
|
|
}
|
|
if (!await Util.isTypeSupported('audio/webm; codecs="opus"')) {
|
|
pending('Codec OPUS in WEBM is not supported by the platform.');
|
|
}
|
|
|
|
// English is AAC MP4.
|
|
const preferredAudioLanguage = 'en';
|
|
player.configure({
|
|
preferredAudio: [{
|
|
language: preferredAudioLanguage,
|
|
role: '',
|
|
label: '',
|
|
channelCount: 0,
|
|
codec: '',
|
|
}],
|
|
});
|
|
player.configure('mediaSource.codecSwitchingStrategy',
|
|
shaka.config.CodecSwitchingStrategy.SMOOTH);
|
|
|
|
await player.load('/base/test/test/assets/dash-multi-codec/dash.mpd', 9);
|
|
await video.play();
|
|
await waiter.waitForMovementOrFailOnTimeout(video, 10);
|
|
|
|
expect(player.isLive()).toBe(false);
|
|
|
|
let audioTracks = player.getAudioTracks();
|
|
|
|
expect(audioTracks.length).toBe(2);
|
|
expect(audioTracks.find((t) => !!t.active).language).toBe('en');
|
|
|
|
// Spanish is Opus WebM.
|
|
const newAudioTrack = audioTracks.find((t) => t.language == 'es');
|
|
goog.asserts.assert(newAudioTrack, 'audio track must be non-null');
|
|
player.selectAudioTrack(newAudioTrack);
|
|
await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 10, 45);
|
|
|
|
audioTracks = player.getAudioTracks();
|
|
|
|
expect(audioTracks.find((t) => !!t.active).language).toBe('es');
|
|
});
|
|
});
|
|
|
|
describe('for audio aac -> ec3', () => {
|
|
it('can switch codecs RELOAD', async () => {
|
|
if (!await Util.isTypeSupported('audio/mp4; codecs="ec-3"')) {
|
|
pending('Codec EC3 in MP4 is not supported by the platform.');
|
|
}
|
|
|
|
// English is AAC MP4.
|
|
const preferredAudioLanguage = 'en';
|
|
player.configure({
|
|
preferredAudio: [{
|
|
language: preferredAudioLanguage,
|
|
role: '',
|
|
label: '',
|
|
channelCount: 0,
|
|
codec: '',
|
|
}],
|
|
});
|
|
player.configure('mediaSource.codecSwitchingStrategy',
|
|
shaka.config.CodecSwitchingStrategy.RELOAD);
|
|
|
|
await player.load(
|
|
'/base/test/test/assets/dash-multi-codec-ec3/dash.mpd', 1);
|
|
await video.play();
|
|
await waiter.waitForMovementOrFailOnTimeout(video, 10);
|
|
|
|
expect(player.isLive()).toBe(false);
|
|
|
|
let audioTracks = player.getAudioTracks();
|
|
|
|
expect(audioTracks.length).toBe(2);
|
|
expect(audioTracks.find((t) => !!t.active).language).toBe('en');
|
|
|
|
// Spanish is EC3.
|
|
const newAudioTrack = audioTracks.find((t) => t.language == 'es');
|
|
goog.asserts.assert(newAudioTrack, 'audio track must be non-null');
|
|
player.selectAudioTrack(newAudioTrack);
|
|
await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 2, 45);
|
|
|
|
audioTracks = player.getAudioTracks();
|
|
|
|
expect(audioTracks.find((t) => !!t.active).language).toBe('es');
|
|
});
|
|
|
|
it('can switch codecs SMOOTH', async () => {
|
|
if (!deviceDetected.supportsSmoothCodecSwitching('')) {
|
|
pending('SourceBuffer.changeType is not supported');
|
|
}
|
|
if (!await Util.isTypeSupported('audio/mp4; codecs="ec-3"')) {
|
|
pending('Codec EC3 in MP4 is not supported by the platform.');
|
|
}
|
|
|
|
// English is AAC MP4.
|
|
const preferredAudioLanguage = 'en';
|
|
player.configure({
|
|
preferredAudio: [{
|
|
language: preferredAudioLanguage,
|
|
role: '',
|
|
label: '',
|
|
channelCount: 0,
|
|
codec: '',
|
|
}],
|
|
});
|
|
player.configure('mediaSource.codecSwitchingStrategy',
|
|
shaka.config.CodecSwitchingStrategy.SMOOTH);
|
|
|
|
await player.load(
|
|
'/base/test/test/assets/dash-multi-codec-ec3/dash.mpd', 1);
|
|
await video.play();
|
|
await waiter.waitForMovementOrFailOnTimeout(video, 10);
|
|
|
|
expect(player.isLive()).toBe(false);
|
|
|
|
let audioTracks = player.getAudioTracks();
|
|
|
|
expect(audioTracks.length).toBe(2);
|
|
expect(audioTracks.find((t) => !!t.active).language).toBe('en');
|
|
|
|
// Spanish is EC3.
|
|
const newAudioTrack = audioTracks.find((t) => t.language == 'es');
|
|
goog.asserts.assert(newAudioTrack, 'audio track must be non-null');
|
|
player.selectAudioTrack(newAudioTrack);
|
|
await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 2, 45);
|
|
|
|
audioTracks = player.getAudioTracks();
|
|
|
|
expect(audioTracks.find((t) => !!t.active).language).toBe('es');
|
|
});
|
|
});
|
|
|
|
describe('for audio ec3 -> aac', () => {
|
|
it('can switch codecs RELOAD', async () => {
|
|
if (!await Util.isTypeSupported('audio/mp4; codecs="ec-3"')) {
|
|
pending('Codec EC3 in MP4 is not supported by the platform.');
|
|
}
|
|
|
|
// Spanish is EC3.
|
|
const preferredAudioLanguage = 'es';
|
|
player.configure({
|
|
preferredAudio: [{
|
|
language: preferredAudioLanguage,
|
|
role: '',
|
|
label: '',
|
|
channelCount: 0,
|
|
codec: '',
|
|
}],
|
|
});
|
|
player.configure('mediaSource.codecSwitchingStrategy',
|
|
shaka.config.CodecSwitchingStrategy.RELOAD);
|
|
|
|
await player.load(
|
|
'/base/test/test/assets/dash-multi-codec-ec3/dash.mpd', 1);
|
|
await video.play();
|
|
await waiter.waitForMovementOrFailOnTimeout(video, 10);
|
|
|
|
expect(player.isLive()).toBe(false);
|
|
|
|
let audioTracks = player.getAudioTracks();
|
|
|
|
expect(audioTracks.length).toBe(2);
|
|
expect(audioTracks.find((t) => !!t.active).language).toBe('es');
|
|
|
|
// English is AAC MP4.
|
|
const newAudioTrack = audioTracks.find((t) => t.language == 'en');
|
|
goog.asserts.assert(newAudioTrack, 'audio track must be non-null');
|
|
player.selectAudioTrack(newAudioTrack);
|
|
await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 2, 45);
|
|
|
|
audioTracks = player.getAudioTracks();
|
|
|
|
expect(audioTracks.find((t) => !!t.active).language).toBe('en');
|
|
});
|
|
|
|
it('can switch codecs SMOOTH', async () => {
|
|
if (!deviceDetected.supportsSmoothCodecSwitching('')) {
|
|
pending('SourceBuffer.changeType is not supported');
|
|
}
|
|
if (!await Util.isTypeSupported('audio/mp4; codecs="ec-3"')) {
|
|
pending('Codec EC3 in MP4 is not supported by the platform.');
|
|
}
|
|
|
|
// Spanish is EC3.
|
|
const preferredAudioLanguage = 'es';
|
|
player.configure({
|
|
preferredAudio: [{
|
|
language: preferredAudioLanguage,
|
|
role: '',
|
|
label: '',
|
|
channelCount: 0,
|
|
codec: '',
|
|
}],
|
|
});
|
|
player.configure('mediaSource.codecSwitchingStrategy',
|
|
shaka.config.CodecSwitchingStrategy.SMOOTH);
|
|
|
|
await player.load(
|
|
'/base/test/test/assets/dash-multi-codec-ec3/dash.mpd', 1);
|
|
await video.play();
|
|
await waiter.waitForMovementOrFailOnTimeout(video, 10);
|
|
|
|
expect(player.isLive()).toBe(false);
|
|
|
|
let audioTracks = player.getAudioTracks();
|
|
|
|
expect(audioTracks.length).toBe(2);
|
|
expect(audioTracks.find((t) => !!t.active).language).toBe('es');
|
|
|
|
// English is AAC MP4.
|
|
const newAudioTrack = audioTracks.find((t) => t.language == 'en');
|
|
goog.asserts.assert(newAudioTrack, 'audio track must be non-null');
|
|
player.selectAudioTrack(newAudioTrack);
|
|
await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 2, 45);
|
|
|
|
audioTracks = player.getAudioTracks();
|
|
|
|
expect(audioTracks.find((t) => !!t.active).language).toBe('en');
|
|
});
|
|
});
|
|
});
|