179 lines
5.8 KiB
JavaScript
179 lines
5.8 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.mapAudioObjectTypeToCodecString = exports.parseAacCodecPrivate = exports.createAacCodecPrivate = exports.getSampleRateFromSampleFrequencyIndex = void 0;
|
|
const getSampleRateFromSampleFrequencyIndex = (samplingFrequencyIndex) => {
|
|
switch (samplingFrequencyIndex) {
|
|
case 0:
|
|
return 96000;
|
|
case 1:
|
|
return 88200;
|
|
case 2:
|
|
return 64000;
|
|
case 3:
|
|
return 48000;
|
|
case 4:
|
|
return 44100;
|
|
case 5:
|
|
return 32000;
|
|
case 6:
|
|
return 24000;
|
|
case 7:
|
|
return 22050;
|
|
case 8:
|
|
return 16000;
|
|
case 9:
|
|
return 12000;
|
|
case 10:
|
|
return 11025;
|
|
case 11:
|
|
return 8000;
|
|
case 12:
|
|
return 7350;
|
|
default:
|
|
throw new Error(`Unexpected sampling frequency index ${samplingFrequencyIndex}`);
|
|
}
|
|
};
|
|
exports.getSampleRateFromSampleFrequencyIndex = getSampleRateFromSampleFrequencyIndex;
|
|
// codec private, for example [17, 144]
|
|
// audioObjectType = 2 = 'AAC LC'
|
|
// samplingFrequencyIndex = 3 = '48000 Hz'
|
|
// channelConfiguration = 2 = '2 channels'
|
|
/**
|
|
* bytes 17,144: 00010 001 | 1 0010 000
|
|
^^^^^ ^^^ ^ ^^^^ ^
|
|
| | | | padding
|
|
| | |
|
|
| | +-- channelConfiguration (2)
|
|
| +-- samplingFrequencyIndex (3)
|
|
+-- audioConfigType (2)
|
|
*/
|
|
// https://wiki.multimedia.cx/index.php/MPEG-4_Audio#Channel_Configurations
|
|
const getConfigForSampleRate = (sampleRate) => {
|
|
if (sampleRate === 96000) {
|
|
return 0;
|
|
}
|
|
if (sampleRate === 88200) {
|
|
return 1;
|
|
}
|
|
if (sampleRate === 64000) {
|
|
return 2;
|
|
}
|
|
if (sampleRate === 48000) {
|
|
return 3;
|
|
}
|
|
if (sampleRate === 44100) {
|
|
return 4;
|
|
}
|
|
if (sampleRate === 32000) {
|
|
return 5;
|
|
}
|
|
if (sampleRate === 24000) {
|
|
return 6;
|
|
}
|
|
if (sampleRate === 22050) {
|
|
return 7;
|
|
}
|
|
if (sampleRate === 16000) {
|
|
return 8;
|
|
}
|
|
if (sampleRate === 12000) {
|
|
return 9;
|
|
}
|
|
if (sampleRate === 11025) {
|
|
return 10;
|
|
}
|
|
if (sampleRate === 8000) {
|
|
return 11;
|
|
}
|
|
if (sampleRate === 7350) {
|
|
return 12;
|
|
}
|
|
throw new Error(`Unexpected sample rate ${sampleRate}`);
|
|
};
|
|
const createAacCodecPrivate = ({ audioObjectType, sampleRate, channelConfiguration, codecPrivate, }) => {
|
|
if (codecPrivate !== null && codecPrivate.length > 2) {
|
|
// Video submitted
|
|
// submitted by Yossi Elkrief
|
|
// TOOD: Check if we are now parsing correctly
|
|
return codecPrivate;
|
|
}
|
|
const bits = `${audioObjectType.toString(2).padStart(5, '0')}${getConfigForSampleRate(sampleRate).toString(2).padStart(4, '0')}${channelConfiguration.toString(2).padStart(4, '0')}000`;
|
|
if (bits.length !== 16) {
|
|
throw new Error('Invalid AAC codec private ' + bits.length);
|
|
}
|
|
if (channelConfiguration === 0 || channelConfiguration > 7) {
|
|
throw new Error('Invalid channel configuration ' + channelConfiguration);
|
|
}
|
|
const firstByte = parseInt(bits.slice(0, 8), 2);
|
|
const secondByte = parseInt(bits.slice(8, 16), 2);
|
|
return new Uint8Array([firstByte, secondByte]);
|
|
};
|
|
exports.createAacCodecPrivate = createAacCodecPrivate;
|
|
const parseAacCodecPrivate = (bytes) => {
|
|
if (bytes.length < 2) {
|
|
throw new Error('Invalid AAC codec private length');
|
|
}
|
|
const bits = [...bytes].map((b) => b.toString(2).padStart(8, '0')).join('');
|
|
let offset = 0;
|
|
const audioObjectType = parseInt(bits.slice(offset, offset + 5), 2);
|
|
offset += 5;
|
|
const samplingFrequencyIndex = parseInt(bits.slice(offset, offset + 4), 2);
|
|
offset += 4;
|
|
if (samplingFrequencyIndex === 0xf) {
|
|
offset += 24;
|
|
}
|
|
const channelConfiguration = parseInt(bits.slice(offset, offset + 4), 2);
|
|
offset += 4;
|
|
if (audioObjectType === 5) {
|
|
const extensionSamplingFrequencyIndex = parseInt(bits.slice(offset, offset + 4), 2);
|
|
offset += 4;
|
|
const newAudioObjectType = parseInt(bits.slice(offset, offset + 5), 2);
|
|
offset += 5;
|
|
return {
|
|
audioObjectType: newAudioObjectType,
|
|
sampleRate: (0, exports.getSampleRateFromSampleFrequencyIndex)(extensionSamplingFrequencyIndex),
|
|
channelConfiguration,
|
|
};
|
|
}
|
|
const sampleRate = (0, exports.getSampleRateFromSampleFrequencyIndex)(samplingFrequencyIndex);
|
|
return {
|
|
audioObjectType,
|
|
sampleRate,
|
|
channelConfiguration,
|
|
};
|
|
};
|
|
exports.parseAacCodecPrivate = parseAacCodecPrivate;
|
|
const mapAudioObjectTypeToCodecString = (audioObjectType) => {
|
|
/**
|
|
* 1. 1 - mp4a.40.2: MPEG-4 AAC LC (Low Complexity)
|
|
2. 2 - mp4a.40.5: MPEG-4 AAC HE (High Efficiency)
|
|
3. 3 - mp4a.40.29: MPEG-4 AAC HEv2 (High Efficiency v2)
|
|
4. 4 - mp4a.40.1: MPEG-4 AAC Main
|
|
5. 5 - mp4a.40.3: MPEG-4 AAC SSR (Scalable Sample Rate)
|
|
6. 6 - mp4a.40.4: MPEG-4 AAC LTP (Long Term Prediction)
|
|
7. 17 - mp4a.40.17: MPEG-4 AAC LD (Low Delay)
|
|
8. 23 - mp4a.40.23: MPEG-4 AAC ELD (Enhanced Low Delay)
|
|
*/
|
|
switch (audioObjectType) {
|
|
case 1:
|
|
return 'mp4a.40.2';
|
|
case 2:
|
|
return 'mp4a.40.5';
|
|
case 3:
|
|
return 'mp4a.40.29';
|
|
case 4:
|
|
return 'mp4a.40.1';
|
|
case 5:
|
|
return 'mp4a.40.3';
|
|
case 6:
|
|
return 'mp4a.40.4';
|
|
case 17:
|
|
return 'mp4a.40.17';
|
|
case 23:
|
|
return 'mp4a.40.23';
|
|
default:
|
|
throw new Error(`Unexpected audio object type ${audioObjectType}`);
|
|
}
|
|
};
|
|
exports.mapAudioObjectTypeToCodecString = mapAudioObjectTypeToCodecString;
|