mediabunny
Version:
Pure TypeScript media toolkit for reading, writing, and converting media files, directly in the browser.
91 lines (90 loc) • 3.79 kB
JavaScript
/*!
* Copyright (c) 2026-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 { buildAdtsHeaderTemplate, parseAacAudioSpecificConfig, writeAdtsFrameLength } from '../../shared/aac-misc.js';
import { validateAudioChunkMetadata } from '../codec.js';
import { Id3V2Writer } from '../id3.js';
import { metadataTagsAreEmpty } from '../metadata.js';
import { assert, toUint8Array } from '../misc.js';
import { Muxer } from '../muxer.js';
export class AdtsMuxer extends Muxer {
constructor(output, format) {
super(output);
this.header = null;
this.headerBitstream = null;
this.inputIsAdts = null;
this.format = format;
}
async start() {
const release = await this.mutex.acquire();
this.writer = await this.output._getRootWriter(true);
if (!metadataTagsAreEmpty(this.output._metadataTags)) {
const id3Writer = new Id3V2Writer(this.writer);
id3Writer.writeId3V2Tag(this.output._metadataTags);
}
release();
}
async getMimeType() {
return 'audio/aac';
}
async addEncodedVideoPacket() {
throw new Error('ADTS does not support video.');
}
async addEncodedAudioPacket(track, packet, meta) {
const release = await this.mutex.acquire();
try {
this.validateTimestamp(track, packet.timestamp, packet.type === 'key');
// First packet - determine input format from metadata
if (this.inputIsAdts === null) {
validateAudioChunkMetadata(meta);
const description = meta?.decoderConfig?.description;
// Follows from the Mediabunny Codec Registry:
this.inputIsAdts = !description;
if (!this.inputIsAdts) {
const config = parseAacAudioSpecificConfig(toUint8Array(description));
const template = buildAdtsHeaderTemplate(config);
this.header = template.header;
this.headerBitstream = template.bitstream;
}
}
if (this.inputIsAdts) {
// Packets are already ADTS frames, write them directly
const startPos = this.writer.getPos();
this.writer.write(packet.data);
if (this.format._options.onFrame) {
this.format._options.onFrame(packet.data, startPos);
}
}
else {
assert(this.header);
// Packets are raw AAC, we gotta turn it into ADTS
const frameLength = packet.data.byteLength + this.header.byteLength;
writeAdtsFrameLength(this.headerBitstream, 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() {
const release = await this.mutex.acquire(); // Required so that finalize() can't resolve before other calls
release();
}
}