mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-09-27 10:53:50 +02:00
111 lines
3.5 KiB
TypeScript
111 lines
3.5 KiB
TypeScript
/*!
|
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
import { AacAudioSpecificConfig, parseAacAudioSpecificConfig, validateAudioChunkMetadata } from '../codec';
|
|
import { assert, Bitstream, toUint8Array } from '../misc';
|
|
import { Muxer } from '../muxer';
|
|
import { Output, OutputAudioTrack } from '../output';
|
|
import { AdtsOutputFormat } from '../output-format';
|
|
import { EncodedPacket } from '../packet';
|
|
import { Writer } from '../writer';
|
|
|
|
export class AdtsMuxer extends Muxer {
|
|
private format: AdtsOutputFormat;
|
|
private writer: Writer;
|
|
private header = new Uint8Array(7);
|
|
private headerBitstream = new Bitstream(this.header);
|
|
|
|
private audioSpecificConfig: AacAudioSpecificConfig | null = null;
|
|
|
|
constructor(output: Output, format: AdtsOutputFormat) {
|
|
super(output);
|
|
|
|
this.format = format;
|
|
this.writer = output._writer;
|
|
}
|
|
|
|
async start() {
|
|
// Nothing needed here
|
|
}
|
|
|
|
async getMimeType() {
|
|
return 'audio/aac';
|
|
}
|
|
|
|
async addEncodedVideoPacket() {
|
|
throw new Error('ADTS does not support video.');
|
|
}
|
|
|
|
async addEncodedAudioPacket(
|
|
track: OutputAudioTrack,
|
|
packet: EncodedPacket,
|
|
meta?: EncodedAudioChunkMetadata,
|
|
) {
|
|
// https://wiki.multimedia.cx/index.php/ADTS (last visited: 2025/08/17)
|
|
|
|
const release = await this.mutex.acquire();
|
|
|
|
try {
|
|
if (!this.audioSpecificConfig) {
|
|
validateAudioChunkMetadata(meta);
|
|
|
|
const description = meta?.decoderConfig?.description;
|
|
assert(description);
|
|
|
|
this.audioSpecificConfig = parseAacAudioSpecificConfig(toUint8Array(description));
|
|
|
|
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);
|
|
|
|
const startPos = this.writer.getPos();
|
|
this.writer.write(this.header);
|
|
this.writer.write(packet.data);
|
|
|
|
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();
|
|
} finally {
|
|
release();
|
|
}
|
|
}
|
|
|
|
async addSubtitleCue() {
|
|
throw new Error('ADTS does not support subtitles.');
|
|
}
|
|
|
|
async finalize() {}
|
|
}
|