@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.
Files changed (121) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +158 -0
  3. package/README_ja.md +153 -0
  4. package/README_zh.md +157 -0
  5. package/d.ts/mpegts.d.ts +524 -0
  6. package/d.ts/src/core/mse-events.d.ts +9 -0
  7. package/d.ts/src/core/transmuxing-events.d.ts +24 -0
  8. package/d.ts/src/demux/aac.d.ts +44 -0
  9. package/d.ts/src/demux/ac3.d.ts +70 -0
  10. package/d.ts/src/demux/av1-parser.d.ts +77 -0
  11. package/d.ts/src/demux/av1.d.ts +11 -0
  12. package/d.ts/src/demux/base-demuxer.d.ts +55 -0
  13. package/d.ts/src/demux/h264.d.ts +40 -0
  14. package/d.ts/src/demux/h265.d.ts +65 -0
  15. package/d.ts/src/demux/klv.d.ts +17 -0
  16. package/d.ts/src/demux/mp3.d.ts +6 -0
  17. package/d.ts/src/demux/mpeg4-audio.d.ts +28 -0
  18. package/d.ts/src/demux/pat-pmt-pes.d.ts +106 -0
  19. package/d.ts/src/demux/patpmt.d.ts +40 -0
  20. package/d.ts/src/demux/pes-private-data.d.ts +14 -0
  21. package/d.ts/src/demux/pgs-data.d.ts +9 -0
  22. package/d.ts/src/demux/scte35.d.ts +250 -0
  23. package/d.ts/src/demux/sei.d.ts +8 -0
  24. package/d.ts/src/demux/smpte2038.d.ts +22 -0
  25. package/d.ts/src/demux/ts-demuxer.d.ts +124 -0
  26. package/d.ts/src/player/live-latency-chaser.d.ts +10 -0
  27. package/d.ts/src/player/live-latency-synchronizer.d.ts +10 -0
  28. package/d.ts/src/player/loading-controller.d.ts +19 -0
  29. package/d.ts/src/player/mse-player.d.ts +30 -0
  30. package/d.ts/src/player/player-engine-dedicated-thread-worker.d.ts +2 -0
  31. package/d.ts/src/player/player-engine-dedicated-thread.d.ts +48 -0
  32. package/d.ts/src/player/player-engine-main-thread.d.ts +50 -0
  33. package/d.ts/src/player/player-engine-worker-cmd-def.d.ts +25 -0
  34. package/d.ts/src/player/player-engine-worker-msg-def.d.ts +54 -0
  35. package/d.ts/src/player/player-engine-worker.d.ts +2 -0
  36. package/d.ts/src/player/player-engine.d.ts +16 -0
  37. package/d.ts/src/player/player-events.d.ts +21 -0
  38. package/d.ts/src/player/seeking-handler.d.ts +22 -0
  39. package/d.ts/src/player/startup-stall-jumper.d.ts +14 -0
  40. package/d.ts/src/utils/typedarray-equality.d.ts +2 -0
  41. package/dist/mpegts.js +3 -0
  42. package/dist/mpegts.js.LICENSE.txt +7 -0
  43. package/dist/mpegts.js.map +1 -0
  44. package/package.json +53 -0
  45. package/src/config.js +67 -0
  46. package/src/core/features.js +88 -0
  47. package/src/core/media-info.js +127 -0
  48. package/src/core/media-segment-info.js +230 -0
  49. package/src/core/mse-controller.js +599 -0
  50. package/src/core/mse-events.ts +28 -0
  51. package/src/core/transmuxer.js +346 -0
  52. package/src/core/transmuxing-controller.js +628 -0
  53. package/src/core/transmuxing-events.ts +43 -0
  54. package/src/core/transmuxing-worker.js +286 -0
  55. package/src/demux/aac.ts +397 -0
  56. package/src/demux/ac3.ts +335 -0
  57. package/src/demux/amf-parser.js +243 -0
  58. package/src/demux/av1-parser.ts +629 -0
  59. package/src/demux/av1.ts +103 -0
  60. package/src/demux/base-demuxer.ts +69 -0
  61. package/src/demux/demux-errors.js +26 -0
  62. package/src/demux/exp-golomb.js +116 -0
  63. package/src/demux/flv-demuxer.js +1854 -0
  64. package/src/demux/h264.ts +187 -0
  65. package/src/demux/h265-parser.js +501 -0
  66. package/src/demux/h265.ts +214 -0
  67. package/src/demux/klv.ts +40 -0
  68. package/src/demux/mp3.ts +7 -0
  69. package/src/demux/mpeg4-audio.ts +45 -0
  70. package/src/demux/pat-pmt-pes.ts +132 -0
  71. package/src/demux/pes-private-data.ts +16 -0
  72. package/src/demux/pgs-data.ts +11 -0
  73. package/src/demux/scte35.ts +723 -0
  74. package/src/demux/sei.ts +99 -0
  75. package/src/demux/smpte2038.ts +89 -0
  76. package/src/demux/sps-parser.js +298 -0
  77. package/src/demux/ts-demuxer.ts +2405 -0
  78. package/src/index.js +4 -0
  79. package/src/io/fetch-stream-loader.js +266 -0
  80. package/src/io/io-controller.js +647 -0
  81. package/src/io/loader.js +134 -0
  82. package/src/io/param-seek-handler.js +85 -0
  83. package/src/io/range-seek-handler.js +52 -0
  84. package/src/io/speed-sampler.js +93 -0
  85. package/src/io/websocket-loader.js +151 -0
  86. package/src/io/xhr-moz-chunked-loader.js +211 -0
  87. package/src/io/xhr-msstream-loader.js +307 -0
  88. package/src/io/xhr-range-loader.js +366 -0
  89. package/src/mpegts.js +95 -0
  90. package/src/player/live-latency-chaser.ts +66 -0
  91. package/src/player/live-latency-synchronizer.ts +79 -0
  92. package/src/player/loading-controller.ts +142 -0
  93. package/src/player/mse-player.ts +150 -0
  94. package/src/player/native-player.js +262 -0
  95. package/src/player/player-engine-dedicated-thread.ts +479 -0
  96. package/src/player/player-engine-main-thread.ts +463 -0
  97. package/src/player/player-engine-worker-cmd-def.ts +62 -0
  98. package/src/player/player-engine-worker-msg-def.ts +102 -0
  99. package/src/player/player-engine-worker.ts +370 -0
  100. package/src/player/player-engine.ts +35 -0
  101. package/src/player/player-errors.js +39 -0
  102. package/src/player/player-events.ts +40 -0
  103. package/src/player/seeking-handler.ts +205 -0
  104. package/src/player/startup-stall-jumper.ts +86 -0
  105. package/src/remux/aac-silent.js +56 -0
  106. package/src/remux/mp4-generator.js +866 -0
  107. package/src/remux/mp4-remuxer.js +778 -0
  108. package/src/utils/browser.js +128 -0
  109. package/src/utils/exception.js +73 -0
  110. package/src/utils/logger.js +140 -0
  111. package/src/utils/logging-control.js +165 -0
  112. package/src/utils/polyfill.js +68 -0
  113. package/src/utils/typedarray-equality.ts +69 -0
  114. package/src/utils/utf8-conv.js +84 -0
  115. package/src/utils/webworkify-webpack.js +202 -0
  116. package/tsconfig.json +16 -0
  117. package/tslint.json +1 -0
  118. package/types/index.d.ts +3 -0
  119. package/types/test-flv.ts +8 -0
  120. package/types/tsconfig.json +24 -0
  121. package/webpack.config.js +55 -0
@@ -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;