| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137 |
1x
| /**
* 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: function (track, data, offset, pts, frameIndex) {
// Using http://www.datavoyage.com/mpgscript/mpeghdr.htm as a reference
if (offset + 24 > data.length) {
return undefined;
}
var header = this.parseHeader(data, offset);
if (header && offset + header.frameLength <= data.length) {
var frameDuration = header.samplesPerFrame * 90000 / header.sampleRate;
var stamp = pts + frameIndex * frameDuration;
var 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);
track.len += header.frameLength;
return { sample, length: header.frameLength };
}
return undefined;
},
parseHeader: function (data, offset) {
var headerB = (data[offset + 1] >> 3) & 3;
var headerC = (data[offset + 1] >> 1) & 3;
var headerE = (data[offset + 2] >> 4) & 15;
var headerF = (data[offset + 2] >> 2) & 3;
var headerG = (data[offset + 2] >> 1) & 1;
if (headerB !== 1 && headerE !== 0 && headerE !== 15 && headerF !== 3) {
var columnInBitrates = headerB === 3 ? (3 - headerC) : (headerC === 3 ? 3 : 4);
var bitRate = MpegAudio.BitratesMap[columnInBitrates * 14 + headerE - 1] * 1000;
var columnInSampleRates = headerB === 3 ? 0 : headerB === 2 ? 1 : 2;
var sampleRate = MpegAudio.SamplingRateMap[columnInSampleRates * 3 + headerF];
var channelCount = data[offset + 3] >> 6 === 3 ? 1 : 2; // If bits of channel mode are `11` then it is a single channel (Mono)
var sampleCoefficient = MpegAudio.SamplesCoefficients[headerB][headerC];
var bytesInSlot = MpegAudio.BytesInSlot[headerC];
var samplesPerFrame = sampleCoefficient * 8 * bytesInSlot;
var frameLength = parseInt(sampleCoefficient * bitRate / sampleRate + headerG, 10) * bytesInSlot;
return { sampleRate, channelCount, frameLength, samplesPerFrame };
}
return undefined;
},
isHeaderPattern: function (data, offset) {
return data[offset] === 0xff && (data[offset + 1] & 0xe0) === 0xe0 && (data[offset + 1] & 0x06) !== 0x00;
},
isHeader: function (data, offset) {
// 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: function (data, offset) {
// 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
let headerLength = 4;
// MPEG frame Length
let header = this.parseHeader(data, offset);
let frameLength = headerLength;
if (header && header.frameLength) {
frameLength = header.frameLength;
}
let newOffset = offset + frameLength;
if (newOffset === data.length || (newOffset + 1 < data.length && this.isHeaderPattern(data, newOffset))) {
return true;
}
}
return false;
}
};
export default MpegAudio;
|