@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,1854 @@
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 AMF from './amf-parser.js';
21
+ import SPSParser from './sps-parser.js';
22
+ import DemuxErrors from './demux-errors.js';
23
+ import MediaInfo from '../core/media-info.js';
24
+ import {IllegalStateException} from '../utils/exception.js';
25
+ import H265Parser from './h265-parser.js';
26
+ import buffersAreEqual from '../utils/typedarray-equality.ts';
27
+ import AV1OBUParser from './av1-parser.ts';
28
+ import ExpGolomb from './exp-golomb.js';
29
+ import { parseSEI } from './sei';
30
+
31
+ function Swap16(src) {
32
+ return (((src >>> 8) & 0xFF) |
33
+ ((src & 0xFF) << 8));
34
+ }
35
+
36
+ function Swap32(src) {
37
+ return (((src & 0xFF000000) >>> 24) |
38
+ ((src & 0x00FF0000) >>> 8) |
39
+ ((src & 0x0000FF00) << 8) |
40
+ ((src & 0x000000FF) << 24));
41
+ }
42
+
43
+ function ReadBig32(array, index) {
44
+ return ((array[index] << 24) |
45
+ (array[index + 1] << 16) |
46
+ (array[index + 2] << 8) |
47
+ (array[index + 3]));
48
+ }
49
+
50
+
51
+ class FLVDemuxer {
52
+
53
+ constructor(probeData, config) {
54
+ this.TAG = 'FLVDemuxer';
55
+
56
+ this._config = config;
57
+
58
+ this._onError = null;
59
+ this._onMediaInfo = null;
60
+ this._onMetaDataArrived = null;
61
+ this._onScriptDataArrived = null;
62
+ this._onTrackMetadata = null;
63
+ this._onDataAvailable = null;
64
+ this._onSeiArrived = null;
65
+
66
+ this._dataOffset = probeData.dataOffset;
67
+ this._firstParse = true;
68
+ this._dispatch = false;
69
+
70
+ this._hasAudio = probeData.hasAudioTrack;
71
+ this._hasVideo = probeData.hasVideoTrack;
72
+
73
+ this._hasAudioFlagOverrided = false;
74
+ this._hasVideoFlagOverrided = false;
75
+
76
+ this._audioInitialMetadataDispatched = false;
77
+ this._videoInitialMetadataDispatched = false;
78
+
79
+ this._mediaInfo = new MediaInfo();
80
+ this._mediaInfo.hasAudio = this._hasAudio;
81
+ this._mediaInfo.hasVideo = this._hasVideo;
82
+ this._metadata = null;
83
+ this._audioMetadata = null;
84
+ this._videoMetadata = null;
85
+
86
+ this._naluLengthSize = 4;
87
+ this._timestampBase = 0; // int32, in milliseconds
88
+ this._timescale = 1000;
89
+ this._duration = 0; // int32, in milliseconds
90
+ this._durationOverrided = false;
91
+ this._referenceFrameRate = {
92
+ fixed: true,
93
+ fps: 23.976,
94
+ fps_num: 23976,
95
+ fps_den: 1000
96
+ };
97
+
98
+ this._flvSoundRateTable = [5500, 11025, 22050, 44100, 48000];
99
+
100
+ this._mpegSamplingRates = [
101
+ 96000, 88200, 64000, 48000, 44100, 32000,
102
+ 24000, 22050, 16000, 12000, 11025, 8000, 7350
103
+ ];
104
+
105
+ this._mpegAudioV10SampleRateTable = [44100, 48000, 32000, 0];
106
+ this._mpegAudioV20SampleRateTable = [22050, 24000, 16000, 0];
107
+ this._mpegAudioV25SampleRateTable = [11025, 12000, 8000, 0];
108
+
109
+ this._mpegAudioL1BitRateTable = [0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1];
110
+ this._mpegAudioL2BitRateTable = [0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1];
111
+ this._mpegAudioL3BitRateTable = [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1];
112
+
113
+ this._videoTrack = {type: 'video', id: 1, sequenceNumber: 0, samples: [], length: 0};
114
+ this._audioTrack = {type: 'audio', id: 2, sequenceNumber: 0, samples: [], length: 0};
115
+
116
+ this._littleEndian = (function () {
117
+ let buf = new ArrayBuffer(2);
118
+ (new DataView(buf)).setInt16(0, 256, true); // little-endian write
119
+ return (new Int16Array(buf))[0] === 256; // platform-spec read, if equal then LE
120
+ })();
121
+ }
122
+
123
+ destroy() {
124
+ this._mediaInfo = null;
125
+ this._metadata = null;
126
+ this._audioMetadata = null;
127
+ this._videoMetadata = null;
128
+ this._videoTrack = null;
129
+ this._audioTrack = null;
130
+
131
+ this._onError = null;
132
+ this._onMediaInfo = null;
133
+ this._onMetaDataArrived = null;
134
+ this._onScriptDataArrived = null;
135
+ this._onTrackMetadata = null;
136
+ this._onDataAvailable = null;
137
+ this._onSeiArrived = null;
138
+ }
139
+
140
+ static probe(buffer) {
141
+ let data = new Uint8Array(buffer);
142
+ if (data.byteLength < 9) {
143
+ return {needMoreData: true};
144
+ }
145
+
146
+ let mismatch = {match: false};
147
+
148
+ if (data[0] !== 0x46 || data[1] !== 0x4C || data[2] !== 0x56 || data[3] !== 0x01) {
149
+ return mismatch;
150
+ }
151
+
152
+ let hasAudio = ((data[4] & 4) >>> 2) !== 0;
153
+ let hasVideo = (data[4] & 1) !== 0;
154
+
155
+ let offset = ReadBig32(data, 5);
156
+
157
+ if (offset < 9) {
158
+ return mismatch;
159
+ }
160
+
161
+ return {
162
+ match: true,
163
+ consumed: offset,
164
+ dataOffset: offset,
165
+ hasAudioTrack: hasAudio,
166
+ hasVideoTrack: hasVideo
167
+ };
168
+ }
169
+
170
+ bindDataSource(loader) {
171
+ loader.onDataArrival = this.parseChunks.bind(this);
172
+ return this;
173
+ }
174
+
175
+ // prototype: function(type: string, metadata: any): void
176
+ get onTrackMetadata() {
177
+ return this._onTrackMetadata;
178
+ }
179
+
180
+ set onTrackMetadata(callback) {
181
+ this._onTrackMetadata = callback;
182
+ }
183
+
184
+ // prototype: function(mediaInfo: MediaInfo): void
185
+ get onMediaInfo() {
186
+ return this._onMediaInfo;
187
+ }
188
+
189
+ set onMediaInfo(callback) {
190
+ this._onMediaInfo = callback;
191
+ }
192
+
193
+ get onMetaDataArrived() {
194
+ return this._onMetaDataArrived;
195
+ }
196
+
197
+ set onMetaDataArrived(callback) {
198
+ this._onMetaDataArrived = callback;
199
+ }
200
+
201
+ get onScriptDataArrived() {
202
+ return this._onScriptDataArrived;
203
+ }
204
+
205
+ set onScriptDataArrived(callback) {
206
+ this._onScriptDataArrived = callback;
207
+ }
208
+
209
+ get onSeiArrived() {
210
+ return this._onSeiArrived
211
+ }
212
+
213
+ set onSeiArrived(callback) {
214
+ this._onSeiArrived = callback;
215
+ }
216
+
217
+ // prototype: function(type: number, info: string): void
218
+ get onError() {
219
+ return this._onError;
220
+ }
221
+
222
+ set onError(callback) {
223
+ this._onError = callback;
224
+ }
225
+
226
+ // prototype: function(videoTrack: any, audioTrack: any): void
227
+ get onDataAvailable() {
228
+ return this._onDataAvailable;
229
+ }
230
+
231
+ set onDataAvailable(callback) {
232
+ this._onDataAvailable = callback;
233
+ }
234
+
235
+ // timestamp base for output samples, must be in milliseconds
236
+ get timestampBase() {
237
+ return this._timestampBase;
238
+ }
239
+
240
+ set timestampBase(base) {
241
+ this._timestampBase = base;
242
+ }
243
+
244
+ get overridedDuration() {
245
+ return this._duration;
246
+ }
247
+
248
+ // Force-override media duration. Must be in milliseconds, int32
249
+ set overridedDuration(duration) {
250
+ this._durationOverrided = true;
251
+ this._duration = duration;
252
+ this._mediaInfo.duration = duration;
253
+ }
254
+
255
+ // Force-override audio track present flag, boolean
256
+ set overridedHasAudio(hasAudio) {
257
+ this._hasAudioFlagOverrided = true;
258
+ this._hasAudio = hasAudio;
259
+ this._mediaInfo.hasAudio = hasAudio;
260
+ }
261
+
262
+ // Force-override video track present flag, boolean
263
+ set overridedHasVideo(hasVideo) {
264
+ this._hasVideoFlagOverrided = true;
265
+ this._hasVideo = hasVideo;
266
+ this._mediaInfo.hasVideo = hasVideo;
267
+ }
268
+
269
+ resetMediaInfo() {
270
+ this._mediaInfo = new MediaInfo();
271
+ }
272
+
273
+ _isInitialMetadataDispatched() {
274
+ if (this._hasAudio && this._hasVideo) { // both audio & video
275
+ return this._audioInitialMetadataDispatched && this._videoInitialMetadataDispatched;
276
+ }
277
+ if (this._hasAudio && !this._hasVideo) { // audio only
278
+ return this._audioInitialMetadataDispatched;
279
+ }
280
+ if (!this._hasAudio && this._hasVideo) { // video only
281
+ return this._videoInitialMetadataDispatched;
282
+ }
283
+ return false;
284
+ }
285
+
286
+ // function parseChunks(chunk: ArrayBuffer, byteStart: number): number;
287
+ parseChunks(chunk, byteStart) {
288
+ if (!this._onError || !this._onMediaInfo || !this._onTrackMetadata || !this._onDataAvailable) {
289
+ throw new IllegalStateException('Flv: onError & onMediaInfo & onTrackMetadata & onDataAvailable callback must be specified');
290
+ }
291
+
292
+ let offset = 0;
293
+ let le = this._littleEndian;
294
+
295
+ if (byteStart === 0) { // buffer with FLV header
296
+ if (chunk.byteLength > 13) {
297
+ let probeData = FLVDemuxer.probe(chunk);
298
+ offset = probeData.dataOffset;
299
+ } else {
300
+ return 0;
301
+ }
302
+ }
303
+
304
+ if (this._firstParse) { // handle PreviousTagSize0 before Tag1
305
+ this._firstParse = false;
306
+ if (byteStart + offset !== this._dataOffset) {
307
+ Log.w(this.TAG, 'First time parsing but chunk byteStart invalid!');
308
+ }
309
+
310
+ let v = new DataView(chunk, offset);
311
+ let prevTagSize0 = v.getUint32(0, !le);
312
+ if (prevTagSize0 !== 0) {
313
+ Log.w(this.TAG, 'PrevTagSize0 !== 0 !!!');
314
+ }
315
+ offset += 4;
316
+ }
317
+
318
+ while (offset < chunk.byteLength) {
319
+ this._dispatch = true;
320
+
321
+ let v = new DataView(chunk, offset);
322
+
323
+ if (offset + 11 + 4 > chunk.byteLength) {
324
+ // data not enough for parsing an flv tag
325
+ break;
326
+ }
327
+
328
+ let tagType = v.getUint8(0);
329
+ let dataSize = v.getUint32(0, !le) & 0x00FFFFFF;
330
+
331
+ if (offset + 11 + dataSize + 4 > chunk.byteLength) {
332
+ // data not enough for parsing actual data body
333
+ break;
334
+ }
335
+
336
+ if (tagType !== 8 && tagType !== 9 && tagType !== 18) {
337
+ Log.w(this.TAG, `Unsupported tag type ${tagType}, skipped`);
338
+ // consume the whole tag (skip it)
339
+ offset += 11 + dataSize + 4;
340
+ continue;
341
+ }
342
+
343
+ let ts2 = v.getUint8(4);
344
+ let ts1 = v.getUint8(5);
345
+ let ts0 = v.getUint8(6);
346
+ let ts3 = v.getUint8(7);
347
+
348
+ let timestamp = ts0 | (ts1 << 8) | (ts2 << 16) | (ts3 << 24);
349
+
350
+ let streamId = v.getUint32(7, !le) & 0x00FFFFFF;
351
+ if (streamId !== 0) {
352
+ Log.w(this.TAG, 'Meet tag which has StreamID != 0!');
353
+ }
354
+
355
+ let dataOffset = offset + 11;
356
+
357
+ switch (tagType) {
358
+ case 8: // Audio
359
+ this._parseAudioData(chunk, dataOffset, dataSize, timestamp);
360
+ break;
361
+ case 9: // Video
362
+ this._parseVideoData(chunk, dataOffset, dataSize, timestamp, byteStart + offset);
363
+ break;
364
+ case 18: // ScriptDataObject
365
+ this._parseScriptData(chunk, dataOffset, dataSize);
366
+ break;
367
+ }
368
+
369
+ let prevTagSize = v.getUint32(11 + dataSize, !le);
370
+ if (prevTagSize !== 11 + dataSize) {
371
+ Log.w(this.TAG, `Invalid PrevTagSize ${prevTagSize}`);
372
+ }
373
+
374
+ offset += 11 + dataSize + 4; // tagBody + dataSize + prevTagSize
375
+ }
376
+
377
+ // dispatch parsed frames to consumer (typically, the remuxer)
378
+ if (this._isInitialMetadataDispatched()) {
379
+ if (this._dispatch && (this._audioTrack.length || this._videoTrack.length)) {
380
+ this._onDataAvailable(this._audioTrack, this._videoTrack);
381
+ }
382
+ }
383
+
384
+ return offset; // consumed bytes, just equals latest offset index
385
+ }
386
+
387
+ _parseScriptData(arrayBuffer, dataOffset, dataSize) {
388
+ let scriptData = AMF.parseScriptData(arrayBuffer, dataOffset, dataSize);
389
+
390
+ if (scriptData.hasOwnProperty('onMetaData')) {
391
+ if (scriptData.onMetaData == null || typeof scriptData.onMetaData !== 'object') {
392
+ Log.w(this.TAG, 'Invalid onMetaData structure!');
393
+ return;
394
+ }
395
+ if (this._metadata) {
396
+ Log.w(this.TAG, 'Found another onMetaData tag!');
397
+ }
398
+ this._metadata = scriptData;
399
+ let onMetaData = this._metadata.onMetaData;
400
+
401
+ if (this._onMetaDataArrived) {
402
+ this._onMetaDataArrived(Object.assign({}, onMetaData));
403
+ }
404
+
405
+ if (typeof onMetaData.hasAudio === 'boolean') { // hasAudio
406
+ if (this._hasAudioFlagOverrided === false) {
407
+ this._hasAudio = onMetaData.hasAudio;
408
+ this._mediaInfo.hasAudio = this._hasAudio;
409
+ }
410
+ }
411
+ if (typeof onMetaData.hasVideo === 'boolean') { // hasVideo
412
+ if (this._hasVideoFlagOverrided === false) {
413
+ this._hasVideo = onMetaData.hasVideo;
414
+ this._mediaInfo.hasVideo = this._hasVideo;
415
+ }
416
+ }
417
+ if (typeof onMetaData.audiodatarate === 'number') { // audiodatarate
418
+ this._mediaInfo.audioDataRate = onMetaData.audiodatarate;
419
+ }
420
+ if (typeof onMetaData.videodatarate === 'number') { // videodatarate
421
+ this._mediaInfo.videoDataRate = onMetaData.videodatarate;
422
+ }
423
+ if (typeof onMetaData.width === 'number') { // width
424
+ this._mediaInfo.width = onMetaData.width;
425
+ }
426
+ if (typeof onMetaData.height === 'number') { // height
427
+ this._mediaInfo.height = onMetaData.height;
428
+ }
429
+ if (typeof onMetaData.duration === 'number') { // duration
430
+ if (!this._durationOverrided) {
431
+ let duration = Math.floor(onMetaData.duration * this._timescale);
432
+ this._duration = duration;
433
+ this._mediaInfo.duration = duration;
434
+ }
435
+ } else {
436
+ this._mediaInfo.duration = 0;
437
+ }
438
+ if (typeof onMetaData.framerate === 'number') { // framerate
439
+ let fps_num = Math.floor(onMetaData.framerate * 1000);
440
+ if (fps_num > 0) {
441
+ let fps = fps_num / 1000;
442
+ this._referenceFrameRate.fixed = true;
443
+ this._referenceFrameRate.fps = fps;
444
+ this._referenceFrameRate.fps_num = fps_num;
445
+ this._referenceFrameRate.fps_den = 1000;
446
+ this._mediaInfo.fps = fps;
447
+ }
448
+ }
449
+ if (typeof onMetaData.keyframes === 'object') { // keyframes
450
+ this._mediaInfo.hasKeyframesIndex = true;
451
+ let keyframes = onMetaData.keyframes;
452
+ this._mediaInfo.keyframesIndex = this._parseKeyframesIndex(keyframes);
453
+ onMetaData.keyframes = null; // keyframes has been extracted, remove it
454
+ } else {
455
+ this._mediaInfo.hasKeyframesIndex = false;
456
+ }
457
+ this._dispatch = false;
458
+ this._mediaInfo.metadata = onMetaData;
459
+ Log.v(this.TAG, 'Parsed onMetaData');
460
+ if (this._mediaInfo.isComplete()) {
461
+ this._onMediaInfo(this._mediaInfo);
462
+ }
463
+ }
464
+
465
+ if (Object.keys(scriptData).length > 0) {
466
+ if (this._onScriptDataArrived) {
467
+ this._onScriptDataArrived(Object.assign({}, scriptData));
468
+ }
469
+ }
470
+ }
471
+
472
+ _parseSEIPayload(data, pts, codec) {
473
+ let sei_data = parseSEI(data, pts, codec);
474
+
475
+ if (sei_data && typeof this._onSeiArrived === 'function') {
476
+ this._onSeiArrived(sei_data);
477
+ }
478
+ }
479
+
480
+ _parseKeyframesIndex(keyframes) {
481
+ let times = [];
482
+ let filepositions = [];
483
+
484
+ // ignore first keyframe which is actually AVC/HEVC Sequence Header (AVCDecoderConfigurationRecord or HEVCDecoderConfigurationRecord)
485
+ for (let i = 1; i < keyframes.times.length; i++) {
486
+ let time = this._timestampBase + Math.floor(keyframes.times[i] * 1000);
487
+ times.push(time);
488
+ filepositions.push(keyframes.filepositions[i]);
489
+ }
490
+
491
+ return {
492
+ times: times,
493
+ filepositions: filepositions
494
+ };
495
+ }
496
+
497
+ _parseAudioData(arrayBuffer, dataOffset, dataSize, tagTimestamp) {
498
+ if (dataSize <= 1) {
499
+ Log.w(this.TAG, 'Flv: Invalid audio packet, missing SoundData payload!');
500
+ return;
501
+ }
502
+
503
+ if (this._hasAudioFlagOverrided === true && this._hasAudio === false) {
504
+ // If hasAudio: false indicated explicitly in MediaDataSource,
505
+ // Ignore all the audio packets
506
+ return;
507
+ }
508
+
509
+ let le = this._littleEndian;
510
+ let v = new DataView(arrayBuffer, dataOffset, dataSize);
511
+
512
+ let soundSpec = v.getUint8(0);
513
+
514
+ let soundFormat = soundSpec >>> 4;
515
+ if (soundFormat === 9) { // Enhanced FLV
516
+ if (dataSize <= 5) {
517
+ Log.w(this.TAG, 'Flv: Invalid audio packet, missing AudioFourCC in Ehnanced FLV payload!');
518
+ return;
519
+ }
520
+ let packetType = soundSpec & 0x0F;
521
+ let fourcc = String.fromCharCode(... (new Uint8Array(arrayBuffer, dataOffset, dataSize)).slice(1, 5));
522
+
523
+ switch(fourcc){
524
+ case 'Opus':
525
+ this._parseOpusAudioPacket(arrayBuffer, dataOffset + 5, dataSize - 5, tagTimestamp, packetType);
526
+ break;
527
+ case 'fLaC':
528
+ this._parseFlacAudioPacket(arrayBuffer, dataOffset + 5, dataSize - 5, tagTimestamp, packetType);
529
+ break;
530
+ default:
531
+ this._onError(DemuxErrors.CODEC_UNSUPPORTED, 'Flv: Unsupported audio codec: ' + fourcc);
532
+ }
533
+
534
+ return;
535
+ }
536
+ // Legacy FLV
537
+
538
+ if (soundFormat !== 2 && soundFormat !== 3 && soundFormat !== 10) { // PCM or MP3 or AAC
539
+ this._onError(DemuxErrors.CODEC_UNSUPPORTED, 'Flv: Unsupported audio codec idx: ' + soundFormat);
540
+ return;
541
+ }
542
+
543
+ let soundRate = 0;
544
+ let soundRateIndex = (soundSpec & 12) >>> 2;
545
+ if (soundRateIndex >= 0 && soundRateIndex <= 4) {
546
+ soundRate = this._flvSoundRateTable[soundRateIndex];
547
+ } else {
548
+ this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: Invalid audio sample rate idx: ' + soundRateIndex);
549
+ return;
550
+ }
551
+
552
+ let soundSize = (soundSpec & 2) >>> 1; // unused
553
+ let soundType = (soundSpec & 1);
554
+
555
+ let meta = this._audioMetadata;
556
+ let track = this._audioTrack;
557
+
558
+ if (!meta) {
559
+ if (this._hasAudio === false && this._hasAudioFlagOverrided === false) {
560
+ this._hasAudio = true;
561
+ this._mediaInfo.hasAudio = true;
562
+ }
563
+
564
+ // initial metadata
565
+ meta = this._audioMetadata = {};
566
+ meta.type = 'audio';
567
+ meta.id = track.id;
568
+ meta.timescale = this._timescale;
569
+ meta.duration = this._duration;
570
+ meta.audioSampleRate = soundRate;
571
+ meta.channelCount = (soundType === 0 ? 1 : 2);
572
+ }
573
+
574
+ if (soundFormat === 10) { // AAC
575
+ let aacData = this._parseAACAudioData(arrayBuffer, dataOffset + 1, dataSize - 1);
576
+ if (aacData == undefined) {
577
+ return;
578
+ }
579
+
580
+ if (aacData.packetType === 0) { // AAC sequence header (AudioSpecificConfig)
581
+ if (meta.config) {
582
+ if (buffersAreEqual(aacData.data.config, meta.config)) {
583
+ // If AudioSpecificConfig is not changed, ignore it to avoid generating initialization segment repeatedly
584
+ return;
585
+ } else {
586
+ Log.w(this.TAG, 'AudioSpecificConfig has been changed, re-generate initialization segment');
587
+ }
588
+ }
589
+ let misc = aacData.data;
590
+ meta.audioSampleRate = misc.samplingRate;
591
+ meta.channelCount = misc.channelCount;
592
+ meta.codec = misc.codec;
593
+ meta.originalCodec = misc.originalCodec;
594
+ meta.config = misc.config;
595
+ // The decode result of an aac sample is 1024 PCM samples
596
+ meta.refSampleDuration = 1024 / meta.audioSampleRate * meta.timescale;
597
+ Log.v(this.TAG, 'Parsed AudioSpecificConfig');
598
+
599
+ if (this._isInitialMetadataDispatched()) {
600
+ // Non-initial metadata, force dispatch (or flush) parsed frames to remuxer
601
+ if (this._dispatch && (this._audioTrack.length || this._videoTrack.length)) {
602
+ this._onDataAvailable(this._audioTrack, this._videoTrack);
603
+ }
604
+ } else {
605
+ this._audioInitialMetadataDispatched = true;
606
+ }
607
+ // then notify new metadata
608
+ this._dispatch = false;
609
+ this._onTrackMetadata('audio', meta);
610
+
611
+ let mi = this._mediaInfo;
612
+ mi.audioCodec = meta.originalCodec;
613
+ mi.audioSampleRate = meta.audioSampleRate;
614
+ mi.audioChannelCount = meta.channelCount;
615
+ if (mi.hasVideo) {
616
+ if (mi.videoCodec != null) {
617
+ mi.mimeType = 'video/x-flv; codecs="' + mi.videoCodec + ',' + mi.audioCodec + '"';
618
+ }
619
+ } else {
620
+ mi.mimeType = 'video/x-flv; codecs="' + mi.audioCodec + '"';
621
+ }
622
+ if (mi.isComplete()) {
623
+ this._onMediaInfo(mi);
624
+ }
625
+ } else if (aacData.packetType === 1) { // AAC raw frame data
626
+ let dts = this._timestampBase + tagTimestamp;
627
+ let aacSample = {unit: aacData.data, length: aacData.data.byteLength, dts: dts, pts: dts};
628
+ track.samples.push(aacSample);
629
+ track.length += aacData.data.length;
630
+ } else {
631
+ Log.e(this.TAG, `Flv: Unsupported AAC data type ${aacData.packetType}`);
632
+ }
633
+ } else if (soundFormat === 2) { // MP3
634
+ if (!meta.codec) {
635
+ // We need metadata for mp3 audio track, extract info from frame header
636
+ let misc = this._parseMP3AudioData(arrayBuffer, dataOffset + 1, dataSize - 1, true);
637
+ if (misc == undefined) {
638
+ return;
639
+ }
640
+ meta.audioSampleRate = misc.samplingRate;
641
+ meta.channelCount = misc.channelCount;
642
+ meta.codec = misc.codec;
643
+ meta.originalCodec = misc.originalCodec;
644
+ // The decode result of an mp3 sample is 1152 PCM samples
645
+ meta.refSampleDuration = 1152 / meta.audioSampleRate * meta.timescale;
646
+ Log.v(this.TAG, 'Parsed MPEG Audio Frame Header');
647
+
648
+ this._audioInitialMetadataDispatched = true;
649
+ this._onTrackMetadata('audio', meta);
650
+
651
+ let mi = this._mediaInfo;
652
+ mi.audioCodec = meta.codec;
653
+ mi.audioSampleRate = meta.audioSampleRate;
654
+ mi.audioChannelCount = meta.channelCount;
655
+ mi.audioDataRate = misc.bitRate;
656
+ if (mi.hasVideo) {
657
+ if (mi.videoCodec != null) {
658
+ mi.mimeType = 'video/x-flv; codecs="' + mi.videoCodec + ',' + mi.audioCodec + '"';
659
+ }
660
+ } else {
661
+ mi.mimeType = 'video/x-flv; codecs="' + mi.audioCodec + '"';
662
+ }
663
+ if (mi.isComplete()) {
664
+ this._onMediaInfo(mi);
665
+ }
666
+ }
667
+
668
+ // This packet is always a valid audio packet, extract it
669
+ let data = this._parseMP3AudioData(arrayBuffer, dataOffset + 1, dataSize - 1, false);
670
+ if (data == undefined) {
671
+ return;
672
+ }
673
+ let dts = this._timestampBase + tagTimestamp;
674
+ let mp3Sample = {unit: data, length: data.byteLength, dts: dts, pts: dts};
675
+ track.samples.push(mp3Sample);
676
+ track.length += data.length;
677
+ } else if (soundFormat === 3) {
678
+ if (!meta.codec) {
679
+ meta.audioSampleRate = soundRate;
680
+ meta.sampleSize = (soundSize + 1) * 8;
681
+ meta.littleEndian = true;
682
+ meta.codec = 'ipcm';
683
+ meta.originalCodec = 'ipcm';
684
+
685
+ this._audioInitialMetadataDispatched = true;
686
+ this._onTrackMetadata('audio', meta);
687
+
688
+ let mi = this._mediaInfo;
689
+ mi.audioCodec = meta.codec;
690
+ mi.audioSampleRate = meta.audioSampleRate;
691
+ mi.audioChannelCount = meta.channelCount;
692
+ mi.audioDataRate = meta.sampleSize * meta.audioSampleRate;
693
+ if (mi.hasVideo) {
694
+ if (mi.videoCodec != null) {
695
+ mi.mimeType = 'video/x-flv; codecs="' + mi.videoCodec + ',' + mi.audioCodec + '"';
696
+ }
697
+ } else {
698
+ mi.mimeType = 'video/x-flv; codecs="' + mi.audioCodec + '"';
699
+ }
700
+ if (mi.isComplete()) {
701
+ this._onMediaInfo(mi);
702
+ }
703
+ }
704
+
705
+ let data = new Uint8Array(arrayBuffer, dataOffset + 1, dataSize - 1);
706
+ let dts = this._timestampBase + tagTimestamp;
707
+ let pcmSample = {unit: data, length: data.byteLength, dts: dts, pts: dts};
708
+ track.samples.push(pcmSample);
709
+ track.length += data.length;
710
+ }
711
+ }
712
+
713
+ _parseAACAudioData(arrayBuffer, dataOffset, dataSize) {
714
+ if (dataSize <= 1) {
715
+ Log.w(this.TAG, 'Flv: Invalid AAC packet, missing AACPacketType or/and Data!');
716
+ return;
717
+ }
718
+
719
+ let result = {};
720
+ let array = new Uint8Array(arrayBuffer, dataOffset, dataSize);
721
+
722
+ result.packetType = array[0];
723
+
724
+ if (array[0] === 0) {
725
+ result.data = this._parseAACAudioSpecificConfig(arrayBuffer, dataOffset + 1, dataSize - 1);
726
+ } else {
727
+ result.data = array.subarray(1);
728
+ }
729
+
730
+ return result;
731
+ }
732
+
733
+ _parseAACAudioSpecificConfig(arrayBuffer, dataOffset, dataSize) {
734
+ let array = new Uint8Array(arrayBuffer, dataOffset, dataSize);
735
+ let config = null;
736
+
737
+ /* Audio Object Type:
738
+ 0: Null
739
+ 1: AAC Main
740
+ 2: AAC LC
741
+ 3: AAC SSR (Scalable Sample Rate)
742
+ 4: AAC LTP (Long Term Prediction)
743
+ 5: HE-AAC / SBR (Spectral Band Replication)
744
+ 6: AAC Scalable
745
+ */
746
+
747
+ let audioObjectType = 0;
748
+ let originalAudioObjectType = 0;
749
+ let audioExtensionObjectType = null;
750
+ let samplingIndex = 0;
751
+ let extensionSamplingIndex = null;
752
+
753
+ // 5 bits
754
+ audioObjectType = originalAudioObjectType = array[0] >>> 3;
755
+ // 4 bits
756
+ samplingIndex = ((array[0] & 0x07) << 1) | (array[1] >>> 7);
757
+ if (samplingIndex < 0 || samplingIndex >= this._mpegSamplingRates.length) {
758
+ this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: AAC invalid sampling frequency index!');
759
+ return;
760
+ }
761
+
762
+ let samplingFrequence = this._mpegSamplingRates[samplingIndex];
763
+
764
+ // 4 bits
765
+ let channelConfig = (array[1] & 0x78) >>> 3;
766
+ if (channelConfig < 0 || channelConfig >= 8) {
767
+ this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: AAC invalid channel configuration');
768
+ return;
769
+ }
770
+
771
+ if (audioObjectType === 5) { // HE-AAC?
772
+ // 4 bits
773
+ extensionSamplingIndex = ((array[1] & 0x07) << 1) | (array[2] >>> 7);
774
+ // 5 bits
775
+ audioExtensionObjectType = (array[2] & 0x7C) >>> 2;
776
+ }
777
+
778
+ // workarounds for various browsers
779
+ let userAgent = self.navigator.userAgent.toLowerCase();
780
+
781
+ if (userAgent.indexOf('firefox') !== -1) {
782
+ // firefox: use SBR (HE-AAC) if freq less than 24kHz
783
+ if (samplingIndex >= 6) {
784
+ audioObjectType = 5;
785
+ config = new Array(4);
786
+ extensionSamplingIndex = samplingIndex - 3;
787
+ } else { // use LC-AAC
788
+ audioObjectType = 2;
789
+ config = new Array(2);
790
+ extensionSamplingIndex = samplingIndex;
791
+ }
792
+ } else if (userAgent.indexOf('android') !== -1) {
793
+ // android: always use LC-AAC
794
+ audioObjectType = 2;
795
+ config = new Array(2);
796
+ extensionSamplingIndex = samplingIndex;
797
+ } else {
798
+ // for other browsers, e.g. chrome...
799
+ // Always use HE-AAC to make it easier to switch aac codec profile
800
+ audioObjectType = 5;
801
+ extensionSamplingIndex = samplingIndex;
802
+ config = new Array(4);
803
+
804
+ if (samplingIndex >= 6) {
805
+ extensionSamplingIndex = samplingIndex - 3;
806
+ } else if (channelConfig === 1) { // Mono channel
807
+ audioObjectType = 2;
808
+ config = new Array(2);
809
+ extensionSamplingIndex = samplingIndex;
810
+ }
811
+ }
812
+
813
+ config[0] = audioObjectType << 3;
814
+ config[0] |= (samplingIndex & 0x0F) >>> 1;
815
+ config[1] = (samplingIndex & 0x0F) << 7;
816
+ config[1] |= (channelConfig & 0x0F) << 3;
817
+ if (audioObjectType === 5) {
818
+ config[1] |= ((extensionSamplingIndex & 0x0F) >>> 1);
819
+ config[2] = (extensionSamplingIndex & 0x01) << 7;
820
+ // extended audio object type: force to 2 (LC-AAC)
821
+ config[2] |= (2 << 2);
822
+ config[3] = 0;
823
+ }
824
+
825
+ return {
826
+ config: config,
827
+ samplingRate: samplingFrequence,
828
+ channelCount: channelConfig,
829
+ codec: 'mp4a.40.' + audioObjectType,
830
+ originalCodec: 'mp4a.40.' + originalAudioObjectType
831
+ };
832
+ }
833
+
834
+ _parseMP3AudioData(arrayBuffer, dataOffset, dataSize, requestHeader) {
835
+ if (dataSize < 4) {
836
+ Log.w(this.TAG, 'Flv: Invalid MP3 packet, header missing!');
837
+ return;
838
+ }
839
+
840
+ let le = this._littleEndian;
841
+ let array = new Uint8Array(arrayBuffer, dataOffset, dataSize);
842
+ let result = null;
843
+
844
+ if (requestHeader) {
845
+ if (array[0] !== 0xFF) {
846
+ return;
847
+ }
848
+ let ver = (array[1] >>> 3) & 0x03;
849
+ let layer = (array[1] & 0x06) >> 1;
850
+
851
+ let bitrate_index = (array[2] & 0xF0) >>> 4;
852
+ let sampling_freq_index = (array[2] & 0x0C) >>> 2;
853
+
854
+ let channel_mode = (array[3] >>> 6) & 0x03;
855
+ let channel_count = channel_mode !== 3 ? 2 : 1;
856
+
857
+ let sample_rate = 0;
858
+ let bit_rate = 0;
859
+ let object_type = 34; // Layer-3, listed in MPEG-4 Audio Object Types
860
+
861
+ let codec = 'mp3';
862
+
863
+ switch (ver) {
864
+ case 0: // MPEG 2.5
865
+ sample_rate = this._mpegAudioV25SampleRateTable[sampling_freq_index];
866
+ break;
867
+ case 2: // MPEG 2
868
+ sample_rate = this._mpegAudioV20SampleRateTable[sampling_freq_index];
869
+ break;
870
+ case 3: // MPEG 1
871
+ sample_rate = this._mpegAudioV10SampleRateTable[sampling_freq_index];
872
+ break;
873
+ }
874
+
875
+ switch (layer) {
876
+ case 1: // Layer 3
877
+ object_type = 34;
878
+ if (bitrate_index < this._mpegAudioL3BitRateTable.length) {
879
+ bit_rate = this._mpegAudioL3BitRateTable[bitrate_index];
880
+ }
881
+ break;
882
+ case 2: // Layer 2
883
+ object_type = 33;
884
+ if (bitrate_index < this._mpegAudioL2BitRateTable.length) {
885
+ bit_rate = this._mpegAudioL2BitRateTable[bitrate_index];
886
+ }
887
+ break;
888
+ case 3: // Layer 1
889
+ object_type = 32;
890
+ if (bitrate_index < this._mpegAudioL1BitRateTable.length) {
891
+ bit_rate = this._mpegAudioL1BitRateTable[bitrate_index];
892
+ }
893
+ break;
894
+ }
895
+
896
+ result = {
897
+ bitRate: bit_rate,
898
+ samplingRate: sample_rate,
899
+ channelCount: channel_count,
900
+ codec: codec,
901
+ originalCodec: codec
902
+ };
903
+ } else {
904
+ result = array;
905
+ }
906
+
907
+ return result;
908
+ }
909
+
910
+ _parseOpusAudioPacket(arrayBuffer, dataOffset, dataSize, tagTimestamp, packetType) {
911
+ if (packetType === 0) { // OpusSequenceHeader
912
+ this._parseOpusSequenceHeader(arrayBuffer, dataOffset, dataSize);
913
+ } else if (packetType === 1) { // OpusCodedData
914
+ this._parseOpusAudioData(arrayBuffer, dataOffset, dataSize, tagTimestamp);
915
+ } else if (packetType === 2) {
916
+ // empty, Opus end of sequence
917
+ } else {
918
+ this._onError(DemuxErrors.FORMAT_ERROR, `Flv: Invalid video packet type ${packetType}`);
919
+ return;
920
+ }
921
+ }
922
+
923
+ _parseOpusSequenceHeader(arrayBuffer, dataOffset, dataSize) {
924
+ if (dataSize <= 16) {
925
+ Log.w(this.TAG, 'Flv: Invalid OpusSequenceHeader, lack of data!');
926
+ return;
927
+ }
928
+ let meta = this._audioMetadata;
929
+ let track = this._audioTrack;
930
+
931
+ if (!meta) {
932
+ if (this._hasAudio === false && this._hasAudioFlagOverrided === false) {
933
+ this._hasAudio = true;
934
+ this._mediaInfo.hasAudio = true;
935
+ }
936
+
937
+ // initial metadata
938
+ meta = this._audioMetadata = {};
939
+ meta.type = 'audio';
940
+ meta.id = track.id;
941
+ meta.timescale = this._timescale;
942
+ meta.duration = this._duration;
943
+ }
944
+
945
+ // Identification Header
946
+ let v = new DataView(arrayBuffer, dataOffset, dataSize);
947
+ v.setUint8(8 + 0, 0); // set version to 0
948
+ let channelCount = v.getUint8(8 + 1); // Opus Header + 1
949
+ v.setUint16(8 + 2, v.getUint16(8 + 2, true), false); // Big Endian to Little Endian for Pre-skip
950
+ let samplingFrequence = v.getUint32(8 + 4, true); // Opus Header + 4
951
+ v.setUint32(8 + 4, v.getUint32(8 + 4, true), false); // Big Endian to Little Endian for Input Sample Rate
952
+ let config = new Uint8Array(arrayBuffer, dataOffset + 8, dataSize - 8);
953
+
954
+ let misc = {
955
+ config,
956
+ channelCount,
957
+ samplingFrequence,
958
+ codec: 'opus',
959
+ originalCodec: 'opus',
960
+ };
961
+ if (meta.config) {
962
+ if (buffersAreEqual(misc.config, meta.config)) {
963
+ // If OpusSequenceHeader is not changed, ignore it to avoid generating initialization segment repeatedly
964
+ return;
965
+ } else {
966
+ Log.w(this.TAG, 'OpusSequenceHeader has been changed, re-generate initialization segment');
967
+ }
968
+ }
969
+ meta.audioSampleRate = misc.samplingFrequence;
970
+ meta.channelCount = misc.channelCount;
971
+ meta.codec = misc.codec;
972
+ meta.originalCodec = misc.originalCodec;
973
+ meta.config = misc.config;
974
+ // The decode result of an opus sample is 20ms
975
+ meta.refSampleDuration = 20;
976
+ Log.v(this.TAG, 'Parsed OpusSequenceHeader');
977
+
978
+ if (this._isInitialMetadataDispatched()) {
979
+ // Non-initial metadata, force dispatch (or flush) parsed frames to remuxer
980
+ if (this._dispatch && (this._audioTrack.length || this._videoTrack.length)) {
981
+ this._onDataAvailable(this._audioTrack, this._videoTrack);
982
+ }
983
+ } else {
984
+ this._audioInitialMetadataDispatched = true;
985
+ }
986
+ // then notify new metadata
987
+ this._dispatch = false;
988
+ this._onTrackMetadata('audio', meta);
989
+
990
+ let mi = this._mediaInfo;
991
+ mi.audioCodec = meta.originalCodec;
992
+ mi.audioSampleRate = meta.audioSampleRate;
993
+ mi.audioChannelCount = meta.channelCount;
994
+ if (mi.hasVideo) {
995
+ if (mi.videoCodec != null) {
996
+ mi.mimeType = 'video/x-flv; codecs="' + mi.videoCodec + ',' + mi.audioCodec + '"';
997
+ }
998
+ } else {
999
+ mi.mimeType = 'video/x-flv; codecs="' + mi.audioCodec + '"';
1000
+ }
1001
+ if (mi.isComplete()) {
1002
+ this._onMediaInfo(mi);
1003
+ }
1004
+ }
1005
+
1006
+ _parseOpusAudioData(arrayBuffer, dataOffset, dataSize, tagTimestamp) {
1007
+ let track = this._audioTrack;
1008
+
1009
+ let data = new Uint8Array(arrayBuffer, dataOffset, dataSize);
1010
+ let dts = this._timestampBase + tagTimestamp;
1011
+ let opusSample = {unit: data, length: data.byteLength, dts: dts, pts: dts};
1012
+
1013
+ track.samples.push(opusSample);
1014
+ track.length += data.length;
1015
+ }
1016
+
1017
+ _parseFlacAudioPacket(arrayBuffer, dataOffset, dataSize, tagTimestamp, packetType) {
1018
+ if (packetType === 0) { // FlacSequenceHeader
1019
+ this._parseFlacSequenceHeader(arrayBuffer, dataOffset, dataSize);
1020
+ } else if (packetType === 1) { // FlacCodedData
1021
+ this._parseFlacAudioData(arrayBuffer, dataOffset, dataSize, tagTimestamp);
1022
+ } else if (packetType === 2) {
1023
+ // empty, Flac end of sequence
1024
+ } else {
1025
+ this._onError(DemuxErrors.FORMAT_ERROR, `Flv: Invalid Flac audio packet type ${packetType}`);
1026
+ return;
1027
+ }
1028
+ }
1029
+
1030
+ _parseFlacSequenceHeader(arrayBuffer, dataOffset, dataSize) {
1031
+ let meta = this._audioMetadata;
1032
+ let track = this._audioTrack;
1033
+
1034
+ if (!meta) {
1035
+ if (this._hasAudio === false && this._hasAudioFlagOverrided === false) {
1036
+ this._hasAudio = true;
1037
+ this._mediaInfo.hasAudio = true;
1038
+ }
1039
+
1040
+ // initial metadata
1041
+ meta = this._audioMetadata = {};
1042
+ meta.type = 'audio';
1043
+ meta.id = track.id;
1044
+ meta.timescale = this._timescale;
1045
+ meta.duration = this._duration;
1046
+ }
1047
+
1048
+ // METADATA_BLOCK_HEADER
1049
+ let header = new Uint8Array(arrayBuffer, dataOffset + 4, dataSize - 4);
1050
+ let gb = new ExpGolomb(header);
1051
+ let minimum_block_size = gb.readBits(16); // minimum_block_size
1052
+ let maximum_block_size = gb.readBits(16); // maximum_block_size
1053
+ let block_size = maximum_block_size === minimum_block_size ? maximum_block_size : null;
1054
+ gb.readBits(24); // minimum_frame_size
1055
+ gb.readBits(24); // maximum_frame_size
1056
+ let samplingFrequence = gb.readBits(20);
1057
+ let channelCount = gb.readBits(3) + 1;
1058
+ let sampleSize = gb.readBits(5) + 1;
1059
+ gb.destroy();
1060
+
1061
+ let config = new Uint8Array(header.byteLength + 4);
1062
+ config.set(header, 4);
1063
+ config[0] = 1 << 7;
1064
+ config[1] = (header.byteLength >>> 16) & 0xFF;
1065
+ config[2] = (header.byteLength >>> 8) & 0xFF;
1066
+ config[3] = (header.byteLength >>> 0) & 0xFF;
1067
+
1068
+ let misc = {
1069
+ config,
1070
+ channelCount,
1071
+ samplingFrequence,
1072
+ sampleSize,
1073
+ codec: 'flac',
1074
+ originalCodec: 'flac',
1075
+ };
1076
+ if (meta.config) {
1077
+ if (buffersAreEqual(misc.config, meta.config)) {
1078
+ // If FlacSequenceHeader is not changed, ignore it to avoid generating initialization segment repeatedly
1079
+ return;
1080
+ } else {
1081
+ Log.w(this.TAG, 'FlacSequenceHeader has been changed, re-generate initialization segment');
1082
+ }
1083
+ }
1084
+ meta.audioSampleRate = misc.samplingFrequence;
1085
+ meta.channelCount = misc.channelCount;
1086
+ meta.sampleSize = misc.sampleSize;
1087
+ meta.codec = misc.codec;
1088
+ meta.originalCodec = misc.originalCodec;
1089
+ meta.config = misc.config;
1090
+ meta.refSampleDuration = block_size != null ? block_size * 1000 / misc.samplingFrequence : null; // practical encoder sends 4608 blobksize (lower bound limitation)
1091
+
1092
+ Log.v(this.TAG, 'Parsed FlacSequenceHeader');
1093
+
1094
+ if (this._isInitialMetadataDispatched()) {
1095
+ // Non-initial metadata, force dispatch (or flush) parsed frames to remuxer
1096
+ if (this._dispatch && (this._audioTrack.length || this._videoTrack.length)) {
1097
+ this._onDataAvailable(this._audioTrack, this._videoTrack);
1098
+ }
1099
+ } else {
1100
+ this._audioInitialMetadataDispatched = true;
1101
+ }
1102
+ // then notify new metadata
1103
+ this._dispatch = false;
1104
+ this._onTrackMetadata('audio', meta);
1105
+
1106
+ let mi = this._mediaInfo;
1107
+ mi.audioCodec = meta.originalCodec;
1108
+ mi.audioSampleRate = meta.audioSampleRate;
1109
+ mi.audioChannelCount = meta.channelCount;
1110
+ if (mi.hasVideo) {
1111
+ if (mi.videoCodec != null) {
1112
+ mi.mimeType = 'video/x-flv; codecs="' + mi.videoCodec + ',' + mi.audioCodec + '"';
1113
+ }
1114
+ } else {
1115
+ mi.mimeType = 'video/x-flv; codecs="' + mi.audioCodec + '"';
1116
+ }
1117
+ if (mi.isComplete()) {
1118
+ this._onMediaInfo(mi);
1119
+ }
1120
+ }
1121
+
1122
+ _parseFlacAudioData(arrayBuffer, dataOffset, dataSize, tagTimestamp) {
1123
+ let track = this._audioTrack;
1124
+
1125
+ let data = new Uint8Array(arrayBuffer, dataOffset, dataSize);
1126
+ let dts = this._timestampBase + tagTimestamp;
1127
+ let flacSample = {unit: data, length: data.byteLength, dts: dts, pts: dts};
1128
+
1129
+ track.samples.push(flacSample);
1130
+ track.length += data.length;
1131
+ }
1132
+
1133
+ _parseVideoData(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition) {
1134
+ if (dataSize <= 1) {
1135
+ Log.w(this.TAG, 'Flv: Invalid video packet, missing VideoData payload!');
1136
+ return;
1137
+ }
1138
+
1139
+ if (this._hasVideoFlagOverrided === true && this._hasVideo === false) {
1140
+ // If hasVideo: false indicated explicitly in MediaDataSource,
1141
+ // Ignore all the video packets
1142
+ return;
1143
+ }
1144
+
1145
+ let spec = (new Uint8Array(arrayBuffer, dataOffset, dataSize))[0];
1146
+
1147
+ let isExHeader = (spec & 0b10000000) !== 0;
1148
+ let frameType = (spec & 0b01110000) >>> 4;
1149
+
1150
+ if (!isExHeader) {
1151
+ let codecId = spec & 0b00001111;
1152
+ if (codecId === 7) { // AVC
1153
+ this._parseAVCVideoPacket(arrayBuffer, dataOffset + 1, dataSize - 1, tagTimestamp, tagPosition, frameType);
1154
+ } else if (codecId === 12) { // HEVC
1155
+ this._parseHEVCVideoPacket(arrayBuffer, dataOffset + 1, dataSize - 1, tagTimestamp, tagPosition, frameType);
1156
+ } else {
1157
+ this._onError(DemuxErrors.CODEC_UNSUPPORTED, `Flv: Unsupported codec in video frame: ${codecId}`);
1158
+ return;
1159
+ }
1160
+ } else {
1161
+ let packetType = spec & 0b00001111;
1162
+ let fourcc = String.fromCharCode(... (new Uint8Array(arrayBuffer, dataOffset, dataSize)).slice(1, 5));
1163
+
1164
+ if (fourcc === 'hvc1') { // HEVC
1165
+ this._parseEnhancedHEVCVideoPacket(arrayBuffer, dataOffset + 5, dataSize - 5, tagTimestamp, tagPosition, frameType, packetType);
1166
+ } else if (fourcc === 'av01') { // HEVC
1167
+ this._parseEnhancedAV1VideoPacket(arrayBuffer, dataOffset + 5, dataSize - 5, tagTimestamp, tagPosition, frameType, packetType);
1168
+ } else {
1169
+ this._onError(DemuxErrors.CODEC_UNSUPPORTED, `Flv: Unsupported codec in video frame: ${fourcc}`);
1170
+ return;
1171
+ }
1172
+ }
1173
+ }
1174
+
1175
+ _parseAVCVideoPacket(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition, frameType) {
1176
+ if (dataSize < 4) {
1177
+ Log.w(this.TAG, 'Flv: Invalid AVC packet, missing AVCPacketType or/and CompositionTime');
1178
+ return;
1179
+ }
1180
+
1181
+ let le = this._littleEndian;
1182
+ let v = new DataView(arrayBuffer, dataOffset, dataSize);
1183
+
1184
+ let packetType = v.getUint8(0);
1185
+ let cts_unsigned = v.getUint32(0, !le) & 0x00FFFFFF;
1186
+ let cts = (cts_unsigned << 8) >> 8; // convert to 24-bit signed int
1187
+
1188
+ if (packetType === 0) { // AVCDecoderConfigurationRecord
1189
+ this._parseAVCDecoderConfigurationRecord(arrayBuffer, dataOffset + 4, dataSize - 4);
1190
+ } else if (packetType === 1) { // One or more Nalus
1191
+ this._parseAVCVideoData(arrayBuffer, dataOffset + 4, dataSize - 4, tagTimestamp, tagPosition, frameType, cts);
1192
+ } else if (packetType === 2) {
1193
+ // empty, AVC end of sequence
1194
+ } else {
1195
+ this._onError(DemuxErrors.FORMAT_ERROR, `Flv: Invalid video packet type ${packetType}`);
1196
+ return;
1197
+ }
1198
+ }
1199
+
1200
+ _parseHEVCVideoPacket(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition, frameType) {
1201
+ if (dataSize < 4) {
1202
+ Log.w(this.TAG, 'Flv: Invalid HEVC packet, missing HEVCPacketType or/and CompositionTime');
1203
+ return;
1204
+ }
1205
+
1206
+ let le = this._littleEndian;
1207
+ let v = new DataView(arrayBuffer, dataOffset, dataSize);
1208
+
1209
+ let packetType = v.getUint8(0);
1210
+ let cts_unsigned = v.getUint32(0, !le) & 0x00FFFFFF;
1211
+ let cts = (cts_unsigned << 8) >> 8; // convert to 24-bit signed int
1212
+
1213
+ if (packetType === 0) { // HEVCDecoderConfigurationRecord
1214
+ this._parseHEVCDecoderConfigurationRecord(arrayBuffer, dataOffset + 4, dataSize - 4);
1215
+ } else if (packetType === 1) { // One or more Nalus
1216
+ this._parseHEVCVideoData(arrayBuffer, dataOffset + 4, dataSize - 4, tagTimestamp, tagPosition, frameType, cts);
1217
+ } else if (packetType === 2) {
1218
+ // empty, HEVC end of sequence
1219
+ } else {
1220
+ this._onError(DemuxErrors.FORMAT_ERROR, `Flv: Invalid video packet type ${packetType}`);
1221
+ return;
1222
+ }
1223
+ }
1224
+
1225
+ _parseEnhancedHEVCVideoPacket(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition, frameType, packetType) {
1226
+ let le = this._littleEndian;
1227
+ let v = new DataView(arrayBuffer, dataOffset, dataSize);
1228
+
1229
+ if (packetType === 0) { // HEVCDecoderConfigurationRecord
1230
+ this._parseHEVCDecoderConfigurationRecord(arrayBuffer, dataOffset, dataSize);
1231
+ } else if (packetType === 1) { // One or more Nalus
1232
+ let cts_unsigned = v.getUint32(0, !le) & 0xFFFFFF00;
1233
+ let cts = cts_unsigned >> 8; // convert to 24-bit signed int
1234
+
1235
+ this._parseHEVCVideoData(arrayBuffer, dataOffset + 3, dataSize - 3, tagTimestamp, tagPosition, frameType, cts);
1236
+ } else if (packetType === 3) {
1237
+ this._parseHEVCVideoData(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition, frameType, 0);
1238
+ } else if (packetType === 2) {
1239
+ // empty, HEVC end of sequence
1240
+ } else {
1241
+ this._onError(DemuxErrors.FORMAT_ERROR, `Flv: Invalid video packet type ${packetType}`);
1242
+ return;
1243
+ }
1244
+ }
1245
+
1246
+ _parseEnhancedAV1VideoPacket(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition, frameType, packetType) {
1247
+ let le = this._littleEndian;
1248
+ let v = new DataView(arrayBuffer, dataOffset, dataSize);
1249
+
1250
+ if (packetType === 0) { // AV1CodecConfigurationRecord
1251
+ this._parseAV1CodecConfigurationRecord(arrayBuffer, dataOffset, dataSize);
1252
+ } else if (packetType === 1) { // One or more OBUs
1253
+ this._parseAV1VideoData(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition, frameType, 0);
1254
+ } else if (packetType === 5) {
1255
+ this._onError(DemuxErrors.FORMAT_ERROR, `Flv: Not Supported MP2T AV1 video packet type ${packetType}`);
1256
+ return;
1257
+ } else if (packetType === 2) {
1258
+ // empty, AV1 end of sequence
1259
+ } else {
1260
+ this._onError(DemuxErrors.FORMAT_ERROR, `Flv: Invalid video packet type ${packetType}`);
1261
+ return;
1262
+ }
1263
+ }
1264
+
1265
+ _parseAVCDecoderConfigurationRecord(arrayBuffer, dataOffset, dataSize) {
1266
+ if (dataSize < 7) {
1267
+ Log.w(this.TAG, 'Flv: Invalid AVCDecoderConfigurationRecord, lack of data!');
1268
+ return;
1269
+ }
1270
+
1271
+ let meta = this._videoMetadata;
1272
+ let track = this._videoTrack;
1273
+ let le = this._littleEndian;
1274
+ let v = new DataView(arrayBuffer, dataOffset, dataSize);
1275
+
1276
+ if (!meta) {
1277
+ if (this._hasVideo === false && this._hasVideoFlagOverrided === false) {
1278
+ this._hasVideo = true;
1279
+ this._mediaInfo.hasVideo = true;
1280
+ }
1281
+
1282
+ meta = this._videoMetadata = {};
1283
+ meta.type = 'video';
1284
+ meta.id = track.id;
1285
+ meta.timescale = this._timescale;
1286
+ meta.duration = this._duration;
1287
+ } else {
1288
+ if (typeof meta.avcc !== 'undefined') {
1289
+ let new_avcc = new Uint8Array(arrayBuffer, dataOffset, dataSize);
1290
+ if (buffersAreEqual(new_avcc, meta.avcc)) {
1291
+ // AVCDecoderConfigurationRecord is not changed, ignore it to avoid initialization segment re-generating
1292
+ return;
1293
+ } else {
1294
+ Log.w(this.TAG, 'AVCDecoderConfigurationRecord has been changed, re-generate initialization segment');
1295
+ }
1296
+ }
1297
+ }
1298
+
1299
+ let version = v.getUint8(0); // configurationVersion
1300
+ let avcProfile = v.getUint8(1); // avcProfileIndication
1301
+ let profileCompatibility = v.getUint8(2); // profile_compatibility
1302
+ let avcLevel = v.getUint8(3); // AVCLevelIndication
1303
+
1304
+ if (version !== 1 || avcProfile === 0) {
1305
+ this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: Invalid AVCDecoderConfigurationRecord');
1306
+ return;
1307
+ }
1308
+
1309
+ this._naluLengthSize = (v.getUint8(4) & 3) + 1; // lengthSizeMinusOne
1310
+ if (this._naluLengthSize !== 3 && this._naluLengthSize !== 4) { // holy shit!!!
1311
+ this._onError(DemuxErrors.FORMAT_ERROR, `Flv: Strange NaluLengthSizeMinusOne: ${this._naluLengthSize - 1}`);
1312
+ return;
1313
+ }
1314
+
1315
+ let spsCount = v.getUint8(5) & 31; // numOfSequenceParameterSets
1316
+ if (spsCount === 0) {
1317
+ this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: Invalid AVCDecoderConfigurationRecord: No SPS');
1318
+ return;
1319
+ } else if (spsCount > 1) {
1320
+ Log.w(this.TAG, `Flv: Strange AVCDecoderConfigurationRecord: SPS Count = ${spsCount}`);
1321
+ }
1322
+
1323
+ let offset = 6;
1324
+
1325
+ for (let i = 0; i < spsCount; i++) {
1326
+ let len = v.getUint16(offset, !le); // sequenceParameterSetLength
1327
+ offset += 2;
1328
+
1329
+ if (len === 0) {
1330
+ continue;
1331
+ }
1332
+
1333
+ // Notice: Nalu without startcode header (00 00 00 01)
1334
+ let sps = new Uint8Array(arrayBuffer, dataOffset + offset, len);
1335
+ offset += len;
1336
+
1337
+ let config = SPSParser.parseSPS(sps);
1338
+ if (i !== 0) {
1339
+ // ignore other sps's config
1340
+ continue;
1341
+ }
1342
+
1343
+ meta.codecWidth = config.codec_size.width;
1344
+ meta.codecHeight = config.codec_size.height;
1345
+ meta.presentWidth = config.present_size.width;
1346
+ meta.presentHeight = config.present_size.height;
1347
+
1348
+ meta.profile = config.profile_string;
1349
+ meta.level = config.level_string;
1350
+ meta.bitDepth = config.bit_depth;
1351
+ meta.chromaFormat = config.chroma_format;
1352
+ meta.sarRatio = config.sar_ratio;
1353
+ meta.frameRate = config.frame_rate;
1354
+
1355
+ if (config.frame_rate.fixed === false ||
1356
+ config.frame_rate.fps_num === 0 ||
1357
+ config.frame_rate.fps_den === 0) {
1358
+ meta.frameRate = this._referenceFrameRate;
1359
+ }
1360
+
1361
+ let fps_den = meta.frameRate.fps_den;
1362
+ let fps_num = meta.frameRate.fps_num;
1363
+ meta.refSampleDuration = meta.timescale * (fps_den / fps_num);
1364
+
1365
+ let codecArray = sps.subarray(1, 4);
1366
+ let codecString = 'avc1.';
1367
+ for (let j = 0; j < 3; j++) {
1368
+ let h = codecArray[j].toString(16);
1369
+ if (h.length < 2) {
1370
+ h = '0' + h;
1371
+ }
1372
+ codecString += h;
1373
+ }
1374
+ meta.codec = codecString;
1375
+
1376
+ let mi = this._mediaInfo;
1377
+ mi.width = meta.codecWidth;
1378
+ mi.height = meta.codecHeight;
1379
+ mi.fps = meta.frameRate.fps;
1380
+ mi.profile = meta.profile;
1381
+ mi.level = meta.level;
1382
+ mi.refFrames = config.ref_frames;
1383
+ mi.chromaFormat = config.chroma_format_string;
1384
+ mi.sarNum = meta.sarRatio.width;
1385
+ mi.sarDen = meta.sarRatio.height;
1386
+ mi.videoCodec = codecString;
1387
+
1388
+ if (mi.hasAudio) {
1389
+ if (mi.audioCodec != null) {
1390
+ mi.mimeType = 'video/x-flv; codecs="' + mi.videoCodec + ',' + mi.audioCodec + '"';
1391
+ }
1392
+ } else {
1393
+ mi.mimeType = 'video/x-flv; codecs="' + mi.videoCodec + '"';
1394
+ }
1395
+ if (mi.isComplete()) {
1396
+ this._onMediaInfo(mi);
1397
+ }
1398
+ }
1399
+
1400
+ let ppsCount = v.getUint8(offset); // numOfPictureParameterSets
1401
+ if (ppsCount === 0) {
1402
+ this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: Invalid AVCDecoderConfigurationRecord: No PPS');
1403
+ return;
1404
+ } else if (ppsCount > 1) {
1405
+ Log.w(this.TAG, `Flv: Strange AVCDecoderConfigurationRecord: PPS Count = ${ppsCount}`);
1406
+ }
1407
+
1408
+ offset++;
1409
+
1410
+ for (let i = 0; i < ppsCount; i++) {
1411
+ let len = v.getUint16(offset, !le); // pictureParameterSetLength
1412
+ offset += 2;
1413
+
1414
+ if (len === 0) {
1415
+ continue;
1416
+ }
1417
+
1418
+ // pps is useless for extracting video information
1419
+ offset += len;
1420
+ }
1421
+
1422
+ meta.avcc = new Uint8Array(dataSize);
1423
+ meta.avcc.set(new Uint8Array(arrayBuffer, dataOffset, dataSize), 0);
1424
+ Log.v(this.TAG, 'Parsed AVCDecoderConfigurationRecord');
1425
+
1426
+ if (this._isInitialMetadataDispatched()) {
1427
+ // flush parsed frames
1428
+ if (this._dispatch && (this._audioTrack.length || this._videoTrack.length)) {
1429
+ this._onDataAvailable(this._audioTrack, this._videoTrack);
1430
+ }
1431
+ } else {
1432
+ this._videoInitialMetadataDispatched = true;
1433
+ }
1434
+ // notify new metadata
1435
+ this._dispatch = false;
1436
+ this._onTrackMetadata('video', meta);
1437
+ }
1438
+
1439
+ _parseHEVCDecoderConfigurationRecord(arrayBuffer, dataOffset, dataSize) {
1440
+ if (dataSize < 22) {
1441
+ Log.w(this.TAG, 'Flv: Invalid HEVCDecoderConfigurationRecord, lack of data!');
1442
+ return;
1443
+ }
1444
+
1445
+ let meta = this._videoMetadata;
1446
+ let track = this._videoTrack;
1447
+ let le = this._littleEndian;
1448
+ let v = new DataView(arrayBuffer, dataOffset, dataSize);
1449
+
1450
+ if (!meta) {
1451
+ if (this._hasVideo === false && this._hasVideoFlagOverrided === false) {
1452
+ this._hasVideo = true;
1453
+ this._mediaInfo.hasVideo = true;
1454
+ }
1455
+
1456
+ meta = this._videoMetadata = {};
1457
+ meta.type = 'video';
1458
+ meta.id = track.id;
1459
+ meta.timescale = this._timescale;
1460
+ meta.duration = this._duration;
1461
+ } else {
1462
+ if (typeof meta.hvcc !== 'undefined') {
1463
+ let new_hvcc = new Uint8Array(arrayBuffer, dataOffset, dataSize);
1464
+ if (buffersAreEqual(new_hvcc, meta.hvcc)) {
1465
+ // HEVCDecoderConfigurationRecord not changed, ignore it to avoid initialization segment re-generating
1466
+ return;
1467
+ } else {
1468
+ Log.w(this.TAG, 'HEVCDecoderConfigurationRecord has been changed, re-generate initialization segment');
1469
+ }
1470
+ }
1471
+ }
1472
+
1473
+ let version = v.getUint8(0); // configurationVersion
1474
+ let hevcProfile = v.getUint8(1) & 0x1F; // hevcProfileIndication
1475
+
1476
+ if ((version !== 0 && version !== 1) || hevcProfile === 0) {
1477
+ this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: Invalid HEVCDecoderConfigurationRecord');
1478
+ return;
1479
+ }
1480
+
1481
+ this._naluLengthSize = (v.getUint8(21) & 3) + 1; // lengthSizeMinusOne
1482
+ if (this._naluLengthSize !== 3 && this._naluLengthSize !== 4) { // holy shit!!!
1483
+ this._onError(DemuxErrors.FORMAT_ERROR, `Flv: Strange NaluLengthSizeMinusOne: ${this._naluLengthSize - 1}`);
1484
+ return;
1485
+ }
1486
+
1487
+ let numOfArrays = v.getUint8(22);
1488
+ for (let i = 0, offset = 23; i < numOfArrays; i++) {
1489
+ let nalUnitType = v.getUint8(offset + 0) & 0x3F;
1490
+ let numNalus = v.getUint16(offset + 1, !le);
1491
+
1492
+ offset += 3;
1493
+ for (let j = 0; j < numNalus; j++) {
1494
+ let len = v.getUint16(offset + 0, !le);
1495
+ if (j !== 0) {
1496
+ offset += 2 + len;
1497
+ continue;
1498
+ }
1499
+
1500
+ if (nalUnitType === 33) {
1501
+ offset += 2;
1502
+ let sps = new Uint8Array(arrayBuffer, dataOffset + offset, len);
1503
+
1504
+ let config = H265Parser.parseSPS(sps);
1505
+ meta.codecWidth = config.codec_size.width;
1506
+ meta.codecHeight = config.codec_size.height;
1507
+ meta.presentWidth = config.present_size.width;
1508
+ meta.presentHeight = config.present_size.height;
1509
+
1510
+ meta.profile = config.profile_string;
1511
+ meta.level = config.level_string;
1512
+ meta.bitDepth = config.bit_depth;
1513
+ meta.chromaFormat = config.chroma_format;
1514
+ meta.sarRatio = config.sar_ratio;
1515
+ meta.frameRate = config.frame_rate;
1516
+
1517
+ if (config.frame_rate.fixed === false ||
1518
+ config.frame_rate.fps_num === 0 ||
1519
+ config.frame_rate.fps_den === 0) {
1520
+ meta.frameRate = this._referenceFrameRate;
1521
+ }
1522
+
1523
+ let fps_den = meta.frameRate.fps_den;
1524
+ let fps_num = meta.frameRate.fps_num;
1525
+ meta.refSampleDuration = meta.timescale * (fps_den / fps_num);
1526
+ meta.codec = config.codec_mimetype;
1527
+
1528
+ let mi = this._mediaInfo;
1529
+ mi.width = meta.codecWidth;
1530
+ mi.height = meta.codecHeight;
1531
+ mi.fps = meta.frameRate.fps;
1532
+ mi.profile = meta.profile;
1533
+ mi.level = meta.level;
1534
+ mi.refFrames = config.ref_frames;
1535
+ mi.chromaFormat = config.chroma_format_string;
1536
+ mi.sarNum = meta.sarRatio.width;
1537
+ mi.sarDen = meta.sarRatio.height;
1538
+ mi.videoCodec = config.codec_mimetype;
1539
+
1540
+ if (mi.hasAudio) {
1541
+ if (mi.audioCodec != null) {
1542
+ mi.mimeType = 'video/x-flv; codecs="' + mi.videoCodec + ',' + mi.audioCodec + '"';
1543
+ }
1544
+ } else {
1545
+ mi.mimeType = 'video/x-flv; codecs="' + mi.videoCodec + '"';
1546
+ }
1547
+ if (mi.isComplete()) {
1548
+ this._onMediaInfo(mi);
1549
+ }
1550
+
1551
+ offset += len;
1552
+ } else {
1553
+ offset += 2 + len;
1554
+ }
1555
+ }
1556
+ }
1557
+
1558
+ meta.hvcc = new Uint8Array(dataSize);
1559
+ meta.hvcc.set(new Uint8Array(arrayBuffer, dataOffset, dataSize), 0);
1560
+ Log.v(this.TAG, 'Parsed HEVCDecoderConfigurationRecord');
1561
+
1562
+ if (this._isInitialMetadataDispatched()) {
1563
+ // flush parsed frames
1564
+ if (this._dispatch && (this._audioTrack.length || this._videoTrack.length)) {
1565
+ this._onDataAvailable(this._audioTrack, this._videoTrack);
1566
+ }
1567
+ } else {
1568
+ this._videoInitialMetadataDispatched = true;
1569
+ }
1570
+ // notify new metadata
1571
+ this._dispatch = false;
1572
+ this._onTrackMetadata('video', meta);
1573
+ }
1574
+
1575
+ _parseAV1CodecConfigurationRecord(arrayBuffer, dataOffset, dataSize) {
1576
+ if (dataSize < 4) {
1577
+ Log.w(this.TAG, 'Flv: Invalid AV1CodecConfigurationRecord, lack of data!');
1578
+ return;
1579
+ }
1580
+
1581
+ let meta = this._videoMetadata;
1582
+ let track = this._videoTrack;
1583
+ let le = this._littleEndian;
1584
+ let v = new DataView(arrayBuffer, dataOffset, dataSize);
1585
+
1586
+ if (!meta) {
1587
+ if (this._hasVideo === false && this._hasVideoFlagOverrided === false) {
1588
+ this._hasVideo = true;
1589
+ this._mediaInfo.hasVideo = true;
1590
+ }
1591
+
1592
+ meta = this._videoMetadata = {};
1593
+ meta.type = 'video';
1594
+ meta.id = track.id;
1595
+ meta.timescale = this._timescale;
1596
+ meta.duration = this._duration;
1597
+ } else {
1598
+ if (typeof meta.av1c !== 'undefined') {
1599
+ Log.w(this.TAG, 'Found another AV1CodecConfigurationRecord!');
1600
+ }
1601
+ }
1602
+
1603
+ let version = v.getUint8(0) & 0x7F;
1604
+ let seq_profile = (v.getUint8(1) & 0xE0) >> 5;
1605
+ let seq_level_idx = (v.getUint8(1) & 0x8F) >> 0;
1606
+ let seq_tier = (v.getUint8(2) & 0x80) >> 7;
1607
+
1608
+ if (version !== 1) {
1609
+ this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: Invalid AV1CodecConfigurationRecord');
1610
+ return;
1611
+ }
1612
+
1613
+ const config = AV1OBUParser.parseOBUs(new Uint8Array(arrayBuffer, dataOffset + 4, dataSize - 4));
1614
+ if (config == null) {
1615
+ this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: Invalid AV1CodecConfigurationRecord');
1616
+ return;
1617
+ }
1618
+
1619
+ meta.profile = config.profile_string;
1620
+ meta.level = config.level_string;
1621
+ meta.bitDepth = config.bit_depth;
1622
+ meta.chromaFormat = config.chroma_format;
1623
+ meta.frameRate = config.frame_rate;
1624
+ if (config.frame_rate.fixed === false ||
1625
+ config.frame_rate.fps_num === 0 ||
1626
+ config.frame_rate.fps_den === 0) {
1627
+ meta.frameRate = this._referenceFrameRate;
1628
+ }
1629
+ let fps_den = meta.frameRate.fps_den;
1630
+ let fps_num = meta.frameRate.fps_num;
1631
+ meta.refSampleDuration = meta.timescale * (fps_den / fps_num);
1632
+ meta.codec = config.codec_mimetype;
1633
+ meta.extra = config;
1634
+
1635
+ let mi = this._mediaInfo;
1636
+ mi.fps = meta.frameRate.fps;
1637
+ mi.profile = meta.profile;
1638
+ mi.level = meta.level;
1639
+ mi.refFrames = config.ref_frames;
1640
+ mi.chromaFormat = config.chroma_format_string;
1641
+ mi.videoCodec = config.codec_mimetype;
1642
+
1643
+ if (mi.hasAudio) {
1644
+ if (mi.audioCodec != null) {
1645
+ mi.mimeType = 'video/x-flv; codecs="' + mi.videoCodec + ',' + mi.audioCodec + '"';
1646
+ }
1647
+ } else {
1648
+ mi.mimeType = 'video/x-flv; codecs="' + mi.videoCodec + '"';
1649
+ }
1650
+ if (mi.isComplete()) {
1651
+ this._onMediaInfo(mi);
1652
+ }
1653
+ meta.av1c = new Uint8Array(dataSize);
1654
+ meta.av1c.set(new Uint8Array(arrayBuffer, dataOffset, dataSize), 0);
1655
+ Log.v(this.TAG, 'Preparing AV1CodecConfigurationRecord');
1656
+ }
1657
+
1658
+ _parseAVCVideoData(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition, frameType, cts) {
1659
+ let le = this._littleEndian;
1660
+ let v = new DataView(arrayBuffer, dataOffset, dataSize);
1661
+
1662
+ let units = [], length = 0;
1663
+
1664
+ let offset = 0;
1665
+ const lengthSize = this._naluLengthSize;
1666
+ let dts = this._timestampBase + tagTimestamp;
1667
+ let keyframe = (frameType === 1); // from FLV Frame Type constants
1668
+
1669
+ while (offset < dataSize) {
1670
+ if (offset + 4 >= dataSize) {
1671
+ Log.w(this.TAG, `Malformed Nalu near timestamp ${dts}, offset = ${offset}, dataSize = ${dataSize}`);
1672
+ break; // data not enough for next Nalu
1673
+ }
1674
+ // Nalu with length-header (AVC1)
1675
+ let naluSize = v.getUint32(offset, !le); // Big-Endian read
1676
+ if (lengthSize === 3) {
1677
+ naluSize >>>= 8;
1678
+ }
1679
+ if (naluSize > dataSize - lengthSize) {
1680
+ Log.w(this.TAG, `Malformed Nalus near timestamp ${dts}, NaluSize > DataSize!`);
1681
+ return;
1682
+ }
1683
+
1684
+ let unitType = v.getUint8(offset + lengthSize) & 0x1F;
1685
+
1686
+ if (unitType === 5) { // IDR
1687
+ keyframe = true;
1688
+ }
1689
+
1690
+ let data = new Uint8Array(arrayBuffer, dataOffset + offset, lengthSize + naluSize);
1691
+ let unit = {type: unitType, data: data};
1692
+ units.push(unit);
1693
+ length += data.byteLength;
1694
+
1695
+ if (unitType === 6) { // SEI
1696
+ this._parseSEIPayload(data.subarray(lengthSize), dts + cts, 'h264');
1697
+ }
1698
+
1699
+ offset += lengthSize + naluSize;
1700
+ }
1701
+
1702
+ if (units.length) {
1703
+ let track = this._videoTrack;
1704
+ let avcSample = {
1705
+ units: units,
1706
+ length: length,
1707
+ isKeyframe: keyframe,
1708
+ dts: dts,
1709
+ cts: cts,
1710
+ pts: (dts + cts)
1711
+ };
1712
+ if (keyframe) {
1713
+ avcSample.fileposition = tagPosition;
1714
+ }
1715
+ track.samples.push(avcSample);
1716
+ track.length += length;
1717
+ }
1718
+ }
1719
+
1720
+ _parseHEVCVideoData(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition, frameType, cts) {
1721
+ let le = this._littleEndian;
1722
+ let v = new DataView(arrayBuffer, dataOffset, dataSize);
1723
+
1724
+ let units = [], length = 0;
1725
+
1726
+ let offset = 0;
1727
+ const lengthSize = this._naluLengthSize;
1728
+ let dts = this._timestampBase + tagTimestamp;
1729
+ let keyframe = (frameType === 1); // from FLV Frame Type constants
1730
+
1731
+ while (offset < dataSize) {
1732
+ if (offset + 4 >= dataSize) {
1733
+ Log.w(this.TAG, `Malformed Nalu near timestamp ${dts}, offset = ${offset}, dataSize = ${dataSize}`);
1734
+ break; // data not enough for next Nalu
1735
+ }
1736
+ // Nalu with length-header (HVC1)
1737
+ let naluSize = v.getUint32(offset, !le); // Big-Endian read
1738
+ if (lengthSize === 3) {
1739
+ naluSize >>>= 8;
1740
+ }
1741
+ if (naluSize > dataSize - lengthSize) {
1742
+ Log.w(this.TAG, `Malformed Nalus near timestamp ${dts}, NaluSize > DataSize!`);
1743
+ return;
1744
+ }
1745
+
1746
+ let unitType = (v.getUint8(offset + lengthSize) >> 1) & 0x3F;
1747
+
1748
+ if (unitType === 19 || unitType === 20 || unitType === 21) { // IRAP
1749
+ keyframe = true;
1750
+ }
1751
+
1752
+ let data = new Uint8Array(arrayBuffer, dataOffset + offset, lengthSize + naluSize);
1753
+ let unit = {type: unitType, data: data};
1754
+ units.push(unit);
1755
+ length += data.byteLength;
1756
+
1757
+ if (unitType === 39 || unitType === 40) { // Prefix / Suffix SEI
1758
+ this._parseSEIPayload(data.subarray(lengthSize), dts + cts, 'h265');
1759
+ }
1760
+
1761
+ offset += lengthSize + naluSize;
1762
+ }
1763
+
1764
+ if (units.length) {
1765
+ let track = this._videoTrack;
1766
+ let hevcSample = {
1767
+ units: units,
1768
+ length: length,
1769
+ isKeyframe: keyframe,
1770
+ dts: dts,
1771
+ cts: cts,
1772
+ pts: (dts + cts)
1773
+ };
1774
+ if (keyframe) {
1775
+ hevcSample.fileposition = tagPosition;
1776
+ }
1777
+ track.samples.push(hevcSample);
1778
+ track.length += length;
1779
+ }
1780
+ }
1781
+
1782
+ _parseAV1VideoData(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition, frameType, cts) {
1783
+ let le = this._littleEndian;
1784
+ let v = new DataView(arrayBuffer, dataOffset, dataSize);
1785
+
1786
+ let units = [], length = 0;
1787
+
1788
+ let offset = 0;
1789
+ let dts = this._timestampBase + tagTimestamp;
1790
+ let keyframe = (frameType === 1); // from FLV Frame Type constants
1791
+
1792
+ if (keyframe) {
1793
+ let meta = this._videoMetadata;
1794
+
1795
+ const config = AV1OBUParser.parseOBUs(new Uint8Array(arrayBuffer, dataOffset, dataSize), meta.extra);
1796
+ if (config == null) {
1797
+ this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: Invalid AV1 VideoData');
1798
+ return;
1799
+ }
1800
+ console.log(config);
1801
+ meta.codecWidth = config.codec_size.width;
1802
+ meta.codecHeight = config.codec_size.height;
1803
+ meta.presentWidth = config.present_size.width;
1804
+ meta.presentHeight = config.present_size.height;
1805
+ meta.sarRatio = config.sar_ratio;
1806
+
1807
+ let mi = this._mediaInfo;
1808
+ mi.width = meta.codecWidth;
1809
+ mi.height = meta.codecHeight;
1810
+ mi.sarNum = meta.sarRatio.width;
1811
+ mi.sarDen = meta.sarRatio.height;
1812
+
1813
+ Log.v(this.TAG, 'Parsed AV1DecoderConfigurationRecord');
1814
+
1815
+ if (this._isInitialMetadataDispatched()) {
1816
+ // flush parsed frames
1817
+ if (this._dispatch && (this._audioTrack.length || this._videoTrack.length)) {
1818
+ this._onDataAvailable(this._audioTrack, this._videoTrack);
1819
+ }
1820
+ } else {
1821
+ this._videoInitialMetadataDispatched = true;
1822
+ }
1823
+ // notify new metadata
1824
+ this._dispatch = false;
1825
+ this._onTrackMetadata('video', meta);
1826
+ }
1827
+
1828
+ /* FIXME: NEEDS Inspect Per OBUs */
1829
+ length = dataSize;
1830
+ units.push({
1831
+ unitType: 0,
1832
+ data: new Uint8Array(arrayBuffer, dataOffset + offset, dataSize)
1833
+ });
1834
+
1835
+ if (units.length) {
1836
+ let track = this._videoTrack;
1837
+ let av1Sample = {
1838
+ units: units,
1839
+ length: length,
1840
+ isKeyframe: keyframe,
1841
+ dts: dts,
1842
+ cts: cts,
1843
+ pts: (dts + cts)
1844
+ };
1845
+ if (keyframe) {
1846
+ av1Sample.fileposition = tagPosition;
1847
+ }
1848
+ track.samples.push(av1Sample);
1849
+ track.length += length;
1850
+ }
1851
+ }
1852
+ }
1853
+
1854
+ export default FLVDemuxer;