UNPKG

@remotion/media-parser

Version:

A pure JavaScript library for parsing video files

259 lines (258 loc) 7.74 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isMp3PacketHeaderHereAndInNext = exports.isMp3PacketHeaderHere = exports.parseMp3PacketHeader = void 0; const get_frame_length_1 = require("./get-frame-length"); const samples_per_mpeg_file_1 = require("./samples-per-mpeg-file"); function getSamplingFrequency({ bits, mpegVersion, }) { const samplingTable = { 0b00: { MPEG1: 44100, MPEG2: 22050 }, 0b01: { MPEG1: 48000, MPEG2: 24000 }, 0b10: { MPEG1: 32000, MPEG2: 16000 }, 0b11: { MPEG1: 'reserved', MPEG2: 'reserved' }, }; const key = `MPEG${mpegVersion}`; const value = samplingTable[bits][key]; if (value === 'reserved') { throw new Error('Reserved sampling frequency'); } if (!value) { throw new Error('Invalid sampling frequency for MPEG version: ' + JSON.stringify({ bits, version: mpegVersion })); } return value; } function getBitrateKB({ bits, mpegVersion, level, }) { const bitrateTable = { 0b0000: { 'V1,L1': 'free', 'V1,L2': 'free', 'V1,L3': 'free', 'V2,L1': 'free', 'V2,L2&L3': 'free', }, 0b0001: { 'V1,L1': 32, 'V1,L2': 32, 'V1,L3': 32, 'V2,L1': 32, 'V2,L2&L3': 8 }, 0b0010: { 'V1,L1': 64, 'V1,L2': 48, 'V1,L3': 40, 'V2,L1': 48, 'V2,L2&L3': 16, }, 0b0011: { 'V1,L1': 96, 'V1,L2': 56, 'V1,L3': 48, 'V2,L1': 56, 'V2,L2&L3': 24, }, 0b0100: { 'V1,L1': 128, 'V1,L2': 64, 'V1,L3': 56, 'V2,L1': 64, 'V2,L2&L3': 32, }, 0b0101: { 'V1,L1': 160, 'V1,L2': 80, 'V1,L3': 64, 'V2,L1': 80, 'V2,L2&L3': 40, }, 0b0110: { 'V1,L1': 192, 'V1,L2': 96, 'V1,L3': 80, 'V2,L1': 96, 'V2,L2&L3': 48, }, 0b0111: { 'V1,L1': 224, 'V1,L2': 112, 'V1,L3': 96, 'V2,L1': 112, 'V2,L2&L3': 56, }, 0b1000: { 'V1,L1': 256, 'V1,L2': 128, 'V1,L3': 112, 'V2,L1': 128, 'V2,L2&L3': 64, }, 0b1001: { 'V1,L1': 288, 'V1,L2': 160, 'V1,L3': 128, 'V2,L1': 144, 'V2,L2&L3': 80, }, 0b1010: { 'V1,L1': 320, 'V1,L2': 192, 'V1,L3': 160, 'V2,L1': 160, 'V2,L2&L3': 96, }, 0b1011: { 'V1,L1': 352, 'V1,L2': 224, 'V1,L3': 192, 'V2,L1': 176, 'V2,L2&L3': 112, }, 0b1100: { 'V1,L1': 384, 'V1,L2': 256, 'V1,L3': 224, 'V2,L1': 192, 'V2,L2&L3': 128, }, 0b1101: { 'V1,L1': 416, 'V1,L2': 320, 'V1,L3': 256, 'V2,L1': 224, 'V2,L2&L3': 144, }, 0b1110: { 'V1,L1': 448, 'V1,L2': 384, 'V1,L3': 320, 'V2,L1': 256, 'V2,L2&L3': 160, }, 0b1111: { 'V1,L1': 'bad', 'V1,L2': 'bad', 'V1,L3': 'bad', 'V2,L1': 'bad', 'V2,L2&L3': 'bad', }, }; // Determine the correct key based on version and level let key; if (mpegVersion === 2 && (level === 2 || level === 3)) { key = 'V2,L2&L3'; } else { key = `V${mpegVersion},L${level}`; } // Return the corresponding bitrate return bitrateTable[bits][key]; } const innerParseMp3PacketHeader = (iterator) => { for (let i = 0; i < 11; i++) { const expectToBe1 = iterator.getBits(1); if (expectToBe1 !== 1) { throw new Error('Expected 1'); } } const audioVersionId = iterator.getBits(2); /** * 00 - MPEG Version 2.5 (later extension of MPEG 2) 01 - reserved 10 - MPEG Version 2 (ISO/IEC 13818-3) 11 - MPEG Version 1 (ISO/IEC 11172-3) */ if (audioVersionId !== 0b11 && audioVersionId !== 0b10) { throw new Error('Expected MPEG Version 1 or 2'); } const mpegVersion = audioVersionId === 0b11 ? 1 : 2; const layerBits = iterator.getBits(2); /** * 00 - reserved 01 - Layer III 10 - Layer II 11 - Layer I */ if (layerBits === 0b00) { throw new Error('Expected Layer I, II or III'); } const layer = layerBits === 0b11 ? 1 : layerBits === 0b10 ? 2 : 3; const protectionBit = iterator.getBits(1); if (protectionBit !== 0b1) { throw new Error('Does not support CRC yet'); } const bitrateIndex = iterator.getBits(4); const bitrateInKbit = getBitrateKB({ bits: bitrateIndex, mpegVersion, level: audioVersionId, }); if (bitrateInKbit === 'bad') { throw new Error('Invalid bitrate'); } if (bitrateInKbit === 'free') { throw new Error('Free bitrate not supported'); } const samplingFrequencyIndex = iterator.getBits(2); const sampleRate = getSamplingFrequency({ bits: samplingFrequencyIndex, mpegVersion, }); const padding = Boolean(iterator.getBits(1)); iterator.getBits(1); // private bit const channelMode = iterator.getBits(2); // channel mode iterator.getBits(2); // mode extension iterator.getBits(1); // copyright iterator.getBits(1); // original iterator.getBits(2); // emphasis const numberOfChannels = channelMode === 0b11 ? 1 : 2; const samplesPerFrame = (0, samples_per_mpeg_file_1.getSamplesPerMpegFrame)({ mpegVersion, layer }); const frameLength = (0, get_frame_length_1.getMpegFrameLength)({ bitrateKbit: bitrateInKbit, padding, samplesPerFrame, samplingFrequency: sampleRate, layer, }); return { frameLength, bitrateInKbit, layer, mpegVersion, numberOfChannels, sampleRate, samplesPerFrame, }; }; const parseMp3PacketHeader = (iterator) => { iterator.startReadingBits(); const d = innerParseMp3PacketHeader(iterator); iterator.stopReadingBits(); return d; }; exports.parseMp3PacketHeader = parseMp3PacketHeader; const isMp3PacketHeaderHere = (iterator) => { const offset = iterator.counter.getOffset(); iterator.startReadingBits(); try { const res = innerParseMp3PacketHeader(iterator); iterator.stopReadingBits(); iterator.counter.decrement(iterator.counter.getOffset() - offset); return res; } catch (_a) { iterator.stopReadingBits(); iterator.counter.decrement(iterator.counter.getOffset() - offset); return false; } }; exports.isMp3PacketHeaderHere = isMp3PacketHeaderHere; const isMp3PacketHeaderHereAndInNext = (iterator) => { const offset = iterator.counter.getOffset(); const res = (0, exports.isMp3PacketHeaderHere)(iterator); if (!res) { return false; } // cannot check here because we don't have enough data, let's hope for the best if (iterator.bytesRemaining() <= res.frameLength) { return true; } iterator.counter.increment(res.frameLength); const isHere = (0, exports.isMp3PacketHeaderHere)(iterator); iterator.counter.decrement(iterator.counter.getOffset() - offset); return isHere; }; exports.isMp3PacketHeaderHereAndInNext = isMp3PacketHeaderHereAndInNext;