mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-10-03 13:53:53 +02:00
Make ADTS demuxer spit out ADTS packets instead of AAC, make ADTS muxer accept both AAC and ADTS input
This commit is contained in:
+52
-33
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import { AacAudioSpecificConfig, parseAacAudioSpecificConfig, validateAudioChunkMetadata } from '../codec';
|
||||
import { assert, Bitstream, toUint8Array } from '../misc';
|
||||
import { Bitstream, toUint8Array } from '../misc';
|
||||
import { Muxer } from '../muxer';
|
||||
import { Output, OutputAudioTrack } from '../output';
|
||||
import { AdtsOutputFormat } from '../output-format';
|
||||
@@ -20,6 +20,7 @@ export class AdtsMuxer extends Muxer {
|
||||
private header = new Uint8Array(7);
|
||||
private headerBitstream = new Bitstream(this.header);
|
||||
private audioSpecificConfig: AacAudioSpecificConfig | null = null;
|
||||
private inputIsAdts: boolean | null = null;
|
||||
|
||||
constructor(output: Output, format: AdtsOutputFormat) {
|
||||
super(output);
|
||||
@@ -52,49 +53,67 @@ export class AdtsMuxer extends Muxer {
|
||||
try {
|
||||
this.validateAndNormalizeTimestamp(track, packet.timestamp, packet.type === 'key');
|
||||
|
||||
if (!this.audioSpecificConfig) {
|
||||
if (this.inputIsAdts === null) {
|
||||
validateAudioChunkMetadata(meta);
|
||||
|
||||
const description = meta?.decoderConfig?.description;
|
||||
assert(description);
|
||||
|
||||
this.audioSpecificConfig = parseAacAudioSpecificConfig(toUint8Array(description));
|
||||
// From the WebCodecs Codec Registry:
|
||||
// "If description is present, it is assumed to a AudioSpecificConfig as defined in [iso14496-3] section
|
||||
// 1.6.2.1, Table 1.15, and the bitstream is assumed to be in aac.
|
||||
// If the description is not present, the bitstream is assumed to be in adts format."
|
||||
this.inputIsAdts = !description;
|
||||
|
||||
const { objectType, frequencyIndex, channelConfiguration } = this.audioSpecificConfig;
|
||||
const profile = objectType - 1;
|
||||
if (!this.inputIsAdts) {
|
||||
this.audioSpecificConfig = parseAacAudioSpecificConfig(toUint8Array(description!));
|
||||
|
||||
this.headerBitstream.writeBits(12, 0b1111_11111111); // Syncword
|
||||
this.headerBitstream.writeBits(1, 0); // MPEG Version
|
||||
this.headerBitstream.writeBits(2, 0); // Layer
|
||||
this.headerBitstream.writeBits(1, 1); // Protection absence
|
||||
this.headerBitstream.writeBits(2, profile); // Profile
|
||||
this.headerBitstream.writeBits(4, frequencyIndex); // MPEG-4 Sampling Frequency Index
|
||||
this.headerBitstream.writeBits(1, 0); // Private bit
|
||||
this.headerBitstream.writeBits(3, channelConfiguration); // MPEG-4 Channel Configuration
|
||||
this.headerBitstream.writeBits(1, 0); // Originality
|
||||
this.headerBitstream.writeBits(1, 0); // Home
|
||||
this.headerBitstream.writeBits(1, 0); // Copyright ID bit
|
||||
this.headerBitstream.writeBits(1, 0); // Copyright ID start
|
||||
this.headerBitstream.skipBits(13); // Frame length
|
||||
this.headerBitstream.writeBits(11, 0x7ff); // Buffer fullness
|
||||
this.headerBitstream.writeBits(2, 0); // Number of AAC frames minus 1
|
||||
// Omit CRC check
|
||||
const { objectType, frequencyIndex, channelConfiguration } = this.audioSpecificConfig;
|
||||
const profile = objectType - 1;
|
||||
|
||||
this.headerBitstream.writeBits(12, 0b1111_11111111); // Syncword
|
||||
this.headerBitstream.writeBits(1, 0); // MPEG Version
|
||||
this.headerBitstream.writeBits(2, 0); // Layer
|
||||
this.headerBitstream.writeBits(1, 1); // Protection absence
|
||||
this.headerBitstream.writeBits(2, profile); // Profile
|
||||
this.headerBitstream.writeBits(4, frequencyIndex); // MPEG-4 Sampling Frequency Index
|
||||
this.headerBitstream.writeBits(1, 0); // Private bit
|
||||
this.headerBitstream.writeBits(3, channelConfiguration); // MPEG-4 Channel Configuration
|
||||
this.headerBitstream.writeBits(1, 0); // Originality
|
||||
this.headerBitstream.writeBits(1, 0); // Home
|
||||
this.headerBitstream.writeBits(1, 0); // Copyright ID bit
|
||||
this.headerBitstream.writeBits(1, 0); // Copyright ID start
|
||||
this.headerBitstream.skipBits(13); // Frame length
|
||||
this.headerBitstream.writeBits(11, 0x7ff); // Buffer fullness
|
||||
this.headerBitstream.writeBits(2, 0); // Number of AAC frames minus 1
|
||||
// Omit CRC check
|
||||
}
|
||||
}
|
||||
|
||||
const frameLength = packet.data.byteLength + this.header.byteLength;
|
||||
this.headerBitstream.pos = 30;
|
||||
this.headerBitstream.writeBits(13, frameLength);
|
||||
if (this.inputIsAdts) {
|
||||
// Packets are already ADTS frames, write them directly
|
||||
const startPos = this.writer.getPos();
|
||||
this.writer.write(packet.data);
|
||||
|
||||
const startPos = this.writer.getPos();
|
||||
this.writer.write(this.header);
|
||||
this.writer.write(packet.data);
|
||||
if (this.format._options.onFrame) {
|
||||
this.format._options.onFrame(packet.data, startPos);
|
||||
}
|
||||
} else {
|
||||
// Packets are raw AAC, prepend ADTS header
|
||||
const frameLength = packet.data.byteLength + this.header.byteLength;
|
||||
this.headerBitstream.pos = 30;
|
||||
this.headerBitstream.writeBits(13, frameLength);
|
||||
|
||||
if (this.format._options.onFrame) {
|
||||
const frameBytes = new Uint8Array(frameLength);
|
||||
frameBytes.set(this.header, 0);
|
||||
frameBytes.set(packet.data, this.header.byteLength);
|
||||
const startPos = this.writer.getPos();
|
||||
this.writer.write(this.header);
|
||||
this.writer.write(packet.data);
|
||||
|
||||
this.format._options.onFrame(frameBytes, startPos);
|
||||
if (this.format._options.onFrame) {
|
||||
const frameBytes = new Uint8Array(frameLength);
|
||||
frameBytes.set(this.header, 0);
|
||||
frameBytes.set(packet.data, this.header.byteLength);
|
||||
|
||||
this.format._options.onFrame(frameBytes, startPos);
|
||||
}
|
||||
}
|
||||
|
||||
await this.writer.flush();
|
||||
|
||||
Reference in New Issue
Block a user