Files
shaka-player/test/codec_switching/codec_switching_integration.js
T
Andy(김규회) c7368024ae feat: Select default track by a list of preferences (#9542)
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.
2026-02-20 10:12:08 +01:00

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');
});
});
});