diff --git a/dev/demux.html b/dev/demux.html
index a64b60c..1fb6443 100644
--- a/dev/demux.html
+++ b/dev/demux.html
@@ -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();
diff --git a/src/isobmff/isobmff-boxes.ts b/src/isobmff/isobmff-boxes.ts
index a5e9604..4b81a8e 100644
--- a/src/isobmff/isobmff-boxes.ts
+++ b/src/isobmff/isobmff-boxes.ts
@@ -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
diff --git a/src/isobmff/isobmff-demuxer.ts b/src/isobmff/isobmff-demuxer.ts
index e96ba09..4b8adef 100644
--- a/src/isobmff/isobmff-demuxer.ts
+++ b/src/isobmff/isobmff-demuxer.ts
@@ -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
diff --git a/src/isobmff/isobmff-reader.ts b/src/isobmff/isobmff-reader.ts
index b040b12..2a3930e 100644
--- a/src/isobmff/isobmff-reader.ts
+++ b/src/isobmff/isobmff-reader.ts
@@ -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;
diff --git a/src/media-drain.ts b/src/media-drain.ts
index 12e9116..6d90e0b 100644
--- a/src/media-drain.ts
+++ b/src/media-drain.ts
@@ -882,7 +882,9 @@ class PcmAudioDecoderWrapper extends DecoderWrapper this.onMedia(audioData));
}
async flush() {