Add MP3 support to ISOBMFF demuxer

This commit is contained in:
Vanilagy
2024-12-30 23:47:42 +01:00
parent 8f861f3cfe
commit d203d75bdd
2 changed files with 37 additions and 20 deletions
+5 -1
View File
@@ -13,7 +13,7 @@ import { SubtitleMetadata } from './subtitles';
/** @public */
export const VIDEO_CODECS = ['avc', 'hevc', 'vp8', 'vp9', 'av1'] as const;
/** @public */
export const AUDIO_CODECS = ['aac', 'opus'] as const; // TODO add the rest
export const AUDIO_CODECS = ['aac', 'mp3', 'opus'] as const; // TODO add the rest
/** @public */
export const SUBTITLE_CODECS = ['webvtt'] as const; // TODO add the rest
@@ -353,6 +353,8 @@ export const buildAudioCodecString = (codec: AudioCodec, numberOfChannels: numbe
// Default to standard AAC-LC for higher sample rates
return 'mp4a.40.2'; // AAC-LC
} else if (codec === 'mp3') {
return 'mp3'; // Easy, this one
} else if (codec === 'opus') {
return 'opus'; // Easy, this one
} else if (codec === 'vorbis') {
@@ -367,6 +369,8 @@ export const extractAudioCodecString = (codec: AudioCodec, description: Uint8Arr
if (codec === 'aac') {
const audioSpecificConfig = parseAacAudioSpecificConfig(description);
return `mp4a.40.${audioSpecificConfig.objectType}`;
} else if (codec === 'mp3') {
return 'mp3';
} else if (codec === 'opus') {
return 'opus';
} else if (codec === 'vorbis') {
+32 -19
View File
@@ -613,7 +613,7 @@ export class IsobmffDemuxer extends Demuxer {
this.readContiguousBoxes(startPos + sampleBoxInfo.totalSize - this.isobmffReader.pos);
} else {
if (sampleBoxInfo.name === 'mp4a') {
track.info.codec = 'aac';
// We don't know the codec yet, need to read the esds box
} else if (sampleBoxInfo.name.toLowerCase() === 'opus') {
track.info.codec = 'opus';
} else {
@@ -811,12 +811,12 @@ export class IsobmffDemuxer extends Demuxer {
case 'esds': {
const track = this.currentTrack;
assert(track && track.info);
assert(track && track.info?.type === 'audio');
this.isobmffReader.pos += 4; // Version + flags
const tag = this.isobmffReader.readU8();
assert(tag === 0x03);
assert(tag === 0x03); // ES Descriptor
this.isobmffReader.readIsomVariableInteger(); // Length
@@ -839,24 +839,38 @@ export class IsobmffDemuxer extends Demuxer {
}
const decoderConfigTag = this.isobmffReader.readU8();
assert(decoderConfigTag === 0x04);
assert(decoderConfigTag === 0x04); // DecoderConfigDescriptor
this.isobmffReader.readIsomVariableInteger(); // Length
const decoderConfigDescriptorLength = this.isobmffReader.readIsomVariableInteger(); // Length
const payloadStart = this.isobmffReader.pos;
const objectTypeIndication = this.isobmffReader.readU8();
assert(objectTypeIndication === 0x40); // Assert it's MPEG-4 audio
if (objectTypeIndication === 0x40) {
track.info.codec = 'aac';
} else if (objectTypeIndication === 0x69 || objectTypeIndication === 0x6b) {
track.info.codec = 'mp3';
} else {
console.warn(
`Unsupported audio codec (objectTypeIndication ${objectTypeIndication}) - discarding track.`,
);
}
this.isobmffReader.pos += 1 + 3 + 4 + 4;
const decoderSpecificInfoTag = this.isobmffReader.readU8();
assert(decoderSpecificInfoTag === 0x05);
if (decoderConfigDescriptorLength > this.isobmffReader.pos - payloadStart) {
// There's a DecoderSpecificInfo at the end, let's read it
const decoderSpecificInfoLength = this.isobmffReader.readIsomVariableInteger();
const decoderSpecificInfoTag = this.isobmffReader.readU8();
assert(decoderSpecificInfoTag === 0x05); // DecoderSpecificInfo
track.info.codecDescription = this.isobmffReader.readRange(
this.isobmffReader.pos,
this.isobmffReader.pos + decoderSpecificInfoLength,
);
const decoderSpecificInfoLength = this.isobmffReader.readIsomVariableInteger();
track.info.codecDescription = this.isobmffReader.readRange(
this.isobmffReader.pos,
this.isobmffReader.pos + decoderSpecificInfoLength,
);
}
}; break;
case 'stts': {
@@ -2003,17 +2017,16 @@ const getSampleInfo = (sampleTable: SampleTable, sampleIndex: number): SampleInf
+ Math.floor((sampleIndex - chunkEntry.startSampleIndex) / chunkEntry.samplesPerChunk);
const chunkOffset = sampleTable.chunkOffsets[chunkIndex]!;
const startSampleIndexOfChunk = chunkEntry.startSampleIndex
+ (chunkIndex - chunkEntry.startChunkIndex) * chunkEntry.samplesPerChunk;
let chunkSize = 0;
let sampleOffset = chunkOffset;
if (sampleTable.sampleSizes.length === 1) {
sampleOffset += sampleSize * (sampleIndex - chunkEntry.startSampleIndex);
sampleOffset += sampleSize * (sampleIndex - startSampleIndexOfChunk);
chunkSize += sampleSize * chunkEntry.samplesPerChunk;
} else {
const startSampleIndex = chunkEntry.startSampleIndex
+ (chunkIndex - chunkEntry.startChunkIndex) * chunkEntry.samplesPerChunk;
for (let i = startSampleIndex; i < startSampleIndex + chunkEntry.samplesPerChunk; i++) {
for (let i = startSampleIndexOfChunk; i < startSampleIndexOfChunk + chunkEntry.samplesPerChunk; i++) {
const sampleSize = sampleTable.sampleSizes[i]!;
if (i < sampleIndex) {