mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-09-28 03:13:51 +02:00
Migrate WAVE demuxer to new packet system
This commit is contained in:
@@ -885,6 +885,10 @@ export class ResultValue<T> {
|
||||
this.value = value;
|
||||
this.pending = false;
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.pending = true;
|
||||
}
|
||||
}
|
||||
|
||||
export class AsyncMutex2 {
|
||||
|
||||
+50
-40
@@ -12,7 +12,7 @@ import { Input } from '../input';
|
||||
import { InputAudioTrack, InputAudioTrackBacking } from '../input-track';
|
||||
import { PacketRetrievalOptions } from '../media-sink';
|
||||
import { DEFAULT_TRACK_DISPOSITION, MetadataTags } from '../metadata';
|
||||
import { assert, UNDETERMINED_LANGUAGE } from '../misc';
|
||||
import { assert, ResultValue, UNDETERMINED_LANGUAGE, Yo } from '../misc';
|
||||
import { EncodedPacket, PLACEHOLDER_DATA } from '../packet';
|
||||
import { readAscii, readBytes, Reader, readU16, readU32, readU64 } from '../reader';
|
||||
import { parseId3V2Tag, readId3V2Header } from '../id3';
|
||||
@@ -327,15 +327,6 @@ export class WaveDemuxer extends Demuxer {
|
||||
return 'audio/wav';
|
||||
}
|
||||
|
||||
async computeDuration() {
|
||||
await this.readMetadata();
|
||||
|
||||
const track = this.tracks[0];
|
||||
assert(track);
|
||||
|
||||
return track.computeDuration();
|
||||
}
|
||||
|
||||
async getTracks() {
|
||||
await this.readMetadata();
|
||||
return this.tracks;
|
||||
@@ -379,11 +370,6 @@ class WaveAudioTrackBacking implements InputAudioTrackBacking {
|
||||
};
|
||||
}
|
||||
|
||||
async computeDuration() {
|
||||
const lastPacket = await this.getPacket(Infinity, { metadataOnly: true });
|
||||
return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0);
|
||||
}
|
||||
|
||||
getNumberOfChannels() {
|
||||
assert(this.demuxer.audioInfo);
|
||||
return this.demuxer.audioInfo.numberOfChannels;
|
||||
@@ -413,18 +399,15 @@ class WaveAudioTrackBacking implements InputAudioTrackBacking {
|
||||
};
|
||||
}
|
||||
|
||||
async getFirstTimestamp() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async getPacketAtIndex(
|
||||
res: ResultValue<EncodedPacket | null>,
|
||||
packetIndex: number,
|
||||
options: PacketRetrievalOptions,
|
||||
): Promise<EncodedPacket | null> {
|
||||
): Promise<Yo> {
|
||||
assert(this.demuxer.audioInfo);
|
||||
const startOffset = packetIndex * PACKET_SIZE_IN_FRAMES * this.demuxer.audioInfo.blockSizeInBytes;
|
||||
if (startOffset >= this.demuxer.dataSize) {
|
||||
return null;
|
||||
return res.set(null);
|
||||
}
|
||||
|
||||
const sizeInBytes = Math.min(
|
||||
@@ -441,7 +424,7 @@ class WaveAudioTrackBacking implements InputAudioTrackBacking {
|
||||
if (slice instanceof Promise) slice = await slice;
|
||||
|
||||
if (!slice) {
|
||||
return null;
|
||||
return res.set(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,21 +447,25 @@ class WaveAudioTrackBacking implements InputAudioTrackBacking {
|
||||
timestamp,
|
||||
);
|
||||
|
||||
return new EncodedPacket(
|
||||
return res.set(new EncodedPacket(
|
||||
data,
|
||||
'key',
|
||||
timestamp,
|
||||
duration,
|
||||
packetIndex,
|
||||
sizeInBytes,
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
getFirstPacket(options: PacketRetrievalOptions) {
|
||||
return this.getPacketAtIndex(0, options);
|
||||
getFirstPacket(res: ResultValue<EncodedPacket | null>, options: PacketRetrievalOptions): Promise<Yo> {
|
||||
return this.getPacketAtIndex(res, 0, options);
|
||||
}
|
||||
|
||||
async getPacket(timestamp: number, options: PacketRetrievalOptions) {
|
||||
async getPacket(
|
||||
res: ResultValue<EncodedPacket | null>,
|
||||
timestamp: number,
|
||||
options: PacketRetrievalOptions,
|
||||
): Promise<Yo> {
|
||||
assert(this.demuxer.audioInfo);
|
||||
|
||||
const packetIndex = Math.floor(Math.min(
|
||||
@@ -486,22 +473,33 @@ class WaveAudioTrackBacking implements InputAudioTrackBacking {
|
||||
(this.demuxer.dataSize - 1) / (PACKET_SIZE_IN_FRAMES * this.demuxer.audioInfo.blockSizeInBytes),
|
||||
));
|
||||
|
||||
const packet = await this.getPacketAtIndex(packetIndex, options);
|
||||
if (packet) {
|
||||
return packet;
|
||||
const result = new ResultValue<EncodedPacket | null>();
|
||||
let promise = this.getPacketAtIndex(result, packetIndex, options);
|
||||
if (result.pending) await promise;
|
||||
|
||||
if (result.value) {
|
||||
return res.set(result.value);
|
||||
}
|
||||
|
||||
if (packetIndex === 0) {
|
||||
return null; // Empty data chunk
|
||||
return res.set(null); // Empty data chunk
|
||||
}
|
||||
|
||||
assert(this.demuxer.reader.fileSize === null);
|
||||
|
||||
// The file is shorter than we thought, meaning the packet we were looking for doesn't exist. So, let's find
|
||||
// the last packet by doing a sequential scan, instead.
|
||||
let currentPacket = await this.getPacketAtIndex(this.demuxer.lastKnownPacketIndex, options);
|
||||
result.reset();
|
||||
promise = this.getPacketAtIndex(result, this.demuxer.lastKnownPacketIndex, options);
|
||||
if (result.pending) await promise;
|
||||
|
||||
let currentPacket = result.value;
|
||||
while (currentPacket) {
|
||||
const nextPacket = await this.getNextPacket(currentPacket, options);
|
||||
result.reset();
|
||||
promise = this.getNextPacket(result, currentPacket, options);
|
||||
if (result.pending) await promise;
|
||||
|
||||
const nextPacket = result.value;
|
||||
if (!nextPacket) {
|
||||
break;
|
||||
}
|
||||
@@ -509,21 +507,33 @@ class WaveAudioTrackBacking implements InputAudioTrackBacking {
|
||||
currentPacket = nextPacket;
|
||||
}
|
||||
|
||||
return currentPacket;
|
||||
return res.set(currentPacket);
|
||||
}
|
||||
|
||||
getNextPacket(packet: EncodedPacket, options: PacketRetrievalOptions) {
|
||||
getNextPacket(
|
||||
res: ResultValue<EncodedPacket | null>,
|
||||
packet: EncodedPacket,
|
||||
options: PacketRetrievalOptions,
|
||||
): Promise<Yo> {
|
||||
assert(this.demuxer.audioInfo);
|
||||
const packetIndex = Math.round(packet.timestamp * this.demuxer.audioInfo.sampleRate / PACKET_SIZE_IN_FRAMES);
|
||||
|
||||
return this.getPacketAtIndex(packetIndex + 1, options);
|
||||
return this.getPacketAtIndex(res, packetIndex + 1, options);
|
||||
}
|
||||
|
||||
getKeyPacket(timestamp: number, options: PacketRetrievalOptions) {
|
||||
return this.getPacket(timestamp, options);
|
||||
getKeyPacket(
|
||||
res: ResultValue<EncodedPacket | null>,
|
||||
timestamp: number,
|
||||
options: PacketRetrievalOptions,
|
||||
): Promise<Yo> {
|
||||
return this.getPacket(res, timestamp, options);
|
||||
}
|
||||
|
||||
getNextKeyPacket(packet: EncodedPacket, options: PacketRetrievalOptions) {
|
||||
return this.getNextPacket(packet, options);
|
||||
getNextKeyPacket(
|
||||
res: ResultValue<EncodedPacket | null>,
|
||||
packet: EncodedPacket,
|
||||
options: PacketRetrievalOptions,
|
||||
): Promise<Yo> {
|
||||
return this.getNextPacket(res, packet, options);
|
||||
}
|
||||
}
|
||||
|
||||
+51
-19
@@ -3,7 +3,7 @@ import { Input } from '../../src/input.js';
|
||||
import { BufferSource, FilePathSource } from '../../src/source.js';
|
||||
import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
import { ALL_FORMATS, MP3, MP4, QTFF, WEBM } from '../../src/input-format.js';
|
||||
import { ALL_FORMATS, MP3, MP4, QTFF, WAVE, WEBM } from '../../src/input-format.js';
|
||||
import { PacketReader } from '../../src/cursors.js';
|
||||
import { InputAudioTrack, InputTrack } from '../../src/input-track.js';
|
||||
import { assert } from '../../src/misc.js';
|
||||
@@ -47,6 +47,24 @@ const testBasicPacketReading = async (track: InputTrack) => {
|
||||
expect(afterLast).toBe(null);
|
||||
};
|
||||
|
||||
const testSyncPacketReading = (track: InputTrack) => {
|
||||
const reader = new PacketReader(track);
|
||||
|
||||
let current = reader.readFirst() as EncodedPacket | null;
|
||||
expect(current).toBeInstanceOf(EncodedPacket);
|
||||
|
||||
let count = 0;
|
||||
while (current) {
|
||||
current = reader.readNext(current) as EncodedPacket | null;
|
||||
expect(current instanceof EncodedPacket || current === null).toBe(true);
|
||||
count++;
|
||||
}
|
||||
|
||||
expect(count).toBeGreaterThan(0);
|
||||
|
||||
return count;
|
||||
};
|
||||
|
||||
test('Regular ISOBMFF demuxing', async () => {
|
||||
using input = new Input({
|
||||
source: new FilePathSource(path.join(__dirname, '../public/trim-buck-bunny.mov')),
|
||||
@@ -177,24 +195,6 @@ test('MP3 demuxing', async () => {
|
||||
expect(duration).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
const testSyncPacketReading = (track: InputTrack) => {
|
||||
const reader = new PacketReader(track);
|
||||
|
||||
let current = reader.readFirst() as EncodedPacket | null;
|
||||
expect(current).toBeInstanceOf(EncodedPacket);
|
||||
|
||||
let count = 0;
|
||||
while (current) {
|
||||
current = reader.readNext(current) as EncodedPacket | null;
|
||||
expect(current instanceof EncodedPacket || current === null).toBe(true);
|
||||
count++;
|
||||
}
|
||||
|
||||
expect(count).toBeGreaterThan(0);
|
||||
|
||||
return count;
|
||||
};
|
||||
|
||||
test('MP3 sync reading', async () => {
|
||||
using input = new Input({
|
||||
source: new BufferSource(fs.readFileSync(path.join(__dirname, '../public/AudacityTest1.mp3'))),
|
||||
@@ -207,3 +207,35 @@ test('MP3 sync reading', async () => {
|
||||
const count = testSyncPacketReading(audioTrack);
|
||||
expect(count).toBe(475);
|
||||
});
|
||||
|
||||
test('WAVE demuxing', async () => {
|
||||
using input = new Input({
|
||||
source: new FilePathSource(path.join(__dirname, '../public/glitch-hop-is-dead.wav')),
|
||||
formats: ALL_FORMATS,
|
||||
});
|
||||
|
||||
expect(await input.getFormat()).toBe(WAVE);
|
||||
|
||||
const tracks = await input.getTracks();
|
||||
expect(tracks).toHaveLength(1);
|
||||
|
||||
const audioTrack = await input.getPrimaryAudioTrack();
|
||||
assert(audioTrack);
|
||||
|
||||
await testBasicPacketReading(audioTrack);
|
||||
|
||||
expect(await input.computeDuration()).toBeCloseTo(9.637188208616779);
|
||||
});
|
||||
|
||||
test('WAVE sync reading', async () => {
|
||||
using input = new Input({
|
||||
source: new BufferSource(fs.readFileSync(path.join(__dirname, '../public/glitch-hop-is-dead.wav'))),
|
||||
formats: ALL_FORMATS,
|
||||
});
|
||||
|
||||
const audioTrack = await input.getPrimaryAudioTrack();
|
||||
assert(audioTrack);
|
||||
|
||||
const count = testSyncPacketReading(audioTrack);
|
||||
expect(count).toBe(208);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user