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
text/typescript
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;