@alessmicrosystems/mpegts.js 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +158 -0
- package/README_ja.md +153 -0
- package/README_zh.md +157 -0
- package/d.ts/mpegts.d.ts +524 -0
- package/d.ts/src/core/mse-events.d.ts +9 -0
- package/d.ts/src/core/transmuxing-events.d.ts +24 -0
- package/d.ts/src/demux/aac.d.ts +44 -0
- package/d.ts/src/demux/ac3.d.ts +70 -0
- package/d.ts/src/demux/av1-parser.d.ts +77 -0
- package/d.ts/src/demux/av1.d.ts +11 -0
- package/d.ts/src/demux/base-demuxer.d.ts +55 -0
- package/d.ts/src/demux/h264.d.ts +40 -0
- package/d.ts/src/demux/h265.d.ts +65 -0
- package/d.ts/src/demux/klv.d.ts +17 -0
- package/d.ts/src/demux/mp3.d.ts +6 -0
- package/d.ts/src/demux/mpeg4-audio.d.ts +28 -0
- package/d.ts/src/demux/pat-pmt-pes.d.ts +106 -0
- package/d.ts/src/demux/patpmt.d.ts +40 -0
- package/d.ts/src/demux/pes-private-data.d.ts +14 -0
- package/d.ts/src/demux/pgs-data.d.ts +9 -0
- package/d.ts/src/demux/scte35.d.ts +250 -0
- package/d.ts/src/demux/sei.d.ts +8 -0
- package/d.ts/src/demux/smpte2038.d.ts +22 -0
- package/d.ts/src/demux/ts-demuxer.d.ts +124 -0
- package/d.ts/src/player/live-latency-chaser.d.ts +10 -0
- package/d.ts/src/player/live-latency-synchronizer.d.ts +10 -0
- package/d.ts/src/player/loading-controller.d.ts +19 -0
- package/d.ts/src/player/mse-player.d.ts +30 -0
- package/d.ts/src/player/player-engine-dedicated-thread-worker.d.ts +2 -0
- package/d.ts/src/player/player-engine-dedicated-thread.d.ts +48 -0
- package/d.ts/src/player/player-engine-main-thread.d.ts +50 -0
- package/d.ts/src/player/player-engine-worker-cmd-def.d.ts +25 -0
- package/d.ts/src/player/player-engine-worker-msg-def.d.ts +54 -0
- package/d.ts/src/player/player-engine-worker.d.ts +2 -0
- package/d.ts/src/player/player-engine.d.ts +16 -0
- package/d.ts/src/player/player-events.d.ts +21 -0
- package/d.ts/src/player/seeking-handler.d.ts +22 -0
- package/d.ts/src/player/startup-stall-jumper.d.ts +14 -0
- package/d.ts/src/utils/typedarray-equality.d.ts +2 -0
- package/dist/mpegts.js +3 -0
- package/dist/mpegts.js.LICENSE.txt +7 -0
- package/dist/mpegts.js.map +1 -0
- package/package.json +53 -0
- package/src/config.js +67 -0
- package/src/core/features.js +88 -0
- package/src/core/media-info.js +127 -0
- package/src/core/media-segment-info.js +230 -0
- package/src/core/mse-controller.js +599 -0
- package/src/core/mse-events.ts +28 -0
- package/src/core/transmuxer.js +346 -0
- package/src/core/transmuxing-controller.js +628 -0
- package/src/core/transmuxing-events.ts +43 -0
- package/src/core/transmuxing-worker.js +286 -0
- package/src/demux/aac.ts +397 -0
- package/src/demux/ac3.ts +335 -0
- package/src/demux/amf-parser.js +243 -0
- package/src/demux/av1-parser.ts +629 -0
- package/src/demux/av1.ts +103 -0
- package/src/demux/base-demuxer.ts +69 -0
- package/src/demux/demux-errors.js +26 -0
- package/src/demux/exp-golomb.js +116 -0
- package/src/demux/flv-demuxer.js +1854 -0
- package/src/demux/h264.ts +187 -0
- package/src/demux/h265-parser.js +501 -0
- package/src/demux/h265.ts +214 -0
- package/src/demux/klv.ts +40 -0
- package/src/demux/mp3.ts +7 -0
- package/src/demux/mpeg4-audio.ts +45 -0
- package/src/demux/pat-pmt-pes.ts +132 -0
- package/src/demux/pes-private-data.ts +16 -0
- package/src/demux/pgs-data.ts +11 -0
- package/src/demux/scte35.ts +723 -0
- package/src/demux/sei.ts +99 -0
- package/src/demux/smpte2038.ts +89 -0
- package/src/demux/sps-parser.js +298 -0
- package/src/demux/ts-demuxer.ts +2405 -0
- package/src/index.js +4 -0
- package/src/io/fetch-stream-loader.js +266 -0
- package/src/io/io-controller.js +647 -0
- package/src/io/loader.js +134 -0
- package/src/io/param-seek-handler.js +85 -0
- package/src/io/range-seek-handler.js +52 -0
- package/src/io/speed-sampler.js +93 -0
- package/src/io/websocket-loader.js +151 -0
- package/src/io/xhr-moz-chunked-loader.js +211 -0
- package/src/io/xhr-msstream-loader.js +307 -0
- package/src/io/xhr-range-loader.js +366 -0
- package/src/mpegts.js +95 -0
- package/src/player/live-latency-chaser.ts +66 -0
- package/src/player/live-latency-synchronizer.ts +79 -0
- package/src/player/loading-controller.ts +142 -0
- package/src/player/mse-player.ts +150 -0
- package/src/player/native-player.js +262 -0
- package/src/player/player-engine-dedicated-thread.ts +479 -0
- package/src/player/player-engine-main-thread.ts +463 -0
- package/src/player/player-engine-worker-cmd-def.ts +62 -0
- package/src/player/player-engine-worker-msg-def.ts +102 -0
- package/src/player/player-engine-worker.ts +370 -0
- package/src/player/player-engine.ts +35 -0
- package/src/player/player-errors.js +39 -0
- package/src/player/player-events.ts +40 -0
- package/src/player/seeking-handler.ts +205 -0
- package/src/player/startup-stall-jumper.ts +86 -0
- package/src/remux/aac-silent.js +56 -0
- package/src/remux/mp4-generator.js +866 -0
- package/src/remux/mp4-remuxer.js +778 -0
- package/src/utils/browser.js +128 -0
- package/src/utils/exception.js +73 -0
- package/src/utils/logger.js +140 -0
- package/src/utils/logging-control.js +165 -0
- package/src/utils/polyfill.js +68 -0
- package/src/utils/typedarray-equality.ts +69 -0
- package/src/utils/utf8-conv.js +84 -0
- package/src/utils/webworkify-webpack.js +202 -0
- package/tsconfig.json +16 -0
- package/tslint.json +1 -0
- package/types/index.d.ts +3 -0
- package/types/test-flv.ts +8 -0
- package/types/tsconfig.json +24 -0
- package/webpack.config.js +55 -0
package/src/demux/ac3.ts
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import Log from "../utils/logger";
|
|
2
|
+
import ExpGolomb from "./exp-golomb";
|
|
3
|
+
import { MPEG4AudioObjectTypes, MPEG4SamplingFrequencies, MPEG4SamplingFrequencyIndex } from "./mpeg4-audio";
|
|
4
|
+
|
|
5
|
+
export class AC3Frame {
|
|
6
|
+
sampling_frequency: number;
|
|
7
|
+
sampling_rate_code: number;
|
|
8
|
+
bit_stream_identification: number;
|
|
9
|
+
bit_stream_mode: number;
|
|
10
|
+
low_frequency_effects_channel_on: number;
|
|
11
|
+
frame_size_code: number;
|
|
12
|
+
channel_count: number;
|
|
13
|
+
channel_mode: number;
|
|
14
|
+
|
|
15
|
+
data: Uint8Array;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const frame_size_code_table = [
|
|
19
|
+
[
|
|
20
|
+
64, 64, 80, 80, 96, 96, 112, 112, 128, 128,
|
|
21
|
+
160, 160, 192, 192, 224, 224, 256, 256, 320, 320,
|
|
22
|
+
384, 384, 448, 448, 512, 512, 640, 640, 768, 768,
|
|
23
|
+
896, 896, 1024, 1024, 1152, 1152, 1280, 1280,
|
|
24
|
+
],
|
|
25
|
+
[
|
|
26
|
+
69, 70, 87, 88, 104, 105, 121, 122, 139, 140,
|
|
27
|
+
174, 175, 208, 209, 243, 244, 278, 279, 348, 349,
|
|
28
|
+
417, 418, 487, 488, 557, 558, 696, 697, 835, 836,
|
|
29
|
+
975, 976, 1114, 1115, 1253, 1254, 1393, 1394
|
|
30
|
+
],
|
|
31
|
+
[
|
|
32
|
+
96, 96, 120, 120, 144, 144, 168, 168, 192, 192,
|
|
33
|
+
240, 240, 288, 288, 336, 336, 384, 384, 480, 480,
|
|
34
|
+
576, 576, 672, 672, 768, 768, 960, 960, 1152, 1152,
|
|
35
|
+
1344, 1344, 1536, 1536, 1728, 1728, 1920, 1920,
|
|
36
|
+
],
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
export class AC3Parser {
|
|
40
|
+
|
|
41
|
+
private readonly TAG: string = "AC3Parser";
|
|
42
|
+
|
|
43
|
+
private data_: Uint8Array;
|
|
44
|
+
private current_syncword_offset_: number;
|
|
45
|
+
private eof_flag_: boolean;
|
|
46
|
+
private has_last_incomplete_data: boolean;
|
|
47
|
+
|
|
48
|
+
public constructor(data: Uint8Array) {
|
|
49
|
+
this.data_ = data;
|
|
50
|
+
this.current_syncword_offset_ = this.findNextSyncwordOffset(0);
|
|
51
|
+
if (this.eof_flag_) {
|
|
52
|
+
Log.e(this.TAG, `Could not found AC3 syncword until payload end`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private findNextSyncwordOffset(syncword_offset: number): number {
|
|
57
|
+
let i = syncword_offset;
|
|
58
|
+
let data = this.data_;
|
|
59
|
+
|
|
60
|
+
while (true) {
|
|
61
|
+
if (i + 7 >= data.byteLength) {
|
|
62
|
+
this.eof_flag_ = true;
|
|
63
|
+
return data.byteLength;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// search 16-bit 0x0B77 syncword
|
|
67
|
+
let syncword = (data[i + 0] << 8) | (data[i + 1] << 0)
|
|
68
|
+
if (syncword === 0x0B77) {
|
|
69
|
+
return i;
|
|
70
|
+
} else {
|
|
71
|
+
i++;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public readNextAC3Frame(): AC3Frame | null {
|
|
77
|
+
let data = this.data_;
|
|
78
|
+
let ac3_frame: AC3Frame = null;
|
|
79
|
+
|
|
80
|
+
while (ac3_frame == null) {
|
|
81
|
+
if (this.eof_flag_) {
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let syncword_offset = this.current_syncword_offset_;
|
|
86
|
+
let offset = syncword_offset;
|
|
87
|
+
|
|
88
|
+
let sampling_rate_code = data[offset + 4] >> 6;
|
|
89
|
+
let sampling_frequency = [48000, 44200, 33000][sampling_rate_code];
|
|
90
|
+
|
|
91
|
+
let frame_size_code = data[offset + 4] & 0x3F;
|
|
92
|
+
let frame_size = frame_size_code_table[sampling_rate_code][frame_size_code] * 2;
|
|
93
|
+
|
|
94
|
+
if (isNaN(frame_size) || offset + frame_size > this.data_.byteLength) {
|
|
95
|
+
// data not enough for extracting last sample
|
|
96
|
+
this.eof_flag_ = true;
|
|
97
|
+
this.has_last_incomplete_data = true;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let next_syncword_offset = this.findNextSyncwordOffset(offset + frame_size);
|
|
102
|
+
this.current_syncword_offset_ = next_syncword_offset;
|
|
103
|
+
|
|
104
|
+
let bit_stream_identification = data[offset + 5] >> 3;
|
|
105
|
+
let bit_stream_mode = data[offset + 5] & 0x07;
|
|
106
|
+
|
|
107
|
+
let channel_mode = data[offset + 6] >> 5;
|
|
108
|
+
|
|
109
|
+
let lfe_skip = 0;
|
|
110
|
+
if ((channel_mode & 0x01) !== 0 && channel_mode !== 1) { lfe_skip += 2; }
|
|
111
|
+
if ((channel_mode & 0x04) !== 0) { lfe_skip += 2; }
|
|
112
|
+
if (channel_mode === 0x02) { lfe_skip += 2; }
|
|
113
|
+
|
|
114
|
+
let low_frequency_effects_channel_on = (((data[offset + 6] << 8) | (data[offset + 7] << 0)) >> (12 - lfe_skip)) & 0x01;
|
|
115
|
+
|
|
116
|
+
let channel_count = [2, 1, 2, 3, 3, 4, 4, 5][channel_mode] + low_frequency_effects_channel_on;
|
|
117
|
+
|
|
118
|
+
ac3_frame = new AC3Frame();
|
|
119
|
+
ac3_frame.sampling_frequency = sampling_frequency;
|
|
120
|
+
ac3_frame.channel_count = channel_count;
|
|
121
|
+
ac3_frame.channel_mode = channel_mode;
|
|
122
|
+
ac3_frame.bit_stream_identification = bit_stream_identification;
|
|
123
|
+
ac3_frame.low_frequency_effects_channel_on = low_frequency_effects_channel_on;
|
|
124
|
+
ac3_frame.bit_stream_mode = bit_stream_mode;
|
|
125
|
+
ac3_frame.frame_size_code = frame_size_code;
|
|
126
|
+
ac3_frame.data = data.subarray(offset, offset + frame_size);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return ac3_frame;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
public hasIncompleteData(): boolean {
|
|
133
|
+
return this.has_last_incomplete_data;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
public getIncompleteData(): Uint8Array {
|
|
137
|
+
if (!this.has_last_incomplete_data) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return this.data_.subarray(this.current_syncword_offset_);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
export class AC3Config {
|
|
145
|
+
|
|
146
|
+
public config: Array<number>;
|
|
147
|
+
public sampling_rate: number;
|
|
148
|
+
public bit_stream_identification: number;
|
|
149
|
+
public bit_stream_mode: number;
|
|
150
|
+
public low_frequency_effects_channel_on: number;
|
|
151
|
+
public channel_count: number;
|
|
152
|
+
public channel_mode: number;
|
|
153
|
+
public codec_mimetype: string;
|
|
154
|
+
public original_codec_mimetype: string;
|
|
155
|
+
|
|
156
|
+
public constructor(frame: AC3Frame) {
|
|
157
|
+
let config: Array<number> = null;
|
|
158
|
+
|
|
159
|
+
config = [
|
|
160
|
+
(frame.sampling_rate_code << 6) | (frame.bit_stream_identification << 1) | (frame.bit_stream_mode >> 2),
|
|
161
|
+
((frame.bit_stream_mode & 0x03) << 6) | (frame.channel_mode << 3) | (frame.low_frequency_effects_channel_on << 2) | (frame.frame_size_code >> 4),
|
|
162
|
+
(frame.frame_size_code << 4) & 0xE0,
|
|
163
|
+
]
|
|
164
|
+
|
|
165
|
+
this.config = config;
|
|
166
|
+
this.sampling_rate = frame.sampling_frequency;
|
|
167
|
+
this.bit_stream_identification = frame.bit_stream_identification;
|
|
168
|
+
this.bit_stream_mode = frame.bit_stream_mode;
|
|
169
|
+
this.low_frequency_effects_channel_on = frame.low_frequency_effects_channel_on;
|
|
170
|
+
this.channel_count = frame.channel_count;
|
|
171
|
+
this.channel_mode = frame.channel_mode;
|
|
172
|
+
this.codec_mimetype = 'ac-3';
|
|
173
|
+
this.original_codec_mimetype = 'ac-3';
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export class EAC3Frame {
|
|
178
|
+
sampling_frequency: number;
|
|
179
|
+
sampling_rate_code: number;
|
|
180
|
+
bit_stream_identification: number;
|
|
181
|
+
low_frequency_effects_channel_on: number;
|
|
182
|
+
num_blks: number;
|
|
183
|
+
frame_size: number;
|
|
184
|
+
channel_count: number;
|
|
185
|
+
channel_mode: number;
|
|
186
|
+
|
|
187
|
+
data: Uint8Array;
|
|
188
|
+
}
|
|
189
|
+
export class EAC3Parser {
|
|
190
|
+
|
|
191
|
+
private readonly TAG: string = "EAC3Parser";
|
|
192
|
+
|
|
193
|
+
private data_: Uint8Array;
|
|
194
|
+
private current_syncword_offset_: number;
|
|
195
|
+
private eof_flag_: boolean;
|
|
196
|
+
private has_last_incomplete_data: boolean;
|
|
197
|
+
|
|
198
|
+
public constructor(data: Uint8Array) {
|
|
199
|
+
this.data_ = data;
|
|
200
|
+
this.current_syncword_offset_ = this.findNextSyncwordOffset(0);
|
|
201
|
+
if (this.eof_flag_) {
|
|
202
|
+
Log.e(this.TAG, `Could not found AC3 syncword until payload end`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private findNextSyncwordOffset(syncword_offset: number): number {
|
|
207
|
+
let i = syncword_offset;
|
|
208
|
+
let data = this.data_;
|
|
209
|
+
|
|
210
|
+
while (true) {
|
|
211
|
+
if (i + 7 >= data.byteLength) {
|
|
212
|
+
this.eof_flag_ = true;
|
|
213
|
+
return data.byteLength;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// search 16-bit 0x0B77 syncword
|
|
217
|
+
let syncword = (data[i + 0] << 8) | (data[i + 1] << 0)
|
|
218
|
+
if (syncword === 0x0B77) {
|
|
219
|
+
return i;
|
|
220
|
+
} else {
|
|
221
|
+
i++;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
public readNextEAC3Frame(): EAC3Frame | null {
|
|
227
|
+
let data = this.data_;
|
|
228
|
+
let eac3_frame: EAC3Frame = null;
|
|
229
|
+
|
|
230
|
+
while (eac3_frame == null) {
|
|
231
|
+
if (this.eof_flag_) {
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
let syncword_offset = this.current_syncword_offset_;
|
|
236
|
+
let offset = syncword_offset;
|
|
237
|
+
|
|
238
|
+
let gb = new ExpGolomb(data.subarray(offset + 2));
|
|
239
|
+
|
|
240
|
+
let stream_type = gb.readBits(2);
|
|
241
|
+
let sub_stream_id = gb.readBits(3);
|
|
242
|
+
let frame_size = (gb.readBits(11) + 1) << 1;
|
|
243
|
+
let sampling_rate_code = gb.readBits(2);
|
|
244
|
+
let sampling_frequency: number | null = null;
|
|
245
|
+
let num_blocks_code: number | null = null;
|
|
246
|
+
if (sampling_rate_code === 0x03) {
|
|
247
|
+
sampling_rate_code = gb.readBits(2);
|
|
248
|
+
sampling_frequency = [24000, 22060, 16000][sampling_rate_code];
|
|
249
|
+
num_blocks_code = 3
|
|
250
|
+
} else {
|
|
251
|
+
sampling_frequency = [48000, 44100, 32000][sampling_rate_code];
|
|
252
|
+
num_blocks_code = gb.readBits(2);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
let channel_mode = gb.readBits(3);
|
|
256
|
+
let low_frequency_effects_channel_on = gb.readBits(1);
|
|
257
|
+
let bit_stream_identification = gb.readBits(5);
|
|
258
|
+
|
|
259
|
+
if (offset + frame_size > this.data_.byteLength) {
|
|
260
|
+
// data not enough for extracting last sample
|
|
261
|
+
this.eof_flag_ = true;
|
|
262
|
+
this.has_last_incomplete_data = true;
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
let next_syncword_offset = this.findNextSyncwordOffset(offset + frame_size);
|
|
267
|
+
this.current_syncword_offset_ = next_syncword_offset;
|
|
268
|
+
|
|
269
|
+
let channel_count = [2, 1, 2, 3, 3, 4, 4, 5][channel_mode] + low_frequency_effects_channel_on;
|
|
270
|
+
|
|
271
|
+
gb.destroy();
|
|
272
|
+
|
|
273
|
+
eac3_frame = new EAC3Frame();
|
|
274
|
+
eac3_frame.sampling_frequency = sampling_frequency;
|
|
275
|
+
eac3_frame.channel_count = channel_count;
|
|
276
|
+
eac3_frame.channel_mode = channel_mode;
|
|
277
|
+
eac3_frame.bit_stream_identification = bit_stream_identification;
|
|
278
|
+
eac3_frame.low_frequency_effects_channel_on = low_frequency_effects_channel_on;
|
|
279
|
+
eac3_frame.frame_size = frame_size;
|
|
280
|
+
eac3_frame.num_blks = [1, 2, 3, 6][num_blocks_code];
|
|
281
|
+
eac3_frame.data = data.subarray(offset, offset + frame_size);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return eac3_frame;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
public hasIncompleteData(): boolean {
|
|
288
|
+
return this.has_last_incomplete_data;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
public getIncompleteData(): Uint8Array {
|
|
292
|
+
if (!this.has_last_incomplete_data) {
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return this.data_.subarray(this.current_syncword_offset_);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
export class EAC3Config {
|
|
300
|
+
|
|
301
|
+
public config: Array<number>;
|
|
302
|
+
public sampling_rate: number;
|
|
303
|
+
public bit_stream_identification: number;
|
|
304
|
+
public num_blks: number;
|
|
305
|
+
public low_frequency_effects_channel_on: number;
|
|
306
|
+
public channel_count: number;
|
|
307
|
+
public channel_mode: number;
|
|
308
|
+
public codec_mimetype: string;
|
|
309
|
+
public original_codec_mimetype: string;
|
|
310
|
+
|
|
311
|
+
public constructor(frame: EAC3Frame) {
|
|
312
|
+
let config: Array<number> = null;
|
|
313
|
+
|
|
314
|
+
const data_rate_sub = Math.floor((frame.frame_size * frame.sampling_frequency) / (frame.num_blks * 16))
|
|
315
|
+
|
|
316
|
+
config = [
|
|
317
|
+
(data_rate_sub & 0x1FE0 >> 5),
|
|
318
|
+
(data_rate_sub & 0x001F << 3), // num_ind_sub = zero
|
|
319
|
+
(frame.sampling_rate_code << 6) | (frame.bit_stream_identification << 1) | (0 << 0),
|
|
320
|
+
(0 << 7) | (0 << 4) | (frame.channel_mode << 1) | (frame.low_frequency_effects_channel_on << 0),
|
|
321
|
+
(0 << 5) | (0 << 1) | (0 << 0)
|
|
322
|
+
];
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
this.config = config;
|
|
326
|
+
this.sampling_rate = frame.sampling_frequency;
|
|
327
|
+
this.bit_stream_identification = frame.bit_stream_identification;
|
|
328
|
+
this.num_blks = frame.num_blks;
|
|
329
|
+
this.low_frequency_effects_channel_on = frame.low_frequency_effects_channel_on;
|
|
330
|
+
this.channel_count = frame.channel_count;
|
|
331
|
+
this.channel_mode = frame.channel_mode;
|
|
332
|
+
this.codec_mimetype = 'ec-3';
|
|
333
|
+
this.original_codec_mimetype = 'ec-3';
|
|
334
|
+
}
|
|
335
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2016 Bilibili. All Rights Reserved.
|
|
3
|
+
*
|
|
4
|
+
* @author zheng qian <xqq@xqq.im>
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import Log from '../utils/logger.js';
|
|
20
|
+
import decodeUTF8 from '../utils/utf8-conv.js';
|
|
21
|
+
import {IllegalStateException} from '../utils/exception.js';
|
|
22
|
+
|
|
23
|
+
let le = (function () {
|
|
24
|
+
let buf = new ArrayBuffer(2);
|
|
25
|
+
(new DataView(buf)).setInt16(0, 256, true); // little-endian write
|
|
26
|
+
return (new Int16Array(buf))[0] === 256; // platform-spec read, if equal then LE
|
|
27
|
+
})();
|
|
28
|
+
|
|
29
|
+
class AMF {
|
|
30
|
+
|
|
31
|
+
static parseScriptData(arrayBuffer, dataOffset, dataSize) {
|
|
32
|
+
let data = {};
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
let name = AMF.parseValue(arrayBuffer, dataOffset, dataSize);
|
|
36
|
+
let value = AMF.parseValue(arrayBuffer, dataOffset + name.size, dataSize - name.size);
|
|
37
|
+
|
|
38
|
+
data[name.data] = value.data;
|
|
39
|
+
} catch (e) {
|
|
40
|
+
Log.e('AMF', e.toString());
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return data;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static parseObject(arrayBuffer, dataOffset, dataSize) {
|
|
47
|
+
if (dataSize < 3) {
|
|
48
|
+
throw new IllegalStateException('Data not enough when parse ScriptDataObject');
|
|
49
|
+
}
|
|
50
|
+
let name = AMF.parseString(arrayBuffer, dataOffset, dataSize);
|
|
51
|
+
let value = AMF.parseValue(arrayBuffer, dataOffset + name.size, dataSize - name.size);
|
|
52
|
+
let isObjectEnd = value.objectEnd;
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
data: {
|
|
56
|
+
name: name.data,
|
|
57
|
+
value: value.data
|
|
58
|
+
},
|
|
59
|
+
size: name.size + value.size,
|
|
60
|
+
objectEnd: isObjectEnd
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static parseVariable(arrayBuffer, dataOffset, dataSize) {
|
|
65
|
+
return AMF.parseObject(arrayBuffer, dataOffset, dataSize);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static parseString(arrayBuffer, dataOffset, dataSize) {
|
|
69
|
+
if (dataSize < 2) {
|
|
70
|
+
throw new IllegalStateException('Data not enough when parse String');
|
|
71
|
+
}
|
|
72
|
+
let v = new DataView(arrayBuffer, dataOffset, dataSize);
|
|
73
|
+
let length = v.getUint16(0, !le);
|
|
74
|
+
|
|
75
|
+
let str;
|
|
76
|
+
if (length > 0) {
|
|
77
|
+
str = decodeUTF8(new Uint8Array(arrayBuffer, dataOffset + 2, length));
|
|
78
|
+
} else {
|
|
79
|
+
str = '';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
data: str,
|
|
84
|
+
size: 2 + length
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static parseLongString(arrayBuffer, dataOffset, dataSize) {
|
|
89
|
+
if (dataSize < 4) {
|
|
90
|
+
throw new IllegalStateException('Data not enough when parse LongString');
|
|
91
|
+
}
|
|
92
|
+
let v = new DataView(arrayBuffer, dataOffset, dataSize);
|
|
93
|
+
let length = v.getUint32(0, !le);
|
|
94
|
+
|
|
95
|
+
let str;
|
|
96
|
+
if (length > 0) {
|
|
97
|
+
str = decodeUTF8(new Uint8Array(arrayBuffer, dataOffset + 4, length));
|
|
98
|
+
} else {
|
|
99
|
+
str = '';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
data: str,
|
|
104
|
+
size: 4 + length
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
static parseDate(arrayBuffer, dataOffset, dataSize) {
|
|
109
|
+
if (dataSize < 10) {
|
|
110
|
+
throw new IllegalStateException('Data size invalid when parse Date');
|
|
111
|
+
}
|
|
112
|
+
let v = new DataView(arrayBuffer, dataOffset, dataSize);
|
|
113
|
+
let timestamp = v.getFloat64(0, !le);
|
|
114
|
+
let localTimeOffset = v.getInt16(8, !le);
|
|
115
|
+
timestamp += localTimeOffset * 60 * 1000; // get UTC time
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
data: new Date(timestamp),
|
|
119
|
+
size: 8 + 2
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
static parseValue(arrayBuffer, dataOffset, dataSize) {
|
|
124
|
+
if (dataSize < 1) {
|
|
125
|
+
throw new IllegalStateException('Data not enough when parse Value');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let v = new DataView(arrayBuffer, dataOffset, dataSize);
|
|
129
|
+
|
|
130
|
+
let offset = 1;
|
|
131
|
+
let type = v.getUint8(0);
|
|
132
|
+
let value;
|
|
133
|
+
let objectEnd = false;
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
switch (type) {
|
|
137
|
+
case 0: // Number(Double) type
|
|
138
|
+
value = v.getFloat64(1, !le);
|
|
139
|
+
offset += 8;
|
|
140
|
+
break;
|
|
141
|
+
case 1: { // Boolean type
|
|
142
|
+
let b = v.getUint8(1);
|
|
143
|
+
value = b ? true : false;
|
|
144
|
+
offset += 1;
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
case 2: { // String type
|
|
148
|
+
let amfstr = AMF.parseString(arrayBuffer, dataOffset + 1, dataSize - 1);
|
|
149
|
+
value = amfstr.data;
|
|
150
|
+
offset += amfstr.size;
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
case 3: { // Object(s) type
|
|
154
|
+
value = {};
|
|
155
|
+
let terminal = 0; // workaround for malformed Objects which has missing ScriptDataObjectEnd
|
|
156
|
+
if ((v.getUint32(dataSize - 4, !le) & 0x00FFFFFF) === 9) {
|
|
157
|
+
terminal = 3;
|
|
158
|
+
}
|
|
159
|
+
while (offset < dataSize - 4) { // 4 === type(UI8) + ScriptDataObjectEnd(UI24)
|
|
160
|
+
let amfobj = AMF.parseObject(arrayBuffer, dataOffset + offset, dataSize - offset - terminal);
|
|
161
|
+
if (amfobj.objectEnd)
|
|
162
|
+
break;
|
|
163
|
+
value[amfobj.data.name] = amfobj.data.value;
|
|
164
|
+
offset += amfobj.size;
|
|
165
|
+
}
|
|
166
|
+
if (offset <= dataSize - 3) {
|
|
167
|
+
let marker = v.getUint32(offset - 1, !le) & 0x00FFFFFF;
|
|
168
|
+
if (marker === 9) {
|
|
169
|
+
offset += 3;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
case 8: { // ECMA array type (Mixed array)
|
|
175
|
+
value = {};
|
|
176
|
+
offset += 4; // ECMAArrayLength(UI32)
|
|
177
|
+
let terminal = 0; // workaround for malformed MixedArrays which has missing ScriptDataObjectEnd
|
|
178
|
+
if ((v.getUint32(dataSize - 4, !le) & 0x00FFFFFF) === 9) {
|
|
179
|
+
terminal = 3;
|
|
180
|
+
}
|
|
181
|
+
while (offset < dataSize - 8) { // 8 === type(UI8) + ECMAArrayLength(UI32) + ScriptDataVariableEnd(UI24)
|
|
182
|
+
let amfvar = AMF.parseVariable(arrayBuffer, dataOffset + offset, dataSize - offset - terminal);
|
|
183
|
+
if (amfvar.objectEnd)
|
|
184
|
+
break;
|
|
185
|
+
value[amfvar.data.name] = amfvar.data.value;
|
|
186
|
+
offset += amfvar.size;
|
|
187
|
+
}
|
|
188
|
+
if (offset <= dataSize - 3) {
|
|
189
|
+
let marker = v.getUint32(offset - 1, !le) & 0x00FFFFFF;
|
|
190
|
+
if (marker === 9) {
|
|
191
|
+
offset += 3;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
case 9: // ScriptDataObjectEnd
|
|
197
|
+
value = undefined;
|
|
198
|
+
offset = 1;
|
|
199
|
+
objectEnd = true;
|
|
200
|
+
break;
|
|
201
|
+
case 10: { // Strict array type
|
|
202
|
+
// ScriptDataValue[n]. NOTE: according to video_file_format_spec_v10_1.pdf
|
|
203
|
+
value = [];
|
|
204
|
+
let strictArrayLength = v.getUint32(1, !le);
|
|
205
|
+
offset += 4;
|
|
206
|
+
for (let i = 0; i < strictArrayLength; i++) {
|
|
207
|
+
let val = AMF.parseValue(arrayBuffer, dataOffset + offset, dataSize - offset);
|
|
208
|
+
value.push(val.data);
|
|
209
|
+
offset += val.size;
|
|
210
|
+
}
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
case 11: { // Date type
|
|
214
|
+
let date = AMF.parseDate(arrayBuffer, dataOffset + 1, dataSize - 1);
|
|
215
|
+
value = date.data;
|
|
216
|
+
offset += date.size;
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
case 12: { // Long string type
|
|
220
|
+
let amfLongStr = AMF.parseString(arrayBuffer, dataOffset + 1, dataSize - 1);
|
|
221
|
+
value = amfLongStr.data;
|
|
222
|
+
offset += amfLongStr.size;
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
default:
|
|
226
|
+
// ignore and skip
|
|
227
|
+
offset = dataSize;
|
|
228
|
+
Log.w('AMF', 'Unsupported AMF value type ' + type);
|
|
229
|
+
}
|
|
230
|
+
} catch (e) {
|
|
231
|
+
Log.e('AMF', e.toString());
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
data: value,
|
|
236
|
+
size: offset,
|
|
237
|
+
objectEnd: objectEnd
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export default AMF;
|