272 lines
9.2 KiB
JavaScript
272 lines
9.2 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.getAudioCodecFromTrack = exports.getAudioCodecStringFromTrak = exports.isTwosAudioCodec = exports.isIn24AudioCodec = exports.isLpcmAudioCodec = exports.getAudioCodecFromTrak = exports.getSampleRate = exports.getNumberOfChannelsFromTrak = exports.getCodecPrivateFromTrak = exports.hasAudioCodec = exports.getAudioCodec = void 0;
|
|
const traversal_1 = require("./containers/iso-base-media/traversal");
|
|
const get_tracks_1 = require("./get-tracks");
|
|
const getAudioCodec = (parserState) => {
|
|
const tracks = (0, get_tracks_1.getTracks)(parserState, true);
|
|
if (tracks.length === 0) {
|
|
throw new Error('No tracks yet');
|
|
}
|
|
const audioTrack = tracks.find((t) => t.type === 'audio');
|
|
if (!audioTrack) {
|
|
return null;
|
|
}
|
|
if (audioTrack.type === 'audio') {
|
|
return audioTrack.codecEnum;
|
|
}
|
|
return null;
|
|
};
|
|
exports.getAudioCodec = getAudioCodec;
|
|
const hasAudioCodec = (state) => {
|
|
return (0, get_tracks_1.getHasTracks)(state, true);
|
|
};
|
|
exports.hasAudioCodec = hasAudioCodec;
|
|
const getCodecSpecificatorFromEsdsBox = ({ child, }) => {
|
|
const descriptor = child.descriptors.find((d) => d.type === 'decoder-config-descriptor');
|
|
if (!descriptor) {
|
|
throw new Error('No decoder-config-descriptor');
|
|
}
|
|
if (descriptor.type !== 'decoder-config-descriptor') {
|
|
throw new Error('Expected decoder-config-descriptor');
|
|
}
|
|
if (descriptor.asNumber !== 0x40) {
|
|
return {
|
|
primary: descriptor.asNumber,
|
|
secondary: null,
|
|
description: undefined,
|
|
};
|
|
}
|
|
const audioSpecificConfig = descriptor.decoderSpecificConfigs.find((d) => {
|
|
return d.type === 'mp4a-specific-config' ? d : null;
|
|
});
|
|
if (!audioSpecificConfig ||
|
|
audioSpecificConfig.type !== 'mp4a-specific-config') {
|
|
throw new Error('No audio-specific-config');
|
|
}
|
|
return {
|
|
primary: descriptor.asNumber,
|
|
secondary: audioSpecificConfig.audioObjectType,
|
|
description: audioSpecificConfig.asBytes,
|
|
};
|
|
};
|
|
const getCodecPrivateFromTrak = (trakBox) => {
|
|
const stsdBox = (0, traversal_1.getStsdBox)(trakBox);
|
|
if (!stsdBox) {
|
|
return null;
|
|
}
|
|
const audioSample = stsdBox.samples.find((s) => s.type === 'audio');
|
|
if (!audioSample || audioSample.type !== 'audio') {
|
|
return null;
|
|
}
|
|
const esds = audioSample.children.find((b) => b.type === 'esds-box');
|
|
if (!esds || esds.type !== 'esds-box') {
|
|
return null;
|
|
}
|
|
const decoderConfigDescriptor = esds.descriptors.find((d) => d.type === 'decoder-config-descriptor');
|
|
if (!decoderConfigDescriptor) {
|
|
return null;
|
|
}
|
|
const mp4a = decoderConfigDescriptor.decoderSpecificConfigs.find((d) => d.type === 'mp4a-specific-config');
|
|
if (!mp4a) {
|
|
return null;
|
|
}
|
|
return { type: 'aac-config', data: mp4a.asBytes };
|
|
};
|
|
exports.getCodecPrivateFromTrak = getCodecPrivateFromTrak;
|
|
const onSample = (sample, children) => {
|
|
const child = children.find((c) => c.type === 'esds-box');
|
|
if (child && child.type === 'esds-box') {
|
|
const ret = getCodecSpecificatorFromEsdsBox({ child });
|
|
return {
|
|
format: sample.format,
|
|
primarySpecificator: ret.primary,
|
|
secondarySpecificator: ret.secondary,
|
|
description: ret.description,
|
|
};
|
|
}
|
|
return {
|
|
format: sample.format,
|
|
primarySpecificator: null,
|
|
secondarySpecificator: null,
|
|
description: undefined,
|
|
};
|
|
};
|
|
const getNumberOfChannelsFromTrak = (trak) => {
|
|
const stsdBox = (0, traversal_1.getStsdBox)(trak);
|
|
if (!stsdBox) {
|
|
return null;
|
|
}
|
|
const sample = stsdBox.samples.find((s) => s.type === 'audio');
|
|
if (!sample || sample.type !== 'audio') {
|
|
return null;
|
|
}
|
|
return sample.numberOfChannels;
|
|
};
|
|
exports.getNumberOfChannelsFromTrak = getNumberOfChannelsFromTrak;
|
|
const getSampleRate = (trak) => {
|
|
const stsdBox = (0, traversal_1.getStsdBox)(trak);
|
|
if (!stsdBox) {
|
|
return null;
|
|
}
|
|
const sample = stsdBox.samples.find((s) => s.type === 'audio');
|
|
if (!sample || sample.type !== 'audio') {
|
|
return null;
|
|
}
|
|
return sample.sampleRate;
|
|
};
|
|
exports.getSampleRate = getSampleRate;
|
|
const getAudioCodecFromTrak = (trak) => {
|
|
const stsdBox = (0, traversal_1.getStsdBox)(trak);
|
|
if (!stsdBox) {
|
|
return null;
|
|
}
|
|
const sample = stsdBox.samples.find((s) => s.type === 'audio');
|
|
if (!sample || sample.type !== 'audio') {
|
|
return null;
|
|
}
|
|
const waveBox = sample.children.find((b) => b.type === 'regular-box' && b.boxType === 'wave');
|
|
if (waveBox && waveBox.type === 'regular-box' && waveBox.boxType === 'wave') {
|
|
const esdsSample = onSample(sample, waveBox.children);
|
|
if (esdsSample) {
|
|
return esdsSample;
|
|
}
|
|
}
|
|
const ret = onSample(sample, sample.children);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
return null;
|
|
};
|
|
exports.getAudioCodecFromTrak = getAudioCodecFromTrak;
|
|
const isLpcmAudioCodec = (trak) => {
|
|
var _a;
|
|
return ((_a = (0, exports.getAudioCodecFromTrak)(trak)) === null || _a === void 0 ? void 0 : _a.format) === 'lpcm';
|
|
};
|
|
exports.isLpcmAudioCodec = isLpcmAudioCodec;
|
|
const isIn24AudioCodec = (trak) => {
|
|
var _a;
|
|
return ((_a = (0, exports.getAudioCodecFromTrak)(trak)) === null || _a === void 0 ? void 0 : _a.format) === 'in24';
|
|
};
|
|
exports.isIn24AudioCodec = isIn24AudioCodec;
|
|
const isTwosAudioCodec = (trak) => {
|
|
var _a;
|
|
return ((_a = (0, exports.getAudioCodecFromTrak)(trak)) === null || _a === void 0 ? void 0 : _a.format) === 'twos';
|
|
};
|
|
exports.isTwosAudioCodec = isTwosAudioCodec;
|
|
const getAudioCodecStringFromTrak = (trak) => {
|
|
const codec = (0, exports.getAudioCodecFromTrak)(trak);
|
|
if (!codec) {
|
|
throw new Error('Expected codec');
|
|
}
|
|
if (codec.format === 'lpcm') {
|
|
return {
|
|
codecString: 'pcm-s16',
|
|
description: codec.description
|
|
? { type: 'unknown-data', data: codec.description }
|
|
: undefined,
|
|
};
|
|
}
|
|
if (codec.format === 'twos') {
|
|
return {
|
|
codecString: 'pcm-s16',
|
|
description: codec.description
|
|
? { type: 'unknown-data', data: codec.description }
|
|
: undefined,
|
|
};
|
|
}
|
|
if (codec.format === 'in24') {
|
|
return {
|
|
codecString: 'pcm-s24',
|
|
description: codec.description
|
|
? { type: 'unknown-data', data: codec.description }
|
|
: undefined,
|
|
};
|
|
}
|
|
const codecStringWithoutMp3Exception = [
|
|
codec.format,
|
|
codec.primarySpecificator ? codec.primarySpecificator.toString(16) : null,
|
|
codec.secondarySpecificator
|
|
? codec.secondarySpecificator.toString().padStart(2, '0')
|
|
: null,
|
|
].filter(Boolean).join('.');
|
|
// Really, MP3? 😔
|
|
const codecString = codecStringWithoutMp3Exception.toLowerCase() === 'mp4a.6b' ||
|
|
codecStringWithoutMp3Exception.toLowerCase() === 'mp4a.69'
|
|
? 'mp3' // or "mp4a.6B" would also work, with the uppercasing, but mp3 is probably more obvious
|
|
: codecStringWithoutMp3Exception;
|
|
if (codecString === 'mp3') {
|
|
return {
|
|
codecString,
|
|
description: codec.description
|
|
? {
|
|
type: 'unknown-data',
|
|
data: codec.description,
|
|
}
|
|
: undefined,
|
|
};
|
|
}
|
|
if (codecString.startsWith('mp4a.')) {
|
|
return {
|
|
codecString,
|
|
description: codec.description
|
|
? {
|
|
type: 'aac-config',
|
|
data: codec.description,
|
|
}
|
|
: undefined,
|
|
};
|
|
}
|
|
return {
|
|
codecString,
|
|
description: codec.description
|
|
? {
|
|
type: 'unknown-data',
|
|
data: codec.description,
|
|
}
|
|
: undefined,
|
|
};
|
|
};
|
|
exports.getAudioCodecStringFromTrak = getAudioCodecStringFromTrak;
|
|
const getAudioCodecFromAudioCodecInfo = (codec) => {
|
|
if (codec.format === 'twos') {
|
|
return 'pcm-s16';
|
|
}
|
|
if (codec.format === 'in24') {
|
|
return 'pcm-s24';
|
|
}
|
|
if (codec.format === 'lpcm') {
|
|
return 'pcm-s16';
|
|
}
|
|
if (codec.format === 'sowt') {
|
|
return 'aiff';
|
|
}
|
|
if (codec.format === 'ac-3') {
|
|
return 'ac3';
|
|
}
|
|
if (codec.format === 'Opus') {
|
|
return 'opus';
|
|
}
|
|
if (codec.format === 'mp4a') {
|
|
if (codec.primarySpecificator === 0x40) {
|
|
return 'aac';
|
|
}
|
|
if (codec.primarySpecificator === 0x6b) {
|
|
return 'mp3';
|
|
}
|
|
if (codec.primarySpecificator === null) {
|
|
return 'aac';
|
|
}
|
|
throw new Error('Unknown mp4a codec: ' + codec.primarySpecificator);
|
|
}
|
|
throw new Error(`Unknown audio format: ${codec.format}`);
|
|
};
|
|
const getAudioCodecFromTrack = (track) => {
|
|
const audioSample = (0, exports.getAudioCodecFromTrak)(track);
|
|
if (!audioSample) {
|
|
throw new Error('Could not find audio sample');
|
|
}
|
|
return getAudioCodecFromAudioCodecInfo(audioSample);
|
|
};
|
|
exports.getAudioCodecFromTrack = getAudioCodecFromTrack;
|