mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-10-01 04:43:51 +02:00
Add @mediabunny/aac-encoder extension package
This commit is contained in:
@@ -6,7 +6,8 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { aacChannelMap, aacFrequencyTable, AudioCodec } from '../codec';
|
||||
import { aacChannelMap, aacFrequencyTable } from '../../shared/aac-misc';
|
||||
import { AudioCodec } from '../codec';
|
||||
import { Demuxer } from '../demuxer';
|
||||
import {
|
||||
ID3_V2_HEADER_SIZE,
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/*!
|
||||
* 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 { AacAudioSpecificConfig } from '../codec';
|
||||
import { Bitstream } from '../misc';
|
||||
|
||||
export type AdtsHeaderTemplate = {
|
||||
header: Uint8Array;
|
||||
bitstream: Bitstream;
|
||||
};
|
||||
|
||||
export const buildAdtsHeaderTemplate = (config: AacAudioSpecificConfig): AdtsHeaderTemplate => {
|
||||
const header = new Uint8Array(7);
|
||||
const bitstream = new Bitstream(header);
|
||||
|
||||
const { objectType, frequencyIndex, channelConfiguration } = config;
|
||||
const profile = objectType - 1;
|
||||
|
||||
bitstream.writeBits(12, 0b1111_11111111); // Syncword
|
||||
bitstream.writeBits(1, 0); // MPEG Version
|
||||
bitstream.writeBits(2, 0); // Layer
|
||||
bitstream.writeBits(1, 1); // Protection absence
|
||||
bitstream.writeBits(2, profile); // Profile
|
||||
bitstream.writeBits(4, frequencyIndex); // MPEG-4 Sampling Frequency Index
|
||||
bitstream.writeBits(1, 0); // Private bit
|
||||
bitstream.writeBits(3, channelConfiguration); // MPEG-4 Channel Configuration
|
||||
bitstream.writeBits(1, 0); // Originality
|
||||
bitstream.writeBits(1, 0); // Home
|
||||
bitstream.writeBits(1, 0); // Copyright ID bit
|
||||
bitstream.writeBits(1, 0); // Copyright ID start
|
||||
bitstream.skipBits(13); // Frame length (to be filled per packet)
|
||||
bitstream.writeBits(11, 0x7ff); // Buffer fullness
|
||||
bitstream.writeBits(2, 0); // Number of AAC frames minus 1
|
||||
// Omit CRC check
|
||||
|
||||
return { header, bitstream };
|
||||
};
|
||||
|
||||
export const writeAdtsFrameLength = (bitstream: Bitstream, frameLength: number) => {
|
||||
bitstream.pos = 30;
|
||||
bitstream.writeBits(13, frameLength);
|
||||
};
|
||||
@@ -6,16 +6,17 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { parseAacAudioSpecificConfig, validateAudioChunkMetadata } from '../codec';
|
||||
import { buildAdtsHeaderTemplate, parseAacAudioSpecificConfig, writeAdtsFrameLength } from '../../shared/aac-misc';
|
||||
import { Bitstream } from '../../shared/bitstream';
|
||||
import { validateAudioChunkMetadata } from '../codec';
|
||||
import { Id3V2Writer } from '../id3';
|
||||
import { metadataTagsAreEmpty } from '../metadata';
|
||||
import { assert, Bitstream, toUint8Array } from '../misc';
|
||||
import { assert, 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';
|
||||
import { buildAdtsHeaderTemplate, writeAdtsFrameLength } from './adts-misc';
|
||||
|
||||
export class AdtsMuxer extends Muxer {
|
||||
private format: AdtsOutputFormat;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Bitstream } from '../misc';
|
||||
import { Bitstream } from '../../shared/bitstream';
|
||||
import { FileSlice, readBytes } from '../reader';
|
||||
|
||||
export const MIN_ADTS_FRAME_HEADER_SIZE = 7;
|
||||
|
||||
+1
-1
@@ -11,7 +11,6 @@ import {
|
||||
assert,
|
||||
assertNever,
|
||||
base64ToBytes,
|
||||
Bitstream,
|
||||
bytesToBase64,
|
||||
keyValueIterator,
|
||||
getUint24,
|
||||
@@ -30,6 +29,7 @@ import {
|
||||
import { PacketType } from './packet';
|
||||
import { MetadataTags } from './metadata';
|
||||
import { AC3_SAMPLE_RATES, EAC3_REDUCED_SAMPLE_RATES } from '../shared/ac3-misc';
|
||||
import { Bitstream } from '../shared/bitstream';
|
||||
|
||||
// References for AVC/HEVC code:
|
||||
// ISO 14496-15
|
||||
|
||||
+1
-101
@@ -6,6 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { parseAacAudioSpecificConfig } from '../shared/aac-misc';
|
||||
import {
|
||||
Av1CodecInfo,
|
||||
AvcDecoderConfigurationRecord,
|
||||
@@ -13,7 +14,6 @@ import {
|
||||
Vp9CodecInfo,
|
||||
} from './codec-data';
|
||||
import {
|
||||
Bitstream,
|
||||
COLOR_PRIMARIES_MAP,
|
||||
MATRIX_COEFFICIENTS_MAP,
|
||||
TRANSFER_CHARACTERISTICS_MAP,
|
||||
@@ -590,106 +590,6 @@ export const extractAudioCodecString = (trackInfo: {
|
||||
throw new TypeError(`Unhandled codec '${codec}'.`);
|
||||
};
|
||||
|
||||
export type AacAudioSpecificConfig = {
|
||||
objectType: number;
|
||||
frequencyIndex: number;
|
||||
sampleRate: number | null;
|
||||
channelConfiguration: number;
|
||||
numberOfChannels: number | null;
|
||||
};
|
||||
|
||||
export const aacFrequencyTable = [
|
||||
96000, 88200, 64000, 48000, 44100, 32000,
|
||||
24000, 22050, 16000, 12000, 11025, 8000, 7350,
|
||||
];
|
||||
|
||||
export const aacChannelMap = [-1, 1, 2, 3, 4, 5, 6, 8];
|
||||
|
||||
export const parseAacAudioSpecificConfig = (bytes: Uint8Array | null): AacAudioSpecificConfig => {
|
||||
if (!bytes || bytes.byteLength < 2) {
|
||||
throw new TypeError('AAC description must be at least 2 bytes long.');
|
||||
}
|
||||
|
||||
const bitstream = new Bitstream(bytes);
|
||||
|
||||
let objectType = bitstream.readBits(5);
|
||||
if (objectType === 31) {
|
||||
objectType = 32 + bitstream.readBits(6);
|
||||
}
|
||||
|
||||
const frequencyIndex = bitstream.readBits(4);
|
||||
let sampleRate: number | null = null;
|
||||
if (frequencyIndex === 15) {
|
||||
sampleRate = bitstream.readBits(24);
|
||||
} else {
|
||||
if (frequencyIndex < aacFrequencyTable.length) {
|
||||
sampleRate = aacFrequencyTable[frequencyIndex]!;
|
||||
}
|
||||
}
|
||||
|
||||
const channelConfiguration = bitstream.readBits(4);
|
||||
let numberOfChannels: number | null = null;
|
||||
if (channelConfiguration >= 1 && channelConfiguration <= 7) {
|
||||
numberOfChannels = aacChannelMap[channelConfiguration]!;
|
||||
}
|
||||
|
||||
return {
|
||||
objectType,
|
||||
frequencyIndex,
|
||||
sampleRate,
|
||||
channelConfiguration,
|
||||
numberOfChannels,
|
||||
};
|
||||
};
|
||||
|
||||
export const buildAacAudioSpecificConfig = (config: {
|
||||
objectType: number;
|
||||
sampleRate: number;
|
||||
numberOfChannels: number;
|
||||
}) => {
|
||||
let frequencyIndex = aacFrequencyTable.indexOf(config.sampleRate);
|
||||
let customSampleRate: number | null = null;
|
||||
|
||||
if (frequencyIndex === -1) {
|
||||
frequencyIndex = 15;
|
||||
customSampleRate = config.sampleRate;
|
||||
}
|
||||
|
||||
const channelConfiguration = aacChannelMap.indexOf(config.numberOfChannels);
|
||||
if (channelConfiguration === -1) {
|
||||
throw new TypeError(`Unsupported number of channels: ${config.numberOfChannels}`);
|
||||
}
|
||||
|
||||
let bitCount = 5 + 4 + 4;
|
||||
if (config.objectType >= 32) {
|
||||
bitCount += 6;
|
||||
}
|
||||
if (frequencyIndex === 15) {
|
||||
bitCount += 24;
|
||||
}
|
||||
|
||||
const byteCount = Math.ceil(bitCount / 8);
|
||||
const bytes = new Uint8Array(byteCount);
|
||||
const bitstream = new Bitstream(bytes);
|
||||
|
||||
if (config.objectType < 32) {
|
||||
bitstream.writeBits(5, config.objectType);
|
||||
} else {
|
||||
bitstream.writeBits(5, 31);
|
||||
bitstream.writeBits(6, config.objectType - 32);
|
||||
}
|
||||
|
||||
bitstream.writeBits(4, frequencyIndex);
|
||||
|
||||
if (frequencyIndex === 15) {
|
||||
bitstream.writeBits(24, customSampleRate!);
|
||||
}
|
||||
|
||||
bitstream.writeBits(4, channelConfiguration);
|
||||
|
||||
return bytes;
|
||||
};
|
||||
|
||||
export const OPUS_SAMPLE_RATE = 48_000;
|
||||
|
||||
const PCM_CODEC_REGEX = /^pcm-([usf])(\d+)+(be)?$/;
|
||||
|
||||
@@ -774,6 +774,12 @@ export class Conversion {
|
||||
);
|
||||
}
|
||||
|
||||
if (codecs.includes('aac')) {
|
||||
elements.push(
|
||||
'\nThe @mediabunny/aac-encoder extension package provides support for encoding AAC.',
|
||||
);
|
||||
}
|
||||
|
||||
if (codecs.includes('ac3') || codecs.includes('eac3')) {
|
||||
elements.push(
|
||||
'\nThe @mediabunny/ac3 extension package provides support'
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
assert,
|
||||
AsyncMutex,
|
||||
binarySearchLessOrEqual,
|
||||
Bitstream,
|
||||
textDecoder,
|
||||
UNDETERMINED_LANGUAGE,
|
||||
} from '../misc';
|
||||
@@ -37,6 +36,7 @@ import {
|
||||
readSampleRate,
|
||||
getSampleRateOrUncommon,
|
||||
} from './flac-misc';
|
||||
import { Bitstream } from '../../shared/bitstream';
|
||||
|
||||
type FlacAudioInfo = {
|
||||
numberOfChannels: number;
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { assert, assertNever, Bitstream } from '../misc';
|
||||
import { Bitstream } from '../../shared/bitstream';
|
||||
import { assert, assertNever } from '../misc';
|
||||
import { FileSlice, readBytes, readU16Be, readU8 } from '../reader';
|
||||
|
||||
type BlockSizeOrUncommon = number | 'uncommon-u16' | 'uncommon-u8';
|
||||
|
||||
@@ -10,7 +10,6 @@ import { validateAudioChunkMetadata } from '../codec';
|
||||
import { createVorbisComments, FlacBlockType } from '../codec-data';
|
||||
import {
|
||||
assert,
|
||||
Bitstream,
|
||||
textEncoder,
|
||||
toDataView,
|
||||
toUint8Array,
|
||||
@@ -27,6 +26,7 @@ import {
|
||||
getBlockSizeOrUncommon,
|
||||
readCodedNumber,
|
||||
} from './flac-misc';
|
||||
import { Bitstream } from '../../shared/bitstream';
|
||||
|
||||
const FLAC_HEADER = /* #__PURE__ */ new Uint8Array([0x66, 0x4c, 0x61, 0x43]); // 'fLaC'
|
||||
const STREAMINFO_SIZE = 38;
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
UNDETERMINED_LANGUAGE,
|
||||
assertNever,
|
||||
keyValueIterator,
|
||||
Bitstream,
|
||||
} from '../misc';
|
||||
import {
|
||||
AudioCodec,
|
||||
@@ -46,6 +45,7 @@ import {
|
||||
} from './isobmff-muxer';
|
||||
import { parseAc3SyncFrame, parseEac3SyncFrame, parseOpusIdentificationHeader } from '../codec-data';
|
||||
import { MetadataTags, RichImageData } from '../metadata';
|
||||
import { Bitstream } from '../../shared/bitstream';
|
||||
|
||||
export class IsobmffBoxWriter {
|
||||
private helper = new Uint8Array(8);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { parseAacAudioSpecificConfig } from '../../shared/aac-misc';
|
||||
import {
|
||||
AacCodecInfo,
|
||||
AudioCodec,
|
||||
@@ -13,7 +14,6 @@ import {
|
||||
extractVideoCodecString,
|
||||
MediaCodec,
|
||||
OPUS_SAMPLE_RATE,
|
||||
parseAacAudioSpecificConfig,
|
||||
parsePcmCodec,
|
||||
PCM_AUDIO_CODECS,
|
||||
PcmAudioCodec,
|
||||
@@ -47,7 +47,6 @@ import {
|
||||
assert,
|
||||
binarySearchExact,
|
||||
binarySearchLessOrEqual,
|
||||
Bitstream,
|
||||
COLOR_PRIMARIES_MAP_INVERSE,
|
||||
findLastIndex,
|
||||
isIso639Dash2LanguageCode,
|
||||
@@ -92,6 +91,7 @@ import {
|
||||
} from '../reader';
|
||||
import { DEFAULT_TRACK_DISPOSITION, MetadataTags, RichImageData, TrackDisposition } from '../metadata';
|
||||
import { AC3_SAMPLE_RATES } from '../../shared/ac3-misc';
|
||||
import { Bitstream } from '../../shared/bitstream';
|
||||
|
||||
type InternalTrack = {
|
||||
id: number;
|
||||
|
||||
@@ -13,10 +13,8 @@ import { BufferTargetWriter, Writer } from '../writer';
|
||||
import { assert, computeRationalApproximation, last, promiseWithResolvers, Rational, simplifyRational } from '../misc';
|
||||
import { IsobmffOutputFormatOptions, IsobmffOutputFormat, MovOutputFormat } from '../output-format';
|
||||
import { inlineTimestampRegex, SubtitleConfig, SubtitleCue, SubtitleMetadata } from '../subtitles';
|
||||
import { aacChannelMap, aacFrequencyTable, buildAacAudioSpecificConfig } from '../../shared/aac-misc';
|
||||
import {
|
||||
aacChannelMap,
|
||||
aacFrequencyTable,
|
||||
buildAacAudioSpecificConfig,
|
||||
parsePcmCodec,
|
||||
PCM_AUDIO_CODECS,
|
||||
PcmAudioCodec,
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Bitstream } from '../../shared/bitstream';
|
||||
import {
|
||||
Bitstream,
|
||||
COLOR_PRIMARIES_MAP,
|
||||
MATRIX_COEFFICIENTS_MAP,
|
||||
TRANSFER_CHARACTERISTICS_MAP,
|
||||
@@ -49,10 +49,8 @@ import {
|
||||
inlineTimestampRegex,
|
||||
parseSubtitleTimestamp,
|
||||
} from '../subtitles';
|
||||
import { aacChannelMap, aacFrequencyTable, buildAacAudioSpecificConfig } from '../../shared/aac-misc';
|
||||
import {
|
||||
aacChannelMap,
|
||||
aacFrequencyTable,
|
||||
buildAacAudioSpecificConfig,
|
||||
OPUS_SAMPLE_RATE,
|
||||
PCM_AUDIO_CODECS,
|
||||
PcmAudioCodec,
|
||||
|
||||
+1
-2
@@ -6,11 +6,10 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { buildAacAudioSpecificConfig, parseAacAudioSpecificConfig } from '../shared/aac-misc';
|
||||
import {
|
||||
AUDIO_CODECS,
|
||||
AudioCodec,
|
||||
buildAacAudioSpecificConfig,
|
||||
parseAacAudioSpecificConfig,
|
||||
parsePcmCodec,
|
||||
PCM_AUDIO_CODECS,
|
||||
PcmAudioCodec,
|
||||
|
||||
+2
-79
@@ -6,6 +6,8 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Bitstream } from '../shared/bitstream';
|
||||
|
||||
export function assert(x: unknown): asserts x {
|
||||
if (!x) {
|
||||
throw new Error('Assertion failed.');
|
||||
@@ -39,85 +41,6 @@ export const isU32 = (value: number) => {
|
||||
return value >= 0 && value < 2 ** 32;
|
||||
};
|
||||
|
||||
export class Bitstream {
|
||||
/** Current offset in bits. */
|
||||
pos = 0;
|
||||
|
||||
constructor(public bytes: Uint8Array) {}
|
||||
|
||||
seekToByte(byteOffset: number) {
|
||||
this.pos = 8 * byteOffset;
|
||||
}
|
||||
|
||||
private readBit() {
|
||||
const byteIndex = Math.floor(this.pos / 8);
|
||||
const byte = this.bytes[byteIndex] ?? 0;
|
||||
const bitIndex = 0b111 - (this.pos & 0b111);
|
||||
const bit = (byte & (1 << bitIndex)) >> bitIndex;
|
||||
|
||||
this.pos++;
|
||||
return bit;
|
||||
}
|
||||
|
||||
readBits(n: number) {
|
||||
if (n === 1) {
|
||||
return this.readBit();
|
||||
}
|
||||
|
||||
let result = 0;
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
result <<= 1;
|
||||
result |= this.readBit();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
writeBits(n: number, value: number) {
|
||||
const end = this.pos + n;
|
||||
|
||||
for (let i = this.pos; i < end; i++) {
|
||||
const byteIndex = Math.floor(i / 8);
|
||||
let byte = this.bytes[byteIndex]!;
|
||||
const bitIndex = 0b111 - (i & 0b111);
|
||||
|
||||
byte &= ~(1 << bitIndex);
|
||||
byte |= ((value & (1 << (end - i - 1))) >> (end - i - 1)) << bitIndex;
|
||||
this.bytes[byteIndex] = byte;
|
||||
}
|
||||
|
||||
this.pos = end;
|
||||
};
|
||||
|
||||
readAlignedByte() {
|
||||
// Ensure we're byte-aligned
|
||||
if (this.pos % 8 !== 0) {
|
||||
throw new Error('Bitstream is not byte-aligned.');
|
||||
}
|
||||
|
||||
const byteIndex = this.pos / 8;
|
||||
const byte = this.bytes[byteIndex] ?? 0;
|
||||
|
||||
this.pos += 8;
|
||||
return byte;
|
||||
}
|
||||
|
||||
skipBits(n: number) {
|
||||
this.pos += n;
|
||||
}
|
||||
|
||||
getBitsLeft() {
|
||||
return this.bytes.length * 8 - this.pos;
|
||||
}
|
||||
|
||||
clone() {
|
||||
const clone = new Bitstream(this.bytes);
|
||||
clone.pos = this.pos;
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
|
||||
/** Reads an exponential-Golomb universal code from a Bitstream. */
|
||||
export const readExpGolomb = (bitstream: Bitstream) => {
|
||||
let leadingZeroBits = 0;
|
||||
|
||||
@@ -8,10 +8,9 @@
|
||||
|
||||
import { SAMPLES_PER_AAC_FRAME } from '../adts/adts-demuxer';
|
||||
import { MAX_ADTS_FRAME_HEADER_SIZE, readAdtsFrameHeader } from '../adts/adts-reader';
|
||||
import { aacChannelMap, aacFrequencyTable } from '../../shared/aac-misc';
|
||||
import {
|
||||
aacChannelMap,
|
||||
AacCodecInfo,
|
||||
aacFrequencyTable,
|
||||
AudioCodec,
|
||||
extractAudioCodecString,
|
||||
extractVideoCodecString,
|
||||
@@ -54,7 +53,6 @@ import {
|
||||
assert,
|
||||
binarySearchExact,
|
||||
binarySearchLessOrEqual,
|
||||
Bitstream,
|
||||
COLOR_PRIMARIES_MAP_INVERSE,
|
||||
findLastIndex,
|
||||
floorToMultiple,
|
||||
@@ -71,6 +69,7 @@ import { EncodedPacket, PacketType, PLACEHOLDER_DATA } from '../packet';
|
||||
import { FileSlice, readBytes, Reader, readU16Be, readU32Be, readU8 } from '../reader';
|
||||
import { buildMpegTsMimeType, MpegTsStreamType, TIMESCALE, TS_PACKET_SIZE } from './mpeg-ts-misc';
|
||||
import { AC3_SAMPLE_RATES } from '../../shared/ac3-misc';
|
||||
import { Bitstream } from '../../shared/bitstream';
|
||||
|
||||
// Resources:
|
||||
// ISO/IEC 13818-1
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { parseAacAudioSpecificConfig, validateAudioChunkMetadata, validateVideoChunkMetadata } from '../codec';
|
||||
import { buildAdtsHeaderTemplate, writeAdtsFrameLength } from '../adts/adts-misc';
|
||||
import { buildAdtsHeaderTemplate, parseAacAudioSpecificConfig, writeAdtsFrameLength } from '../../shared/aac-misc';
|
||||
import { validateAudioChunkMetadata, validateVideoChunkMetadata } from '../codec';
|
||||
import {
|
||||
AC3_REGISTRATION_DESCRIPTOR,
|
||||
AvcDecoderConfigurationRecord,
|
||||
@@ -23,7 +23,8 @@ import {
|
||||
iterateNalUnitsInAnnexB,
|
||||
iterateNalUnitsInLengthPrefixed,
|
||||
} from '../codec-data';
|
||||
import { assert, Bitstream, promiseWithResolvers, setUint24, toDataView, toUint8Array } from '../misc';
|
||||
import { Bitstream } from '../../shared/bitstream';
|
||||
import { assert, promiseWithResolvers, setUint24, toDataView, toUint8Array } from '../misc';
|
||||
import { Muxer } from '../muxer';
|
||||
import { Output, OutputAudioTrack, OutputTrack, OutputVideoTrack } from '../output';
|
||||
import { MpegTsOutputFormat } from '../output-format';
|
||||
|
||||
Reference in New Issue
Block a user