Files
mediabunny/test/node/hls-output.test.ts
T

794 lines
24 KiB
TypeScript

import { expect, test, vi } from 'vitest';
import { Output, OutputTrackGroup } from '../../src/output.js';
import { HLS_OUTPUT_FORMATS_DEFAULT, HlsOutputFormat } from '../../src/output-format.js';
import { NullTarget } from '../../src/target.js';
import { EncodedAudioPacketSource, EncodedVideoPacketSource } from '../../src/media-source.js';
import { HlsMuxer } from '../../src/hls/hls-muxer.js';
import { AudioCodec, VideoCodec } from '../../src/codec.js';
const videoSource = (codec: VideoCodec = 'avc') => new EncodedVideoPacketSource(codec);
const audioSource = (codec: AudioCodec = 'aac') => new EncodedAudioPacketSource(codec);
test('Playlist assignment, single video', async () => {
const output = new Output({
format: new HlsOutputFormat({
segmentFormats: HLS_OUTPUT_FORMATS_DEFAULT,
}),
target: () => new NullTarget(),
rootPath: '',
});
output.addVideoTrack(videoSource());
await output.start();
const muxer = output._muxer as HlsMuxer;
const decl = muxer.playlistDeclarations;
expect(decl).toHaveLength(1);
expect(decl[0]!.playlist.tracks).toHaveLength(1);
expect(decl[0]!.groupId).toBeNull();
expect(decl[0]!.references).toHaveLength(0);
});
test('Playlist assignment, single audio', async () => {
const output = new Output({
format: new HlsOutputFormat({
segmentFormats: HLS_OUTPUT_FORMATS_DEFAULT,
}),
target: () => new NullTarget(),
rootPath: '',
});
output.addAudioTrack(audioSource());
await output.start();
const muxer = output._muxer as HlsMuxer;
const decl = muxer.playlistDeclarations;
expect(decl).toHaveLength(1);
expect(decl[0]!.playlist.tracks).toHaveLength(1);
expect(decl[0]!.groupId).toBeNull();
expect(decl[0]!.references).toHaveLength(0);
});
test('Playlist assignment, multiple video', async () => {
const output = new Output({
format: new HlsOutputFormat({
segmentFormats: HLS_OUTPUT_FORMATS_DEFAULT,
}),
target: () => new NullTarget(),
rootPath: '',
});
output.addVideoTrack(videoSource());
output.addVideoTrack(videoSource());
await output.start();
const muxer = output._muxer as HlsMuxer;
const decl = muxer.playlistDeclarations;
expect(decl).toHaveLength(2);
expect(decl[0]!.playlist.tracks).toHaveLength(1);
expect(decl[0]!.groupId).toBeNull();
expect(decl[0]!.references).toHaveLength(0);
expect(decl[1]!.playlist.tracks).toHaveLength(1);
expect(decl[1]!.groupId).toBeNull();
expect(decl[1]!.references).toHaveLength(0);
});
test('Playlist assignment, multiple audio', async () => {
const output = new Output({
format: new HlsOutputFormat({
segmentFormats: HLS_OUTPUT_FORMATS_DEFAULT,
}),
target: () => new NullTarget(),
rootPath: '',
});
output.addAudioTrack(audioSource());
output.addAudioTrack(audioSource());
await output.start();
const muxer = output._muxer as HlsMuxer;
const decl = muxer.playlistDeclarations;
expect(decl).toHaveLength(2);
expect(decl[0]!.playlist.tracks).toHaveLength(1);
expect(decl[0]!.groupId).toBeNull();
expect(decl[0]!.references).toHaveLength(0);
expect(decl[1]!.playlist.tracks).toHaveLength(1);
expect(decl[1]!.groupId).toBeNull();
expect(decl[1]!.references).toHaveLength(0);
});
test('Playlist assignment, multiple video with different metadata #1', async () => {
const output = new Output({
format: new HlsOutputFormat({
segmentFormats: HLS_OUTPUT_FORMATS_DEFAULT,
}),
target: () => new NullTarget(),
rootPath: '',
});
output.addVideoTrack(videoSource(), { languageCode: 'eng' });
output.addVideoTrack(videoSource(), { languageCode: 'esp' });
await output.start();
const muxer = output._muxer as HlsMuxer;
const decl = muxer.playlistDeclarations;
expect(decl).toHaveLength(3);
expect(decl[0]!.playlist.tracks).toHaveLength(1);
expect(decl[0]!.groupId).toBe('video-1');
expect(decl[0]!.references).toHaveLength(0);
expect(decl[0]!.noUri).toBe(true);
expect(decl[1]!.playlist.tracks).toHaveLength(1);
expect(decl[1]!.groupId).toBe('video-1');
expect(decl[1]!.references).toHaveLength(0);
expect(decl[1]!.noUri).toBe(false);
expect(decl[2]!.playlist.tracks).toHaveLength(1);
expect(decl[2]!.groupId).toBeNull();
expect(decl[2]!.references).toEqual(decl.slice(0, 2));
expect(decl[2]!.playlist).toBe(decl[0]!.playlist);
});
test('Playlist assignment, multiple video with different metadata #2', async () => {
const output = new Output({
format: new HlsOutputFormat({
segmentFormats: HLS_OUTPUT_FORMATS_DEFAULT,
}),
target: () => new NullTarget(),
rootPath: '',
});
output.addVideoTrack(videoSource(), { disposition: { primary: true } });
output.addVideoTrack(videoSource(), { disposition: { primary: false } });
await output.start();
const muxer = output._muxer as HlsMuxer;
const decl = muxer.playlistDeclarations;
expect(decl).toHaveLength(3);
expect(decl[0]!.playlist.tracks).toHaveLength(1);
expect(decl[0]!.groupId).toBe('video-1');
expect(decl[0]!.references).toHaveLength(0);
expect(decl[0]!.noUri).toBe(true);
expect(decl[1]!.playlist.tracks).toHaveLength(1);
expect(decl[1]!.groupId).toBe('video-1');
expect(decl[1]!.references).toHaveLength(0);
expect(decl[1]!.noUri).toBe(false);
expect(decl[2]!.playlist.tracks).toHaveLength(1);
expect(decl[2]!.groupId).toBeNull();
expect(decl[2]!.references).toEqual(decl.slice(0, 2));
expect(decl[2]!.playlist).toBe(decl[0]!.playlist);
});
test('Playlist assignment, multiple audio with different metadata', async () => {
const output = new Output({
format: new HlsOutputFormat({
segmentFormats: HLS_OUTPUT_FORMATS_DEFAULT,
}),
target: () => new NullTarget(),
rootPath: '',
});
output.addAudioTrack(audioSource(), { languageCode: 'eng' });
output.addAudioTrack(audioSource(), { languageCode: 'esp' });
await output.start();
const muxer = output._muxer as HlsMuxer;
const decl = muxer.playlistDeclarations;
expect(decl).toHaveLength(3);
expect(decl[0]!.playlist.tracks).toHaveLength(1);
expect(decl[0]!.groupId).toBe('audio-1');
expect(decl[0]!.references).toHaveLength(0);
expect(decl[0]!.noUri).toBe(true);
expect(decl[1]!.playlist.tracks).toHaveLength(1);
expect(decl[1]!.groupId).toBe('audio-1');
expect(decl[1]!.references).toHaveLength(0);
expect(decl[1]!.noUri).toBe(false);
expect(decl[2]!.playlist.tracks).toHaveLength(1);
expect(decl[2]!.groupId).toBeNull();
expect(decl[2]!.references).toEqual(decl.slice(0, 2));
expect(decl[2]!.playlist).toBe(decl[0]!.playlist);
});
test('Playlist assignment, video and audio', async () => {
const output = new Output({
format: new HlsOutputFormat({
segmentFormats: HLS_OUTPUT_FORMATS_DEFAULT,
}),
target: () => new NullTarget(),
rootPath: '',
});
output.addVideoTrack(videoSource());
output.addAudioTrack(audioSource());
await output.start();
const muxer = output._muxer as HlsMuxer;
const decl = muxer.playlistDeclarations;
expect(decl).toHaveLength(1);
expect(decl[0]!.playlist.tracks).toHaveLength(2);
expect(decl[0]!.groupId).toBeNull();
expect(decl[0]!.references).toHaveLength(0);
});
test('Playlist assignment, one video and multiple audio', async () => {
const output = new Output({
format: new HlsOutputFormat({
segmentFormats: HLS_OUTPUT_FORMATS_DEFAULT,
}),
target: () => new NullTarget(),
rootPath: '',
});
output.addVideoTrack(videoSource());
output.addAudioTrack(audioSource());
output.addAudioTrack(audioSource());
output.addAudioTrack(audioSource());
await output.start();
const muxer = output._muxer as HlsMuxer;
const decl = muxer.playlistDeclarations;
expect(decl).toHaveLength(4);
expect(decl[0]!.playlist.tracks).toHaveLength(1);
expect(decl[0]!.playlist.tracks[0]!.type).toBe('audio');
expect(decl[0]!.groupId).toBe('audio-1');
expect(decl[0]!.noUri).toBe(false);
expect(decl[1]!.playlist.tracks).toHaveLength(1);
expect(decl[1]!.playlist.tracks[0]!.type).toBe('audio');
expect(decl[0]!.groupId).toBe('audio-1');
expect(decl[1]!.noUri).toBe(false);
expect(decl[2]!.playlist.tracks).toHaveLength(1);
expect(decl[2]!.playlist.tracks[0]!.type).toBe('audio');
expect(decl[0]!.groupId).toBe('audio-1');
expect(decl[2]!.noUri).toBe(false);
expect(decl[3]!.playlist.tracks).toHaveLength(1);
expect(decl[3]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[3]!.groupId).toBeNull();
expect(decl[3]!.references).toEqual(decl.slice(0, 3));
});
test('Playlist assignment, multiple video and one audio', async () => {
const output = new Output({
format: new HlsOutputFormat({
segmentFormats: HLS_OUTPUT_FORMATS_DEFAULT,
}),
target: () => new NullTarget(),
rootPath: '',
});
output.addVideoTrack(videoSource());
output.addVideoTrack(videoSource());
output.addVideoTrack(videoSource());
output.addAudioTrack(audioSource());
await output.start();
const muxer = output._muxer as HlsMuxer;
const decl = muxer.playlistDeclarations;
expect(decl).toHaveLength(4);
expect(decl[0]!.playlist.tracks).toHaveLength(1);
expect(decl[0]!.playlist.tracks[0]!.type).toBe('audio');
expect(decl[0]!.groupId).toBe('audio-1');
expect(decl[0]!.noUri).toBe(false);
expect(decl[1]!.playlist.tracks).toHaveLength(1);
expect(decl[1]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[1]!.groupId).toBeNull();
expect(decl[1]!.references).toEqual([decl[0]!]);
expect(decl[2]!.playlist.tracks).toHaveLength(1);
expect(decl[2]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[2]!.groupId).toBeNull();
expect(decl[2]!.references).toEqual([decl[0]!]);
expect(decl[3]!.playlist.tracks).toHaveLength(1);
expect(decl[3]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[3]!.groupId).toBeNull();
expect(decl[3]!.references).toEqual([decl[0]!]);
});
test('Playlist assignment, multiple video and audio', async () => {
const output = new Output({
format: new HlsOutputFormat({
segmentFormats: HLS_OUTPUT_FORMATS_DEFAULT,
}),
target: () => new NullTarget(),
rootPath: '',
});
output.addVideoTrack(videoSource());
output.addVideoTrack(videoSource());
output.addVideoTrack(videoSource());
output.addAudioTrack(audioSource());
output.addAudioTrack(audioSource());
output.addAudioTrack(audioSource());
await output.start();
const muxer = output._muxer as HlsMuxer;
const decl = muxer.playlistDeclarations;
expect(decl).toHaveLength(6);
expect(decl[0]!.playlist.tracks).toHaveLength(1);
expect(decl[0]!.playlist.tracks[0]!.type).toBe('audio');
expect(decl[0]!.groupId).toBe('audio-1');
expect(decl[0]!.noUri).toBe(false);
expect(decl[1]!.playlist.tracks).toHaveLength(1);
expect(decl[1]!.playlist.tracks[0]!.type).toBe('audio');
expect(decl[1]!.groupId).toBe('audio-1');
expect(decl[1]!.noUri).toBe(false);
expect(decl[2]!.playlist.tracks).toHaveLength(1);
expect(decl[2]!.playlist.tracks[0]!.type).toBe('audio');
expect(decl[2]!.groupId).toBe('audio-1');
expect(decl[2]!.noUri).toBe(false);
expect(decl[3]!.playlist.tracks).toHaveLength(1);
expect(decl[3]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[3]!.groupId).toBeNull();
expect(decl[3]!.references).toEqual(decl.slice(0, 3));
expect(decl[4]!.playlist.tracks).toHaveLength(1);
expect(decl[4]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[4]!.groupId).toBeNull();
expect(decl[4]!.references).toEqual(decl.slice(0, 3));
expect(decl[5]!.playlist.tracks).toHaveLength(1);
expect(decl[5]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[5]!.groupId).toBeNull();
expect(decl[5]!.references).toEqual(decl.slice(0, 3));
});
test('Playlist assignment, video and audio in different groups', async () => {
const output = new Output({
format: new HlsOutputFormat({
segmentFormats: HLS_OUTPUT_FORMATS_DEFAULT,
}),
target: () => new NullTarget(),
rootPath: '',
});
const a = new OutputTrackGroup();
const b = new OutputTrackGroup();
output.addVideoTrack(videoSource(), { group: a });
output.addAudioTrack(audioSource(), { group: b });
await output.start();
const muxer = output._muxer as HlsMuxer;
const decl = muxer.playlistDeclarations;
expect(decl).toHaveLength(2);
expect(decl[0]!.playlist.tracks).toHaveLength(1);
expect(decl[0]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[0]!.groupId).toBeNull();
expect(decl[0]!.references).toHaveLength(0);
expect(decl[1]!.playlist.tracks).toHaveLength(1);
expect(decl[1]!.playlist.tracks[0]!.type).toBe('audio');
expect(decl[1]!.groupId).toBeNull();
expect(decl[1]!.references).toHaveLength(0);
});
test('Playlist assignment, multiple video and audio in pairs', async () => {
const output = new Output({
format: new HlsOutputFormat({
segmentFormats: HLS_OUTPUT_FORMATS_DEFAULT,
}),
target: () => new NullTarget(),
rootPath: '',
});
const a = new OutputTrackGroup();
const b = new OutputTrackGroup();
const c = new OutputTrackGroup();
output.addVideoTrack(videoSource(), { group: a });
output.addVideoTrack(videoSource(), { group: b });
output.addVideoTrack(videoSource(), { group: c });
output.addAudioTrack(audioSource(), { group: a });
output.addAudioTrack(audioSource(), { group: b });
output.addAudioTrack(audioSource(), { group: c });
await output.start();
const muxer = output._muxer as HlsMuxer;
const decl = muxer.playlistDeclarations;
expect(decl).toHaveLength(3);
expect(decl[0]!.playlist.tracks).toHaveLength(2);
expect(decl[0]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[0]!.playlist.tracks[1]!.type).toBe('audio');
expect(decl[0]!.groupId).toBeNull();
expect(decl[0]!.references).toHaveLength(0);
expect(decl[1]!.playlist.tracks).toHaveLength(2);
expect(decl[1]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[1]!.playlist.tracks[1]!.type).toBe('audio');
expect(decl[1]!.groupId).toBeNull();
expect(decl[1]!.references).toHaveLength(0);
expect(decl[2]!.playlist.tracks).toHaveLength(2);
expect(decl[2]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[2]!.playlist.tracks[1]!.type).toBe('audio');
expect(decl[2]!.groupId).toBeNull();
expect(decl[2]!.references).toHaveLength(0);
});
test('Playlist assignment, multiple video and audio with some unpaired', async () => {
const output = new Output({
format: new HlsOutputFormat({
segmentFormats: HLS_OUTPUT_FORMATS_DEFAULT,
}),
target: () => new NullTarget(),
rootPath: '',
});
const a = new OutputTrackGroup();
const b = new OutputTrackGroup();
const c = new OutputTrackGroup();
output.addVideoTrack(videoSource(), { group: a });
output.addVideoTrack(videoSource(), { group: a });
output.addVideoTrack(videoSource(), { group: b });
output.addAudioTrack(audioSource(), { group: a });
output.addAudioTrack(audioSource(), { group: a });
output.addAudioTrack(audioSource(), { group: c });
await output.start();
const muxer = output._muxer as HlsMuxer;
const decl = muxer.playlistDeclarations;
expect(decl).toHaveLength(6);
expect(decl[0]!.playlist.tracks).toHaveLength(1);
expect(decl[0]!.playlist.tracks[0]!.type).toBe('audio');
expect(decl[0]!.groupId).toBe('audio-1');
expect(decl[0]!.noUri).toBe(false);
expect(decl[1]!.playlist.tracks).toHaveLength(1);
expect(decl[1]!.playlist.tracks[0]!.type).toBe('audio');
expect(decl[1]!.groupId).toBe('audio-1');
expect(decl[1]!.noUri).toBe(false);
expect(decl[2]!.playlist.tracks).toHaveLength(1);
expect(decl[2]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[2]!.groupId).toBeNull();
expect(decl[2]!.references).toEqual(decl.slice(0, 2));
expect(decl[3]!.playlist.tracks).toHaveLength(1);
expect(decl[3]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[3]!.groupId).toBeNull();
expect(decl[3]!.references).toEqual(decl.slice(0, 2));
expect(decl[4]!.playlist.tracks).toHaveLength(1);
expect(decl[4]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[4]!.groupId).toBeNull();
expect(decl[4]!.references).toHaveLength(0);
expect(decl[5]!.playlist.tracks).toHaveLength(1);
expect(decl[5]!.playlist.tracks[0]!.type).toBe('audio');
expect(decl[5]!.groupId).toBeNull();
expect(decl[5]!.references).toHaveLength(0);
});
test('Playlist assignment, multiple video and audio with multiple groups', async () => {
const output = new Output({
format: new HlsOutputFormat({
segmentFormats: HLS_OUTPUT_FORMATS_DEFAULT,
}),
target: () => new NullTarget(),
rootPath: '',
});
const a = new OutputTrackGroup();
const b = new OutputTrackGroup();
output.addVideoTrack(videoSource(), { group: a });
output.addVideoTrack(videoSource(), { group: a });
output.addVideoTrack(videoSource(), { group: b });
output.addVideoTrack(videoSource(), { group: b });
output.addAudioTrack(audioSource(), { group: a });
output.addAudioTrack(audioSource(), { group: a });
output.addAudioTrack(audioSource(), { group: b });
output.addAudioTrack(audioSource(), { group: b });
await output.start();
const muxer = output._muxer as HlsMuxer;
const decl = muxer.playlistDeclarations;
expect(decl).toHaveLength(8);
expect(decl[0]!.playlist.tracks).toHaveLength(1);
expect(decl[0]!.playlist.tracks[0]!.type).toBe('audio');
expect(decl[0]!.groupId).toBe('audio-1');
expect(decl[0]!.noUri).toBe(false);
expect(decl[1]!.playlist.tracks).toHaveLength(1);
expect(decl[1]!.playlist.tracks[0]!.type).toBe('audio');
expect(decl[1]!.groupId).toBe('audio-1');
expect(decl[1]!.noUri).toBe(false);
expect(decl[2]!.playlist.tracks).toHaveLength(1);
expect(decl[2]!.playlist.tracks[0]!.type).toBe('audio');
expect(decl[2]!.groupId).toBe('audio-2');
expect(decl[2]!.noUri).toBe(false);
expect(decl[3]!.playlist.tracks).toHaveLength(1);
expect(decl[3]!.playlist.tracks[0]!.type).toBe('audio');
expect(decl[3]!.groupId).toBe('audio-2');
expect(decl[3]!.noUri).toBe(false);
expect(decl[4]!.playlist.tracks).toHaveLength(1);
expect(decl[4]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[4]!.groupId).toBeNull();
expect(decl[4]!.references).toEqual(decl.slice(0, 2));
expect(decl[5]!.playlist.tracks).toHaveLength(1);
expect(decl[5]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[5]!.groupId).toBeNull();
expect(decl[5]!.references).toEqual(decl.slice(0, 2));
expect(decl[6]!.playlist.tracks).toHaveLength(1);
expect(decl[6]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[6]!.groupId).toBeNull();
expect(decl[6]!.references).toEqual(decl.slice(2, 4));
expect(decl[7]!.playlist.tracks).toHaveLength(1);
expect(decl[7]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[7]!.groupId).toBeNull();
expect(decl[7]!.references).toEqual(decl.slice(2, 4));
});
test('Playlist assignment, video with multiple audio codecs', async () => {
const output = new Output({
format: new HlsOutputFormat({
segmentFormats: HLS_OUTPUT_FORMATS_DEFAULT,
}),
target: () => new NullTarget(),
rootPath: '',
});
output.addVideoTrack(videoSource());
output.addAudioTrack(audioSource('aac'));
output.addAudioTrack(audioSource('ac3'));
await output.start();
const muxer = output._muxer as HlsMuxer;
const decl = muxer.playlistDeclarations;
expect(decl).toHaveLength(4);
expect(decl[0]!.playlist.tracks).toHaveLength(1);
expect(decl[0]!.playlist.tracks[0]!.type).toBe('audio');
expect(decl[0]!.playlist.tracks[0]!.source._codec).toBe('aac');
expect(decl[0]!.groupId).toBe('audio-1');
expect(decl[0]!.noUri).toBe(false);
expect(decl[1]!.playlist.tracks).toHaveLength(1);
expect(decl[1]!.playlist.tracks[0]!.type).toBe('audio');
expect(decl[1]!.playlist.tracks[0]!.source._codec).toBe('ac3');
expect(decl[1]!.groupId).toBe('audio-2');
expect(decl[1]!.noUri).toBe(false);
expect(decl[2]!.playlist.tracks).toHaveLength(1);
expect(decl[2]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[2]!.groupId).toBeNull();
expect(decl[2]!.references).toEqual([decl[0]!]);
expect(decl[3]!.playlist.tracks).toHaveLength(1);
expect(decl[3]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[3]!.groupId).toBeNull();
expect(decl[3]!.references).toEqual([decl[1]!]);
expect(decl[2]!.playlist).toBe(decl[3]!.playlist);
});
test('Playlist assignment, audio with multiple video codecs', async () => {
const output = new Output({
format: new HlsOutputFormat({
segmentFormats: HLS_OUTPUT_FORMATS_DEFAULT,
}),
target: () => new NullTarget(),
rootPath: '',
});
output.addVideoTrack(videoSource('avc'));
output.addVideoTrack(videoSource('hevc'));
output.addAudioTrack(audioSource());
await output.start();
const muxer = output._muxer as HlsMuxer;
const decl = muxer.playlistDeclarations;
expect(decl).toHaveLength(3);
expect(decl[0]!.playlist.tracks).toHaveLength(1);
expect(decl[0]!.playlist.tracks[0]!.type).toBe('audio');
expect(decl[0]!.groupId).toBe('audio-1');
expect(decl[0]!.noUri).toBe(false);
expect(decl[1]!.playlist.tracks).toHaveLength(1);
expect(decl[1]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[1]!.playlist.tracks[0]!.source._codec).toBe('avc');
expect(decl[1]!.groupId).toBeNull();
expect(decl[1]!.references).toEqual([decl[0]!]);
expect(decl[2]!.playlist.tracks).toHaveLength(1);
expect(decl[2]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[2]!.playlist.tracks[0]!.source._codec).toBe('hevc');
expect(decl[2]!.groupId).toBeNull();
expect(decl[2]!.references).toEqual([decl[0]!]);
});
test('Playlist assignment, multiple video with conflicting audio interests', async () => {
const output = new Output({
format: new HlsOutputFormat({
segmentFormats: HLS_OUTPUT_FORMATS_DEFAULT,
}),
target: () => new NullTarget(),
rootPath: '',
});
const a = new OutputTrackGroup();
const b = new OutputTrackGroup();
output.addVideoTrack(videoSource(), { group: a });
output.addVideoTrack(videoSource(), { group: b });
output.addAudioTrack(audioSource(), { group: [a, b] });
output.addAudioTrack(audioSource(), { group: a });
await output.start();
const muxer = output._muxer as HlsMuxer;
const decl = muxer.playlistDeclarations;
expect(decl).toHaveLength(5);
expect(decl[0]!.playlist.tracks).toHaveLength(1);
expect(decl[0]!.playlist.tracks[0]!.type).toBe('audio');
expect(decl[0]!.groupId).toBe('audio-1');
expect(decl[0]!.noUri).toBe(false);
expect(decl[1]!.playlist.tracks).toHaveLength(1);
expect(decl[1]!.playlist.tracks[0]!.type).toBe('audio');
expect(decl[1]!.groupId).toBe('audio-1');
expect(decl[1]!.noUri).toBe(false);
expect(decl[2]!.playlist.tracks).toHaveLength(1);
expect(decl[2]!.playlist.tracks[0]!.type).toBe('audio');
expect(decl[2]!.playlist).toBe(decl[0]!.playlist);
expect(decl[2]!.groupId).toBe('audio-2');
expect(decl[2]!.noUri).toBe(false);
expect(decl[3]!.playlist.tracks).toHaveLength(1);
expect(decl[3]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[3]!.groupId).toBeNull();
expect(decl[3]!.references).toEqual([decl[0]!, decl[1]!]);
expect(decl[4]!.playlist.tracks).toHaveLength(1);
expect(decl[4]!.playlist.tracks[0]!.type).toBe('video');
expect(decl[4]!.groupId).toBeNull();
expect(decl[4]!.references).toEqual([decl[2]!]);
});
test('Playlist assignment, video paired with video', async () => {
const consoleSpy = vi.spyOn(console, 'warn');
const output = new Output({
format: new HlsOutputFormat({
segmentFormats: HLS_OUTPUT_FORMATS_DEFAULT,
}),
target: () => new NullTarget(),
rootPath: '',
});
const a = new OutputTrackGroup();
const b = new OutputTrackGroup();
a.pairWith(b);
output.addVideoTrack(videoSource(), { group: a });
output.addVideoTrack(videoSource(), { group: b });
await output.start();
const muxer = output._muxer as HlsMuxer;
const decl = muxer.playlistDeclarations;
expect(decl).toHaveLength(2);
expect(decl[0]!.playlist.tracks).toHaveLength(1);
expect(decl[0]!.groupId).toBeNull();
expect(decl[0]!.references).toHaveLength(0);
expect(decl[1]!.playlist.tracks).toHaveLength(1);
expect(decl[1]!.groupId).toBeNull();
expect(decl[1]!.references).toHaveLength(0);
expect(consoleSpy.mock.calls).toHaveLength(1);
expect(consoleSpy.mock.calls[0]![0]).toContain('Illegal pairing');
});
test('Playlist assignment, audio paired with audio', async () => {
const consoleSpy = vi.spyOn(console, 'warn');
const output = new Output({
format: new HlsOutputFormat({
segmentFormats: HLS_OUTPUT_FORMATS_DEFAULT,
}),
target: () => new NullTarget(),
rootPath: '',
});
const a = new OutputTrackGroup();
const b = new OutputTrackGroup();
a.pairWith(b);
output.addAudioTrack(audioSource(), { group: a });
output.addAudioTrack(audioSource(), { group: b });
await output.start();
const muxer = output._muxer as HlsMuxer;
const decl = muxer.playlistDeclarations;
expect(decl).toHaveLength(2);
expect(decl[0]!.playlist.tracks).toHaveLength(1);
expect(decl[0]!.groupId).toBeNull();
expect(decl[0]!.references).toHaveLength(0);
expect(decl[1]!.playlist.tracks).toHaveLength(1);
expect(decl[1]!.groupId).toBeNull();
expect(decl[1]!.references).toHaveLength(0);
expect(consoleSpy.mock.calls).toHaveLength(1);
expect(consoleSpy.mock.calls[0]![0]).toContain('Illegal pairing');
});