mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-09-27 10:53:50 +02:00
Add Opus support to ISOBMFF demuxer
This commit is contained in:
+24
-1
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user