UNPKG

hjplayer

Version:

hjplayer, a HTML5 Player, can play flv and hls by Media Source Extension, based on typescript;

243 lines (221 loc) 6.42 kB
import { track } from '../TSCodecInterface'; /** * MPEG parser helper */ const MpegAudio = { BitratesMap: [ 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 ], SamplingRateMap: [44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000], SamplesCoefficients: [ // MPEG 2.5 [ 0, // Reserved 72, // Layer3 144, // Layer2 12 // Layer1 ], // Reserved [ 0, // Reserved 0, // Layer3 0, // Layer2 0 // Layer1 ], // MPEG 2 [ 0, // Reserved 72, // Layer3 144, // Layer2 12 // Layer1 ], // MPEG 1 [ 0, // Reserved 144, // Layer3 144, // Layer2 12 // Layer1 ] ], BytesInSlot: [ 0, // Reserved 1, // Layer3 1, // Layer2 4 // Layer1 ], appendFrame( track: track, data: Uint8Array, offset: number, pts: number, frameIndex: number ) { // Using http://www.datavoyage.com/mpgscript/mpeghdr.htm as a reference if(offset + 24 > data.length) { return undefined; } const header = this.parseHeader(data, offset); if(header && offset + header.frameLength <= data.length) { const frameDuration = (header.samplesPerFrame * 90000) / header.sampleRate; const stamp = pts + frameIndex * frameDuration; const sample = { unit: data.subarray(offset, offset + header.frameLength), pts: stamp, dts: stamp }; track.config = []; track.channelCount = header.channelCount; track.samplerate = header.sampleRate; track.samples.push(sample); return { sample, length: header.frameLength }; } return undefined; }, parseHeader(data: Uint8Array, offset: number) { const headerB = (data[offset + 1] >> 3) & 3; const headerC = (data[offset + 1] >> 1) & 3; const headerE = (data[offset + 2] >> 4) & 15; const headerF = (data[offset + 2] >> 2) & 3; const headerG = (data[offset + 2] >> 1) & 1; if(headerB !== 1 && headerE !== 0 && headerE !== 15 && headerF !== 3) { // const columnInBitrates = headerB === 3 ? 3 - headerC : headerC === 3 ? 3 : 4; let columnInBitrates: number; if(headerB === 3) { columnInBitrates = 3 - headerC; } else if(headerC === 3) { columnInBitrates = 3; } else { columnInBitrates = 4; } const bitRate = MpegAudio.BitratesMap[columnInBitrates * 14 + headerE - 1] * 1000; let columnInSampleRates: number; if(headerB === 3) { columnInSampleRates = 0; } else if(headerB === 2) { columnInSampleRates = 1; } else { columnInSampleRates = 2; } // const columnInSampleRates = headerB === 3 ? 0 : headerB === 2 ? 1 : 2; const sampleRate = MpegAudio.SamplingRateMap[columnInSampleRates * 3 + headerF]; const channelCount = data[offset + 3] >> 6 === 3 ? 1 : 2; // If bits of channel mode are `11` then it is a single channel (Mono) const sampleCoefficient = MpegAudio.SamplesCoefficients[headerB][headerC]; const bytesInSlot = MpegAudio.BytesInSlot[headerC]; const samplesPerFrame = sampleCoefficient * 8 * bytesInSlot; const frameLength = parseInt(String((sampleCoefficient * bitRate) / sampleRate + headerG), 10) * bytesInSlot; return { sampleRate, channelCount, frameLength, samplesPerFrame }; } return undefined; }, isHeaderPattern(data: Uint8Array, offset: number) { return ( data[offset] === 0xff && (data[offset + 1] & 0xe0) === 0xe0 && (data[offset + 1] & 0x06) !== 0x00 ); }, isHeader(data: Uint8Array, offset: number) { // Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1 // Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III) // More info http://www.mp3-tech.org/programmer/frame_header.html if(offset + 1 < data.length && this.isHeaderPattern(data, offset)) { return true; } return false; }, probe(data: Uint8Array, offset: number) { // same as isHeader but we also check that MPEG frame follows last MPEG frame // or end of data is reached if(offset + 1 < data.length && this.isHeaderPattern(data, offset)) { // MPEG header Length const headerLength = 4; // MPEG frame Length const header = this.parseHeader(data, offset); let frameLength = headerLength; if(header && header.frameLength) { ({ frameLength } = header); } const newOffset = offset + frameLength; if( newOffset === data.length || (newOffset + 1 < data.length && this.isHeaderPattern(data, newOffset)) ) { return true; } } return false; } }; export default MpegAudio;