Add Opus support to ISOBMFF demuxer

This commit is contained in:
Vanilagy
2025-01-02 14:19:17 +01:00
parent 8a8fbc264e
commit 7c5e31a93a
5 changed files with 87 additions and 6 deletions
+24 -1
View File
@@ -4,6 +4,27 @@
const fileInput = document.createElement('input');
fileInput.type = 'file';
document.body.append(fileInput);
const encoder = new AudioEncoder({
output: console.log,
error: console.error
});
encoder.configure({
codec: 'vorbis',
numberOfChannels: 2,
sampleRate: 48000,
bitrate: 128000
});
let audioData = new AudioData({
format: 'f32',
numberOfChannels: 2,
sampleRate: 48000,
timestamp: 0,
data: new Float32Array(2 * 48000),
numberOfFrames: 48000
});
encoder.encode(audioData);
fileInput.addEventListener('change', async () => {
const file = fileInput.files[0];
@@ -16,7 +37,9 @@
const audioTrack = await input.getPrimaryAudioTrack();
const drain = new Metamuxer.EncodedAudioChunkDrain(audioTrack);
console.log(await drain.getFirstChunk());
for await (const chunk of drain.chunks()) {
console.log(chunk)
}
/*
const videoTrack = await input.getPrimaryVideoTrack();
+1
View File
@@ -751,6 +751,7 @@ export const dOps = (trackData: IsobmffAudioTrackData) => {
gain = view.getInt16(14, true);
}
// https://www.opus-codec.org/docs/opus_in_isobmff.html
return box('dOps', [
u8(0), // Version
u8(trackData.info.numberOfChannels), // OutputChannelCount
+52 -4
View File
@@ -1017,6 +1017,56 @@ export class IsobmffDemuxer extends Demuxer {
}
}; break;
case 'dOps': { // Used for Opus audio
const track = this.currentTrack;
assert(track && track.info?.type === 'audio');
// TODO TEMP
/*
const bytes = this.isobmffReader.readBytes(boxInfo.contentSize);
const description2 = new Uint8Array(8 + bytes.byteLength);
const view2 = new DataView(description2.buffer);
view2.setUint32(0, 0x4f707573, false); // 'Opus'
view2.setUint32(4, 0x646f7073, false); // 'dOps'
description2.set(bytes, 8);
track.info.codecDescription = description2;
break;
*/
this.isobmffReader.pos += 1; // Version
// https://www.opus-codec.org/docs/opus_in_isobmff.html
const outputChannelCount = this.isobmffReader.readU8();
const preSkip = this.isobmffReader.readU16();
const inputSampleRate = this.isobmffReader.readU32();
const outputGain = this.isobmffReader.readI16();
const channelMappingFamily = this.isobmffReader.readU8();
let channelMappingTable: Uint8Array;
if (channelMappingFamily !== 0) {
channelMappingTable = this.isobmffReader.readBytes(2 + outputChannelCount);
} else {
channelMappingTable = new Uint8Array(0);
}
// https://datatracker.ietf.org/doc/html/draft-ietf-codec-oggopus-06
const description = new Uint8Array(8 + 1 + 1 + 2 + 4 + 2 + 1 + channelMappingTable.byteLength);
const view = new DataView(description.buffer);
view.setUint32(0, 0x4f707573, false); // 'Opus'
view.setUint32(4, 0x48656164, false); // 'Head'
view.setUint8(8, 1); // Version
view.setUint8(9, outputChannelCount);
view.setUint16(10, preSkip, true);
view.setUint32(12, inputSampleRate, true);
view.setInt16(16, outputGain, true);
view.setUint8(18, channelMappingFamily);
description.set(channelMappingTable, 19);
track.info.codecDescription = description;
track.info.numberOfChannels = outputChannelCount;
track.info.sampleRate = inputSampleRate;
}; break;
case 'dfLa': { // Used for FLAC audio
const track = this.currentTrack;
assert(track && track.info?.type === 'audio');
@@ -1063,10 +1113,8 @@ export class IsobmffDemuxer extends Demuxer {
const bytes = this.isobmffReader.readBytes(endPos - startPos);
const description = new Uint8Array(4 + bytes.byteLength);
description[0] = 0x66; // f
description[1] = 0x4C; // L
description[2] = 0x61; // a
description[3] = 0x43; // C
const view = new DataView(description.buffer);
view.setUint32(0, 0x664c6143, false); // 'fLaC'
description.set(bytes, 4);
// Set the codec description to be 'fLaC' + all metadata blocks
+7
View File
@@ -26,6 +26,13 @@ export class IsobmffReader {
return view.getUint16(offset, false);
}
readI16() {
const { view, offset } = this.reader.getViewAndOffset(this.pos, this.pos + 2);
this.pos += 2;
return view.getInt16(offset, false);
}
readU24() {
const { view, offset } = this.reader.getViewAndOffset(this.pos, this.pos + 3);
this.pos += 3;
+3 -1
View File
@@ -882,7 +882,9 @@ class PcmAudioDecoderWrapper extends DecoderWrapper<EncodedAudioChunk, AudioData
numberOfFrames,
timestamp: chunk.timestamp,
});
this.onMedia(audioData);
// Since all other decoders are async, we'll make this one behave async as well
queueMicrotask(() => this.onMedia(audioData));
}
async flush() {