Files
mediabunny/test/node/matroska-muxer.test.ts

139 lines
4.2 KiB
TypeScript

import { expect, test } from 'vitest';
import path from 'node:path';
import { Input } from '../../src/input.js';
import { BufferSource, FilePathSource } from '../../src/source.js';
import { ADTS, ALL_FORMATS } from '../../src/input-format.js';
import { EncodedPacketSink } from '../../src/media-sink.js';
import { Output } from '../../src/output.js';
import { BufferTarget } from '../../src/target.js';
import { MkvOutputFormat } from '../../src/output-format.js';
import { Conversion } from '../../src/conversion.js';
import { assert } from '../../src/misc.js';
import { EncodedVideoPacketSource } from '../../src/media-source.js';
import { EncodedPacket } from '../../src/packet.js';
const __dirname = new URL('.', import.meta.url).pathname;
test('Matroska muxer internally converts ADTS to AAC', async () => {
using input = new Input({
source: new FilePathSource(path.join(__dirname, '../public/sample3.aac')),
formats: ALL_FORMATS,
});
expect(await input.getFormat()).toBe(ADTS);
const inputTrack = await input.getPrimaryAudioTrack();
assert(inputTrack);
const inputDecoderConfig = await inputTrack.getDecoderConfig();
expect(inputDecoderConfig!.description).toBeUndefined(); // ADTS input has no description
const output = new Output({
format: new MkvOutputFormat(),
target: new BufferTarget(),
});
const conversion = await Conversion.init({ input, output, showWarnings: false });
await conversion.execute();
using outputAsInput = new Input({
source: new BufferSource(output.target.buffer!),
formats: ALL_FORMATS,
});
const outputTrack = await outputAsInput.getPrimaryAudioTrack();
assert(outputTrack);
expect(await outputTrack.getCodec()).toBe('aac');
expect(await outputTrack.getSampleRate()).toBe(await inputTrack.getSampleRate());
expect(await outputTrack.getNumberOfChannels()).toBe(await inputTrack.getNumberOfChannels());
const outputDecoderConfig = await outputTrack.getDecoderConfig();
expect(outputDecoderConfig!.description).toBeDefined();
const outputSink = new EncodedPacketSink(outputTrack);
let count = 0;
for await (const packet of outputSink.packets()) {
// Packets should NOT be ADTS frames (should not start with 0xFFF sync word)
const isAdts = packet.data[0] === 0xff && (packet.data[1]! & 0xf0) === 0xf0;
expect(isAdts).toBe(false);
count++;
}
expect(count).toBe(4557);
});
test('Negative start timestamps', async () => {
await testNegativeTimestampRoundTrip(
Array.from({ length: 50 }, (_, index) => (index - 10) / 10),
0.1,
10,
);
});
test('Wholly negative timestamps', async () => {
await testNegativeTimestampRoundTrip([-1, -0.9, -0.8, -0.7, -0.6], 0.1, 10);
});
const testNegativeTimestampRoundTrip = async (timestamps: number[], duration: number, frameRate: number) => {
const output = new Output({
format: new MkvOutputFormat(),
target: new BufferTarget(),
});
const source = new EncodedVideoPacketSource('vp8');
output.addVideoTrack(source, { frameRate });
await output.start();
const meta = { decoderConfig: { codec: 'vp8', codedWidth: 1280, codedHeight: 720 } };
const inputPackets = timestamps.map((timestamp, index) => new EncodedPacket(
new Uint8Array(1024).fill(index),
'key',
timestamp,
duration,
));
for (let i = 0; i < inputPackets.length; i++) {
await source.add(inputPackets[i]!, i === 0 ? meta : undefined);
}
await output.finalize();
using input = new Input({
source: new BufferSource(output.target.buffer!),
formats: ALL_FORMATS,
});
const track = await input.getPrimaryVideoTrack();
assert(track);
const sink = new EncodedPacketSink(track);
const outputPackets: EncodedPacket[] = [];
for await (const packet of sink.packets()) {
outputPackets.push(packet);
}
expect(outputPackets.map(packet => ({
timestamp: packet.timestamp,
duration: packet.duration,
}))).toEqual(inputPackets.map(packet => ({
timestamp: packet.timestamp,
duration: packet.duration,
})));
for (const inputPacket of inputPackets) {
const outputPacket = await sink.getPacket(inputPacket.timestamp);
assert(outputPacket);
expect({
timestamp: outputPacket.timestamp,
duration: outputPacket.duration,
}).toEqual({
timestamp: inputPacket.timestamp,
duration: inputPacket.duration,
});
}
};