mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-10-03 05:43:50 +02:00
Add MP3 support to ISOBMFF demuxer
This commit is contained in:
+5
-1
@@ -13,7 +13,7 @@ import { SubtitleMetadata } from './subtitles';
|
|||||||
/** @public */
|
/** @public */
|
||||||
export const VIDEO_CODECS = ['avc', 'hevc', 'vp8', 'vp9', 'av1'] as const;
|
export const VIDEO_CODECS = ['avc', 'hevc', 'vp8', 'vp9', 'av1'] as const;
|
||||||
/** @public */
|
/** @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 */
|
/** @public */
|
||||||
export const SUBTITLE_CODECS = ['webvtt'] as const; // TODO add the rest
|
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
|
// Default to standard AAC-LC for higher sample rates
|
||||||
return 'mp4a.40.2'; // AAC-LC
|
return 'mp4a.40.2'; // AAC-LC
|
||||||
|
} else if (codec === 'mp3') {
|
||||||
|
return 'mp3'; // Easy, this one
|
||||||
} else if (codec === 'opus') {
|
} else if (codec === 'opus') {
|
||||||
return 'opus'; // Easy, this one
|
return 'opus'; // Easy, this one
|
||||||
} else if (codec === 'vorbis') {
|
} else if (codec === 'vorbis') {
|
||||||
@@ -367,6 +369,8 @@ export const extractAudioCodecString = (codec: AudioCodec, description: Uint8Arr
|
|||||||
if (codec === 'aac') {
|
if (codec === 'aac') {
|
||||||
const audioSpecificConfig = parseAacAudioSpecificConfig(description);
|
const audioSpecificConfig = parseAacAudioSpecificConfig(description);
|
||||||
return `mp4a.40.${audioSpecificConfig.objectType}`;
|
return `mp4a.40.${audioSpecificConfig.objectType}`;
|
||||||
|
} else if (codec === 'mp3') {
|
||||||
|
return 'mp3';
|
||||||
} else if (codec === 'opus') {
|
} else if (codec === 'opus') {
|
||||||
return 'opus';
|
return 'opus';
|
||||||
} else if (codec === 'vorbis') {
|
} else if (codec === 'vorbis') {
|
||||||
|
|||||||
@@ -613,7 +613,7 @@ export class IsobmffDemuxer extends Demuxer {
|
|||||||
this.readContiguousBoxes(startPos + sampleBoxInfo.totalSize - this.isobmffReader.pos);
|
this.readContiguousBoxes(startPos + sampleBoxInfo.totalSize - this.isobmffReader.pos);
|
||||||
} else {
|
} else {
|
||||||
if (sampleBoxInfo.name === 'mp4a') {
|
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') {
|
} else if (sampleBoxInfo.name.toLowerCase() === 'opus') {
|
||||||
track.info.codec = 'opus';
|
track.info.codec = 'opus';
|
||||||
} else {
|
} else {
|
||||||
@@ -811,12 +811,12 @@ export class IsobmffDemuxer extends Demuxer {
|
|||||||
|
|
||||||
case 'esds': {
|
case 'esds': {
|
||||||
const track = this.currentTrack;
|
const track = this.currentTrack;
|
||||||
assert(track && track.info);
|
assert(track && track.info?.type === 'audio');
|
||||||
|
|
||||||
this.isobmffReader.pos += 4; // Version + flags
|
this.isobmffReader.pos += 4; // Version + flags
|
||||||
|
|
||||||
const tag = this.isobmffReader.readU8();
|
const tag = this.isobmffReader.readU8();
|
||||||
assert(tag === 0x03);
|
assert(tag === 0x03); // ES Descriptor
|
||||||
|
|
||||||
this.isobmffReader.readIsomVariableInteger(); // Length
|
this.isobmffReader.readIsomVariableInteger(); // Length
|
||||||
|
|
||||||
@@ -839,24 +839,38 @@ export class IsobmffDemuxer extends Demuxer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const decoderConfigTag = this.isobmffReader.readU8();
|
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();
|
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;
|
this.isobmffReader.pos += 1 + 3 + 4 + 4;
|
||||||
|
|
||||||
const decoderSpecificInfoTag = this.isobmffReader.readU8();
|
if (decoderConfigDescriptorLength > this.isobmffReader.pos - payloadStart) {
|
||||||
assert(decoderSpecificInfoTag === 0x05);
|
// 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(
|
const decoderSpecificInfoLength = this.isobmffReader.readIsomVariableInteger();
|
||||||
this.isobmffReader.pos,
|
|
||||||
this.isobmffReader.pos + decoderSpecificInfoLength,
|
track.info.codecDescription = this.isobmffReader.readRange(
|
||||||
);
|
this.isobmffReader.pos,
|
||||||
|
this.isobmffReader.pos + decoderSpecificInfoLength,
|
||||||
|
);
|
||||||
|
}
|
||||||
}; break;
|
}; break;
|
||||||
|
|
||||||
case 'stts': {
|
case 'stts': {
|
||||||
@@ -2003,17 +2017,16 @@ const getSampleInfo = (sampleTable: SampleTable, sampleIndex: number): SampleInf
|
|||||||
+ Math.floor((sampleIndex - chunkEntry.startSampleIndex) / chunkEntry.samplesPerChunk);
|
+ Math.floor((sampleIndex - chunkEntry.startSampleIndex) / chunkEntry.samplesPerChunk);
|
||||||
const chunkOffset = sampleTable.chunkOffsets[chunkIndex]!;
|
const chunkOffset = sampleTable.chunkOffsets[chunkIndex]!;
|
||||||
|
|
||||||
|
const startSampleIndexOfChunk = chunkEntry.startSampleIndex
|
||||||
|
+ (chunkIndex - chunkEntry.startChunkIndex) * chunkEntry.samplesPerChunk;
|
||||||
let chunkSize = 0;
|
let chunkSize = 0;
|
||||||
|
|
||||||
let sampleOffset = chunkOffset;
|
let sampleOffset = chunkOffset;
|
||||||
|
|
||||||
if (sampleTable.sampleSizes.length === 1) {
|
if (sampleTable.sampleSizes.length === 1) {
|
||||||
sampleOffset += sampleSize * (sampleIndex - chunkEntry.startSampleIndex);
|
sampleOffset += sampleSize * (sampleIndex - startSampleIndexOfChunk);
|
||||||
chunkSize += sampleSize * chunkEntry.samplesPerChunk;
|
chunkSize += sampleSize * chunkEntry.samplesPerChunk;
|
||||||
} else {
|
} else {
|
||||||
const startSampleIndex = chunkEntry.startSampleIndex
|
for (let i = startSampleIndexOfChunk; i < startSampleIndexOfChunk + chunkEntry.samplesPerChunk; i++) {
|
||||||
+ (chunkIndex - chunkEntry.startChunkIndex) * chunkEntry.samplesPerChunk;
|
|
||||||
|
|
||||||
for (let i = startSampleIndex; i < startSampleIndex + chunkEntry.samplesPerChunk; i++) {
|
|
||||||
const sampleSize = sampleTable.sampleSizes[i]!;
|
const sampleSize = sampleTable.sampleSizes[i]!;
|
||||||
|
|
||||||
if (i < sampleIndex) {
|
if (i < sampleIndex) {
|
||||||
|
|||||||
Reference in New Issue
Block a user