@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,286 @@
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 LoggingControl from '../utils/logging-control.js';
21
+ import Polyfill from '../utils/polyfill.js';
22
+ import TransmuxingController from './transmuxing-controller.js';
23
+ import TransmuxingEvents from './transmuxing-events';
24
+
25
+ /* post message to worker:
26
+ data: {
27
+ cmd: string
28
+ param: any
29
+ }
30
+
31
+ receive message from worker:
32
+ data: {
33
+ msg: string,
34
+ data: any
35
+ }
36
+ */
37
+
38
+ let TransmuxingWorker = function (self) {
39
+
40
+ let TAG = 'TransmuxingWorker';
41
+ let controller = null;
42
+ let logcatListener = onLogcatCallback.bind(this);
43
+
44
+ Polyfill.install();
45
+
46
+ self.addEventListener('message', function (e) {
47
+ switch (e.data.cmd) {
48
+ case 'init':
49
+ controller = new TransmuxingController(e.data.param[0], e.data.param[1]);
50
+ controller.on(TransmuxingEvents.IO_ERROR, onIOError.bind(this));
51
+ controller.on(TransmuxingEvents.DEMUX_ERROR, onDemuxError.bind(this));
52
+ controller.on(TransmuxingEvents.INIT_SEGMENT, onInitSegment.bind(this));
53
+ controller.on(TransmuxingEvents.MEDIA_SEGMENT, onMediaSegment.bind(this));
54
+ controller.on(TransmuxingEvents.LOADING_COMPLETE, onLoadingComplete.bind(this));
55
+ controller.on(TransmuxingEvents.RECOVERED_EARLY_EOF, onRecoveredEarlyEof.bind(this));
56
+ controller.on(TransmuxingEvents.MEDIA_INFO, onMediaInfo.bind(this));
57
+ controller.on(TransmuxingEvents.METADATA_ARRIVED, onMetaDataArrived.bind(this));
58
+ controller.on(TransmuxingEvents.SCRIPTDATA_ARRIVED, onScriptDataArrived.bind(this));
59
+ controller.on(TransmuxingEvents.TIMED_ID3_METADATA_ARRIVED, onTimedID3MetadataArrived.bind(this));
60
+ controller.on(TransmuxingEvents.PGS_SUBTITLE_ARRIVED, onPGSSubtitleDataArrived.bind(this));
61
+ controller.on(TransmuxingEvents.SYNCHRONOUS_KLV_METADATA_ARRIVED, onSynchronousKLVMetadataArrived.bind(this));
62
+ controller.on(TransmuxingEvents.ASYNCHRONOUS_KLV_METADATA_ARRIVED, onAsynchronousKLVMetadataArrived.bind(this));
63
+ controller.on(TransmuxingEvents.SMPTE2038_METADATA_ARRIVED, onSMPTE2038MetadataArrived.bind(this));
64
+ controller.on(TransmuxingEvents.SEI_ARRIVED, onSEIArrived.bind(this));
65
+ controller.on(TransmuxingEvents.SCTE35_METADATA_ARRIVED, onSCTE35MetadataArrived.bind(this));
66
+ controller.on(TransmuxingEvents.PES_PRIVATE_DATA_DESCRIPTOR, onPESPrivateDataDescriptor.bind(this));
67
+ controller.on(TransmuxingEvents.PES_PRIVATE_DATA_ARRIVED, onPESPrivateDataArrived.bind(this));
68
+ controller.on(TransmuxingEvents.STATISTICS_INFO, onStatisticsInfo.bind(this));
69
+ controller.on(TransmuxingEvents.RECOMMEND_SEEKPOINT, onRecommendSeekpoint.bind(this));
70
+ break;
71
+ case 'destroy':
72
+ if (controller) {
73
+ controller.destroy();
74
+ controller = null;
75
+ }
76
+ self.postMessage({msg: 'destroyed'});
77
+ break;
78
+ case 'start':
79
+ controller.start();
80
+ break;
81
+ case 'stop':
82
+ controller.stop();
83
+ break;
84
+ case 'seek':
85
+ controller.seek(e.data.param);
86
+ break;
87
+ case 'pause':
88
+ controller.pause();
89
+ break;
90
+ case 'resume':
91
+ controller.resume();
92
+ break;
93
+ case 'logging_config': {
94
+ let config = e.data.param;
95
+ LoggingControl.applyConfig(config);
96
+
97
+ if (config.enableCallback === true) {
98
+ LoggingControl.addLogListener(logcatListener);
99
+ } else {
100
+ LoggingControl.removeLogListener(logcatListener);
101
+ }
102
+ break;
103
+ }
104
+ }
105
+ });
106
+
107
+ function onInitSegment(type, initSegment) {
108
+ let obj = {
109
+ msg: TransmuxingEvents.INIT_SEGMENT,
110
+ data: {
111
+ type: type,
112
+ data: initSegment
113
+ }
114
+ };
115
+ self.postMessage(obj, [initSegment.data]); // data: ArrayBuffer
116
+ }
117
+
118
+ function onMediaSegment(type, mediaSegment) {
119
+ let obj = {
120
+ msg: TransmuxingEvents.MEDIA_SEGMENT,
121
+ data: {
122
+ type: type,
123
+ data: mediaSegment
124
+ }
125
+ };
126
+ self.postMessage(obj, [mediaSegment.data]); // data: ArrayBuffer
127
+ }
128
+
129
+ function onLoadingComplete() {
130
+ let obj = {
131
+ msg: TransmuxingEvents.LOADING_COMPLETE
132
+ };
133
+ self.postMessage(obj);
134
+ }
135
+
136
+ function onRecoveredEarlyEof() {
137
+ let obj = {
138
+ msg: TransmuxingEvents.RECOVERED_EARLY_EOF
139
+ };
140
+ self.postMessage(obj);
141
+ }
142
+
143
+ function onMediaInfo(mediaInfo) {
144
+ let obj = {
145
+ msg: TransmuxingEvents.MEDIA_INFO,
146
+ data: mediaInfo
147
+ };
148
+ self.postMessage(obj);
149
+ }
150
+
151
+ function onMetaDataArrived(metadata) {
152
+ let obj = {
153
+ msg: TransmuxingEvents.METADATA_ARRIVED,
154
+ data: metadata
155
+ };
156
+ self.postMessage(obj);
157
+ }
158
+
159
+ function onScriptDataArrived(data) {
160
+ let obj = {
161
+ msg: TransmuxingEvents.SCRIPTDATA_ARRIVED,
162
+ data: data
163
+ };
164
+ self.postMessage(obj);
165
+ }
166
+
167
+ function onTimedID3MetadataArrived (data) {
168
+ let obj = {
169
+ msg: TransmuxingEvents.TIMED_ID3_METADATA_ARRIVED,
170
+ data: data
171
+ };
172
+ self.postMessage(obj);
173
+ }
174
+
175
+ function onPGSSubtitleDataArrived (data) {
176
+ let obj = {
177
+ msg: TransmuxingEvents.PGS_SUBTITLE_ARRIVED,
178
+ data: data
179
+ };
180
+ self.postMessage(obj);
181
+ }
182
+
183
+ function onSynchronousKLVMetadataArrived (data) {
184
+ let obj = {
185
+ msg: TransmuxingEvents.SYNCHRONOUS_KLV_METADATA_ARRIVED,
186
+ data: data
187
+ };
188
+ self.postMessage(obj);
189
+ }
190
+
191
+ function onAsynchronousKLVMetadataArrived (data) {
192
+ let obj = {
193
+ msg: TransmuxingEvents.ASYNCHRONOUS_KLV_METADATA_ARRIVED,
194
+ data: data
195
+ };
196
+ self.postMessage(obj);
197
+ }
198
+
199
+ function onSMPTE2038MetadataArrived (data) {
200
+ let obj = {
201
+ msg: TransmuxingEvents.SMPTE2038_METADATA_ARRIVED,
202
+ data: data
203
+ };
204
+ self.postMessage(obj);
205
+ }
206
+
207
+ function onSEIArrived (data) {
208
+ let obj = {
209
+ msg: TransmuxingEvents.SEI_ARRIVED,
210
+ data: data
211
+ };
212
+ self.postMessage(obj);
213
+ }
214
+
215
+ function onSCTE35MetadataArrived (data) {
216
+ let obj = {
217
+ msg: TransmuxingEvents.SCTE35_METADATA_ARRIVED,
218
+ data: data
219
+ };
220
+ self.postMessage(obj);
221
+ }
222
+
223
+ function onPESPrivateDataDescriptor(data) {
224
+ let obj = {
225
+ msg: TransmuxingEvents.PES_PRIVATE_DATA_DESCRIPTOR,
226
+ data: data
227
+ };
228
+ self.postMessage(obj);
229
+ }
230
+
231
+ function onPESPrivateDataArrived(data) {
232
+ let obj = {
233
+ msg: TransmuxingEvents.PES_PRIVATE_DATA_ARRIVED,
234
+ data: data
235
+ };
236
+ self.postMessage(obj);
237
+ }
238
+
239
+ function onStatisticsInfo(statInfo) {
240
+ let obj = {
241
+ msg: TransmuxingEvents.STATISTICS_INFO,
242
+ data: statInfo
243
+ };
244
+ self.postMessage(obj);
245
+ }
246
+
247
+ function onIOError(type, info) {
248
+ self.postMessage({
249
+ msg: TransmuxingEvents.IO_ERROR,
250
+ data: {
251
+ type: type,
252
+ info: info
253
+ }
254
+ });
255
+ }
256
+
257
+ function onDemuxError(type, info) {
258
+ self.postMessage({
259
+ msg: TransmuxingEvents.DEMUX_ERROR,
260
+ data: {
261
+ type: type,
262
+ info: info
263
+ }
264
+ });
265
+ }
266
+
267
+ function onRecommendSeekpoint(milliseconds) {
268
+ self.postMessage({
269
+ msg: TransmuxingEvents.RECOMMEND_SEEKPOINT,
270
+ data: milliseconds
271
+ });
272
+ }
273
+
274
+ function onLogcatCallback(type, str) {
275
+ self.postMessage({
276
+ msg: 'logcat_callback',
277
+ data: {
278
+ type: type,
279
+ logcat: str
280
+ }
281
+ });
282
+ }
283
+
284
+ };
285
+
286
+ export default TransmuxingWorker;
@@ -0,0 +1,397 @@
1
+ import Log from "../utils/logger";
2
+ import ExpGolomb from "./exp-golomb";
3
+ import { MPEG4AudioObjectTypes, MPEG4SamplingFrequencies, MPEG4SamplingFrequencyIndex } from "./mpeg4-audio";
4
+
5
+ export class AACFrame {
6
+ audio_object_type: MPEG4AudioObjectTypes;
7
+ sampling_freq_index: MPEG4SamplingFrequencyIndex;
8
+ sampling_frequency: number;
9
+ channel_config: number;
10
+
11
+ data: Uint8Array;
12
+ }
13
+
14
+ export class LOASAACFrame extends AACFrame {
15
+ other_data_present: boolean;
16
+ }
17
+
18
+ export class AACADTSParser {
19
+
20
+ private readonly TAG: string = "AACADTSParser";
21
+
22
+ private data_: Uint8Array;
23
+ private current_syncword_offset_: number;
24
+ private eof_flag_: boolean;
25
+ private has_last_incomplete_data: boolean;
26
+
27
+ public constructor(data: Uint8Array) {
28
+ this.data_ = data;
29
+ this.current_syncword_offset_ = this.findNextSyncwordOffset(0);
30
+ if (this.eof_flag_) {
31
+ Log.e(this.TAG, `Could not found ADTS syncword until payload end`);
32
+ }
33
+ }
34
+
35
+ private findNextSyncwordOffset(syncword_offset: number): number {
36
+ let i = syncword_offset;
37
+ let data = this.data_;
38
+
39
+ while (true) {
40
+ if (i + 7 >= data.byteLength) {
41
+ this.eof_flag_ = true;
42
+ return data.byteLength;
43
+ }
44
+
45
+ // search 12-bit 0xFFF syncword
46
+ let syncword = ((data[i + 0] << 8) | data[i + 1]) >>> 4;
47
+ if (syncword === 0xFFF) {
48
+ return i;
49
+ } else {
50
+ i++;
51
+ }
52
+ }
53
+ }
54
+
55
+ public readNextAACFrame(): AACFrame | null {
56
+ let data = this.data_;
57
+ let aac_frame: AACFrame = null;
58
+
59
+ while (aac_frame == null) {
60
+ if (this.eof_flag_) {
61
+ break;
62
+ }
63
+
64
+ let syncword_offset = this.current_syncword_offset_;
65
+ let offset = syncword_offset;
66
+
67
+ // adts_fixed_header()
68
+ // syncword 0xFFF: 12-bit
69
+ let ID = (data[offset + 1] & 0x08) >>> 3;
70
+ let layer = (data[offset + 1] & 0x06) >>> 1;
71
+ let protection_absent = data[offset + 1] & 0x01;
72
+ let profile = (data[offset + 2] & 0xC0) >>> 6;
73
+ let sampling_frequency_index = (data[offset + 2] & 0x3C) >>> 2;
74
+ let channel_configuration = ((data[offset + 2] & 0x01) << 2)
75
+ | ((data[offset + 3] & 0xC0) >>> 6);
76
+
77
+ // adts_variable_header()
78
+ let aac_frame_length = ((data[offset + 3] & 0x03) << 11)
79
+ | (data[offset + 4] << 3)
80
+ | ((data[offset + 5] & 0xE0) >>> 5);
81
+ let number_of_raw_data_blocks_in_frame = data[offset + 6] & 0x03;
82
+
83
+ if (offset + aac_frame_length > this.data_.byteLength) {
84
+ // data not enough for extracting last sample
85
+ this.eof_flag_ = true;
86
+ this.has_last_incomplete_data = true;
87
+ break;
88
+ }
89
+
90
+ let adts_header_length = (protection_absent === 1) ? 7 : 9;
91
+ let adts_frame_payload_length = aac_frame_length - adts_header_length;
92
+
93
+ offset += adts_header_length;
94
+
95
+ let next_syncword_offset = this.findNextSyncwordOffset(offset + adts_frame_payload_length);
96
+ this.current_syncword_offset_ = next_syncword_offset;
97
+
98
+ if ((ID !== 0 && ID !== 1) || layer !== 0) {
99
+ // invalid adts frame ?
100
+ continue;
101
+ }
102
+
103
+ let frame_data = data.subarray(offset, offset + adts_frame_payload_length);
104
+
105
+ aac_frame = new AACFrame();
106
+ aac_frame.audio_object_type = (profile + 1) as MPEG4AudioObjectTypes;
107
+ aac_frame.sampling_freq_index = sampling_frequency_index as MPEG4SamplingFrequencyIndex;
108
+ aac_frame.sampling_frequency = MPEG4SamplingFrequencies[sampling_frequency_index];
109
+ aac_frame.channel_config = channel_configuration;
110
+ aac_frame.data = frame_data;
111
+ }
112
+
113
+ return aac_frame;
114
+ }
115
+
116
+ public hasIncompleteData(): boolean {
117
+ return this.has_last_incomplete_data;
118
+ }
119
+
120
+ public getIncompleteData(): Uint8Array {
121
+ if (!this.has_last_incomplete_data) {
122
+ return null;
123
+ }
124
+
125
+ return this.data_.subarray(this.current_syncword_offset_);
126
+ }
127
+ }
128
+
129
+ export class AACLOASParser {
130
+
131
+ private readonly TAG: string = "AACLOASParser";
132
+
133
+ private data_: Uint8Array;
134
+ private current_syncword_offset_: number;
135
+ private eof_flag_: boolean;
136
+ private has_last_incomplete_data: boolean;
137
+
138
+ public constructor(data: Uint8Array) {
139
+ this.data_ = data;
140
+ this.current_syncword_offset_ = this.findNextSyncwordOffset(0);
141
+ if (this.eof_flag_) {
142
+ Log.e(this.TAG, `Could not found LOAS syncword until payload end`);
143
+ }
144
+ }
145
+
146
+ private findNextSyncwordOffset(syncword_offset: number): number {
147
+ let i = syncword_offset;
148
+ let data = this.data_;
149
+
150
+ while (true) {
151
+ if (i + 1 >= data.byteLength) {
152
+ this.eof_flag_ = true;
153
+ return data.byteLength;
154
+ }
155
+
156
+ // search 12-bit 0xFFF syncword
157
+ let syncword = (data[i + 0] << 3) | (data[i + 1] >>> 5);
158
+ if (syncword === 0x2B7) {
159
+ return i;
160
+ } else {
161
+ i++;
162
+ }
163
+ }
164
+ }
165
+
166
+ private getLATMValue(gb: ExpGolomb) {
167
+ let bytesForValue = gb.readBits(2);
168
+ let value = 0;
169
+ for (let i = 0; i <= bytesForValue; i++) {
170
+ value = value << 8;
171
+ value = value | gb.readByte();
172
+ }
173
+ return value;
174
+ }
175
+
176
+ public readNextAACFrame(privious?: LOASAACFrame): LOASAACFrame | null {
177
+ let data = this.data_;
178
+ let aac_frame: LOASAACFrame = null;
179
+
180
+ while (aac_frame == null) {
181
+ if (this.eof_flag_) {
182
+ break;
183
+ }
184
+
185
+ let syncword_offset = this.current_syncword_offset_;
186
+ let offset = syncword_offset;
187
+
188
+ let audioMuxLengthBytes = ((data[offset + 1] & 0x1F) << 8) | data[offset + 2];
189
+ if (offset + 3 + audioMuxLengthBytes >= this.data_.byteLength) {
190
+ // data not enough for extracting last sample
191
+ this.eof_flag_ = true;
192
+ this.has_last_incomplete_data = true;
193
+ break;
194
+ }
195
+
196
+ // AudioMuxElement(1)
197
+ let gb = new ExpGolomb(data.subarray(offset + 3, offset + 3 + audioMuxLengthBytes));
198
+ let useSameStreamMux = gb.readBool();
199
+ let streamMuxConfig: LOASAACFrame | null = null;
200
+ if (!useSameStreamMux) {
201
+ let audioMuxVersion = gb.readBool();
202
+ let audioMuxVersionA = audioMuxVersion && gb.readBool();
203
+ if (audioMuxVersionA) {
204
+ Log.e(this.TAG, 'audioMuxVersionA is Not Supported');
205
+ gb.destroy();
206
+ break;
207
+ }
208
+ if (audioMuxVersion) {
209
+ this.getLATMValue(gb);
210
+ }
211
+ let allStreamsSameTimeFraming = gb.readBool();
212
+ if (!allStreamsSameTimeFraming) {
213
+ Log.e(this.TAG, 'allStreamsSameTimeFraming zero is Not Supported');
214
+ gb.destroy();
215
+ break;
216
+ }
217
+ let numSubFrames = gb.readBits(6);
218
+ if (numSubFrames !== 0) {
219
+ Log.e(this.TAG, 'more than 2 numSubFrames Not Supported');
220
+ gb.destroy();
221
+ break;
222
+ }
223
+ let numProgram = gb.readBits(4);
224
+ if (numProgram !== 0) {
225
+ Log.e(this.TAG, 'more than 2 numProgram Not Supported');
226
+ gb.destroy();
227
+ break;
228
+ }
229
+ let numLayer = gb.readBits(3);
230
+ if (numLayer !== 0) {
231
+ Log.e(this.TAG, 'more than 2 numLayer Not Supported');
232
+ gb.destroy();
233
+ break;
234
+ }
235
+
236
+ let fillBits = audioMuxVersion ? this.getLATMValue(gb) : 0;
237
+ let audio_object_type = gb.readBits(5); fillBits -= 5;
238
+ let sampling_freq_index = gb.readBits(4);fillBits -= 4;
239
+ let channel_config = gb.readBits(4); fillBits -= 4;
240
+ gb.readBits(3); fillBits -= 3; // GA Specfic Config
241
+ if (fillBits > 0) { gb.readBits(fillBits); }
242
+
243
+ let frameLengthType = gb.readBits(3);
244
+ if (frameLengthType === 0) {
245
+ gb.readByte();
246
+ } else {
247
+ Log.e(this.TAG, `frameLengthType = ${frameLengthType}. Only frameLengthType = 0 Supported`);
248
+ gb.destroy();
249
+ break;
250
+ }
251
+
252
+ let otherDataPresent = gb.readBool();
253
+ if (otherDataPresent) {
254
+ if (audioMuxVersion) {
255
+ this.getLATMValue(gb);
256
+ } else {
257
+ let otherDataLenBits = 0;
258
+ while (true) {
259
+ otherDataLenBits = otherDataLenBits << 8;
260
+ let otherDataLenEsc = gb.readBool();
261
+ let otherDataLenTmp = gb.readByte();
262
+ otherDataLenBits += otherDataLenTmp
263
+ if (!otherDataLenEsc) { break; }
264
+ }
265
+ console.log(otherDataLenBits)
266
+ }
267
+ }
268
+
269
+ let crcCheckPresent = gb.readBool();
270
+ if (crcCheckPresent) {
271
+ gb.readByte();
272
+ }
273
+
274
+ streamMuxConfig = new LOASAACFrame();
275
+ streamMuxConfig.audio_object_type = audio_object_type;
276
+ streamMuxConfig.sampling_freq_index = sampling_freq_index;
277
+ streamMuxConfig.sampling_frequency = MPEG4SamplingFrequencies[streamMuxConfig.sampling_freq_index];
278
+ streamMuxConfig.channel_config = channel_config;
279
+ streamMuxConfig.other_data_present = otherDataPresent;
280
+ } else if (privious == null) {
281
+ Log.w(this.TAG, 'StreamMuxConfig Missing')
282
+ this.current_syncword_offset_ = this.findNextSyncwordOffset(offset + 3 + audioMuxLengthBytes);
283
+ gb.destroy();
284
+ continue;
285
+ } else {
286
+ streamMuxConfig = privious;
287
+ }
288
+
289
+ let length = 0;
290
+ while (true) {
291
+ let tmp = gb.readByte();
292
+ length += tmp;
293
+ if (tmp !== 0xFF) { break; }
294
+ }
295
+
296
+ let aac_data = new Uint8Array(length);
297
+ for (let i = 0; i < length; i++) {
298
+ aac_data[i] = gb.readByte();
299
+ }
300
+
301
+ aac_frame = new LOASAACFrame();
302
+ aac_frame.audio_object_type = (streamMuxConfig.audio_object_type) as MPEG4AudioObjectTypes;
303
+ aac_frame.sampling_freq_index = (streamMuxConfig.sampling_freq_index) as MPEG4SamplingFrequencyIndex;
304
+ aac_frame.sampling_frequency = MPEG4SamplingFrequencies[streamMuxConfig.sampling_freq_index];
305
+ aac_frame.channel_config = streamMuxConfig.channel_config;
306
+ aac_frame.other_data_present = streamMuxConfig.other_data_present;
307
+ aac_frame.data = aac_data;
308
+
309
+ this.current_syncword_offset_ = this.findNextSyncwordOffset(offset + 3 + audioMuxLengthBytes);
310
+ }
311
+
312
+ return aac_frame;
313
+ }
314
+
315
+ public hasIncompleteData(): boolean {
316
+ return this.has_last_incomplete_data;
317
+ }
318
+
319
+ public getIncompleteData(): Uint8Array {
320
+ if (!this.has_last_incomplete_data) {
321
+ return null;
322
+ }
323
+
324
+ return this.data_.subarray(this.current_syncword_offset_);
325
+ }
326
+ }
327
+
328
+ export class AudioSpecificConfig {
329
+
330
+ public config: Array<number>;
331
+ public sampling_rate: number;
332
+ public channel_count: number;
333
+ public codec_mimetype: string;
334
+ public original_codec_mimetype: string;
335
+
336
+ public constructor(frame: AACFrame) {
337
+ let config: Array<number> = null;
338
+
339
+ let original_audio_object_type = frame.audio_object_type;
340
+ let audio_object_type = frame.audio_object_type;
341
+ let sampling_index = frame.sampling_freq_index;
342
+ let channel_config = frame.channel_config;
343
+ let extension_sampling_index = 0;
344
+
345
+ let userAgent = navigator.userAgent.toLowerCase();
346
+
347
+ if (userAgent.indexOf('firefox') !== -1) {
348
+ // firefox: use SBR (HE-AAC) if freq less than 24kHz
349
+ if (sampling_index >= 6) {
350
+ audio_object_type = 5;
351
+ config = new Array(4);
352
+ extension_sampling_index = sampling_index - 3;
353
+ } else { // use LC-AAC
354
+ audio_object_type = 2;
355
+ config = new Array(2);
356
+ extension_sampling_index = sampling_index;
357
+ }
358
+ } else if (userAgent.indexOf('android') !== -1) {
359
+ // android: always use LC-AAC
360
+ audio_object_type = 2;
361
+ config = new Array(2);
362
+ extension_sampling_index = sampling_index;
363
+ } else {
364
+ // for other browsers, e.g. chrome...
365
+ // Always use HE-AAC to make it easier to switch aac codec profile
366
+ audio_object_type = 5;
367
+ extension_sampling_index = sampling_index;
368
+ config = new Array(4);
369
+
370
+ if (sampling_index >= 6) {
371
+ extension_sampling_index = sampling_index - 3;
372
+ } else if (channel_config === 1) { // Mono channel
373
+ audio_object_type = 2;
374
+ config = new Array(2);
375
+ extension_sampling_index = sampling_index;
376
+ }
377
+ }
378
+
379
+ config[0] = audio_object_type << 3;
380
+ config[0] |= (sampling_index & 0x0F) >>> 1;
381
+ config[1] = (sampling_index & 0x0F) << 7;
382
+ config[1] |= (channel_config & 0x0F) << 3;
383
+ if (audio_object_type === 5) {
384
+ config[1] |= ((extension_sampling_index & 0x0F) >>> 1);
385
+ config[2] = (extension_sampling_index & 0x01) << 7;
386
+ // extended audio object type: force to 2 (LC-AAC)
387
+ config[2] |= (2 << 2);
388
+ config[3] = 0;
389
+ }
390
+
391
+ this.config = config;
392
+ this.sampling_rate = MPEG4SamplingFrequencies[sampling_index];
393
+ this.channel_count = channel_config;
394
+ this.codec_mimetype = 'mp4a.40.' + audio_object_type;
395
+ this.original_codec_mimetype = 'mp4a.40.' + original_audio_object_type;
396
+ }
397
+ }