Add ulaw and alaw support to ISOBMFF demuxer

This commit is contained in:
Vanilagy
2025-01-01 22:03:12 +01:00
parent fac4e4bf7b
commit 4e0b3a7b3a
2 changed files with 39 additions and 26 deletions
+6
View File
@@ -35,6 +35,8 @@ export const AUDIO_CODECS = [
'aac', 'aac',
'mp3', 'mp3',
'opus', 'opus',
'ulaw',
'alaw',
...PCM_CODECS, ...PCM_CODECS,
] as const; // TODO add the rest ] as const; // TODO add the rest
/** @public */ /** @public */
@@ -398,6 +400,10 @@ export const extractAudioCodecString = (codec: AudioCodec, description: Uint8Arr
return 'opus'; return 'opus';
} else if (codec.startsWith('pcm-')) { } else if (codec.startsWith('pcm-')) {
return codec; return codec;
} else if (codec === 'ulaw') {
return 'ulaw';
} else if (codec === 'alaw') {
return 'alaw';
} else if (codec === 'vorbis') { } else if (codec === 'vorbis') {
return 'vorbis'; return 'vorbis';
} }
+33 -26
View File
@@ -272,11 +272,13 @@ export class IsobmffDemuxer extends Demuxer {
this.traverseBox(); this.traverseBox();
this.currentTrack = null; this.currentTrack = null;
if ( const isPcmCodec = internalTrack.info?.type === 'audio' && (
internalTrack.info?.type === 'audio' internalTrack.info.codec?.startsWith('pcm-')
&& internalTrack.info.codec?.startsWith('pcm-') || internalTrack.info.codec === 'ulaw'
&& sampleTable.sampleCompositionTimeOffsets.length === 0 || internalTrack.info.codec === 'alaw'
) { );
if (isPcmCodec && sampleTable.sampleCompositionTimeOffsets.length === 0) {
// If the audio has PCM samples, the way the samples are defined in the sample table is somewhat // If the audio has PCM samples, the way the samples are defined in the sample table is somewhat
// suboptimal: Each individual audio sample is its own sample, meaning we can have 48000 samples per second. // suboptimal: Each individual audio sample is its own sample, meaning we can have 48000 samples per second.
// Because we treat each sample as its own atomic unit that can be decoded, this would lead to a huge // Because we treat each sample as its own atomic unit that can be decoded, this would lead to a huge
@@ -673,17 +675,18 @@ export class IsobmffDemuxer extends Demuxer {
for (let i = 0; i < entries; i++) { for (let i = 0; i < entries; i++) {
const sampleBoxInfo = this.isobmffReader.readBoxHeader(); const sampleBoxInfo = this.isobmffReader.readBoxHeader();
const lowercaseBoxName = sampleBoxInfo.name.toLowerCase();
if (track.info.type === 'video') { if (track.info.type === 'video') {
if (sampleBoxInfo.name === 'avc1') { if (lowercaseBoxName === 'avc1') {
track.info.codec = 'avc'; track.info.codec = 'avc';
} else if (sampleBoxInfo.name === 'hvc1' || sampleBoxInfo.name === 'hev1') { } else if (lowercaseBoxName === 'hvc1' || lowercaseBoxName === 'hev1') {
track.info.codec = 'hevc'; track.info.codec = 'hevc';
} else if (sampleBoxInfo.name === 'vp08') { } else if (lowercaseBoxName === 'vp08') {
track.info.codec = 'vp8'; track.info.codec = 'vp8';
} else if (sampleBoxInfo.name === 'vp09') { } else if (lowercaseBoxName === 'vp09') {
track.info.codec = 'vp9'; track.info.codec = 'vp9';
} else if (sampleBoxInfo.name === 'av01') { } else if (lowercaseBoxName === 'av01') {
track.info.codec = 'av1'; track.info.codec = 'av1';
} else { } else {
const { name } = sampleBoxInfo; const { name } = sampleBoxInfo;
@@ -700,21 +703,25 @@ 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 (lowercaseBoxName === 'mp4a') {
// We don't know the codec yet (might be AAC, might be MP3), need to read the esds box // We don't know the codec yet (might be AAC, might be MP3), need to read the esds box
} else if (sampleBoxInfo.name.toLowerCase() === 'opus') { } else if (lowercaseBoxName === 'opus') {
track.info.codec = 'opus'; track.info.codec = 'opus';
} else if ( } else if (
sampleBoxInfo.name === 'twos' lowercaseBoxName === 'twos'
|| sampleBoxInfo.name === 'sowt' || lowercaseBoxName === 'sowt'
|| sampleBoxInfo.name === 'raw ' || lowercaseBoxName === 'raw '
|| sampleBoxInfo.name === 'in24' || lowercaseBoxName === 'in24'
|| sampleBoxInfo.name === 'in32' || lowercaseBoxName === 'in32'
|| sampleBoxInfo.name === 'fl32' || lowercaseBoxName === 'fl32'
|| sampleBoxInfo.name === 'lpcm' || lowercaseBoxName === 'lpcm'
) { ) {
// It's PCM // It's PCM
// developer.apple.com/documentation/quicktime-file-format/sound_sample_descriptions/ // developer.apple.com/documentation/quicktime-file-format/sound_sample_descriptions/
} else if (lowercaseBoxName === 'ulaw') {
track.info.codec = 'ulaw';
} else if (lowercaseBoxName === 'alaw') {
track.info.codec = 'alaw';
} else { } else {
const { name } = sampleBoxInfo; const { name } = sampleBoxInfo;
console.warn(`Unsupported audio codec (sample entry type '${name}') - discarding track.`); console.warn(`Unsupported audio codec (sample entry type '${name}') - discarding track.`);
@@ -750,7 +757,7 @@ export class IsobmffDemuxer extends Demuxer {
this.isobmffReader.pos += 2 * 4; this.isobmffReader.pos += 2 * 4;
if (sampleBoxInfo.name === 'lpcm') { if (lowercaseBoxName === 'lpcm') {
const bytesPerSample = (sampleSize + 7) >> 3; const bytesPerSample = (sampleSize + 7) >> 3;
const isFloat = Boolean(flags & 1); const isFloat = Boolean(flags & 1);
const isBigEndian = Boolean(flags & 2); const isBigEndian = Boolean(flags & 2);
@@ -791,7 +798,7 @@ export class IsobmffDemuxer extends Demuxer {
track.info.numberOfChannels = channelCount; track.info.numberOfChannels = channelCount;
track.info.sampleRate = sampleRate; track.info.sampleRate = sampleRate;
if (sampleBoxInfo.name === 'twos') { if (lowercaseBoxName === 'twos') {
if (sampleSize === 8) { if (sampleSize === 8) {
track.info.codec = 'pcm-s8'; track.info.codec = 'pcm-s8';
} else if (sampleSize === 16) { } else if (sampleSize === 16) {
@@ -799,7 +806,7 @@ export class IsobmffDemuxer extends Demuxer {
} else { } else {
throw new Error(`Unsupported sample size ${sampleSize} for codec 'twos'.`); throw new Error(`Unsupported sample size ${sampleSize} for codec 'twos'.`);
} }
} else if (sampleBoxInfo.name === 'sowt') { } else if (lowercaseBoxName === 'sowt') {
if (sampleSize === 8) { if (sampleSize === 8) {
track.info.codec = 'pcm-s8'; track.info.codec = 'pcm-s8';
} else if (sampleSize === 16) { } else if (sampleSize === 16) {
@@ -807,13 +814,13 @@ export class IsobmffDemuxer extends Demuxer {
} else { } else {
throw new Error(`Unsupported sample size ${sampleSize} for codec 'sowt'.`); throw new Error(`Unsupported sample size ${sampleSize} for codec 'sowt'.`);
} }
} else if (sampleBoxInfo.name === 'raw ') { } else if (lowercaseBoxName === 'raw ') {
track.info.codec = 'pcm-u8'; track.info.codec = 'pcm-u8';
} else if (sampleBoxInfo.name === 'in24') { } else if (lowercaseBoxName === 'in24') {
track.info.codec = 'pcm-s24be'; track.info.codec = 'pcm-s24be';
} else if (sampleBoxInfo.name === 'in32') { } else if (lowercaseBoxName === 'in32') {
track.info.codec = 'pcm-s32be'; track.info.codec = 'pcm-s32be';
} else if (sampleBoxInfo.name === 'fl32') { } else if (lowercaseBoxName === 'fl32') {
track.info.codec = 'pcm-f32be'; track.info.codec = 'pcm-f32be';
} }