@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,647 @@
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 SpeedSampler from './speed-sampler.js';
21
+ import {LoaderStatus, LoaderErrors} from './loader.js';
22
+ import FetchStreamLoader from './fetch-stream-loader.js';
23
+ import MozChunkedLoader from './xhr-moz-chunked-loader.js';
24
+ import MSStreamLoader from './xhr-msstream-loader.js';
25
+ import RangeLoader from './xhr-range-loader.js';
26
+ import WebSocketLoader from './websocket-loader.js';
27
+ import RangeSeekHandler from './range-seek-handler.js';
28
+ import ParamSeekHandler from './param-seek-handler.js';
29
+ import {RuntimeException, IllegalStateException, InvalidArgumentException} from '../utils/exception.js';
30
+
31
+ /**
32
+ * DataSource: {
33
+ * url: string,
34
+ * filesize: number,
35
+ * cors: boolean,
36
+ * withCredentials: boolean
37
+ * }
38
+ *
39
+ */
40
+
41
+ // Manage IO Loaders
42
+ class IOController {
43
+
44
+ constructor(dataSource, config, extraData) {
45
+ this.TAG = 'IOController';
46
+
47
+ this._config = config;
48
+ this._extraData = extraData;
49
+
50
+ this._stashInitialSize = 64 * 1024; // default initial size: 64KB
51
+ if (config.stashInitialSize != undefined && config.stashInitialSize > 0) {
52
+ // apply from config
53
+ this._stashInitialSize = config.stashInitialSize;
54
+ }
55
+
56
+ this._stashUsed = 0;
57
+ this._stashSize = this._stashInitialSize;
58
+ this._bufferSize = Math.max(this._stashSize, 1024 * 1024 * 3); // initial size: 3MB at least
59
+ this._stashBuffer = new ArrayBuffer(this._bufferSize);
60
+ this._stashByteStart = 0;
61
+ this._enableStash = true;
62
+ if (config.enableStashBuffer === false) {
63
+ this._enableStash = false;
64
+ }
65
+
66
+ this._loader = null;
67
+ this._loaderClass = null;
68
+ this._seekHandler = null;
69
+
70
+ this._dataSource = dataSource;
71
+ this._isWebSocketURL = /wss?:\/\/(.+?)/.test(dataSource.url);
72
+ this._refTotalLength = dataSource.filesize ? dataSource.filesize : null;
73
+ this._totalLength = this._refTotalLength;
74
+ this._fullRequestFlag = false;
75
+ this._currentRange = null;
76
+ this._redirectedURL = null;
77
+
78
+ this._speedNormalized = 0;
79
+ this._speedSampler = new SpeedSampler();
80
+ this._speedNormalizeList = [32, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096];
81
+
82
+ this._isEarlyEofReconnecting = false;
83
+
84
+ this._paused = false;
85
+ this._resumeFrom = 0;
86
+
87
+ this._onDataArrival = null;
88
+ this._onSeeked = null;
89
+ this._onError = null;
90
+ this._onComplete = null;
91
+ this._onRedirect = null;
92
+ this._onRecoveredEarlyEof = null;
93
+
94
+ this._selectSeekHandler();
95
+ this._selectLoader();
96
+ this._createLoader();
97
+ }
98
+
99
+ destroy() {
100
+ if (this._loader.isWorking()) {
101
+ this._loader.abort();
102
+ }
103
+ this._loader.destroy();
104
+ this._loader = null;
105
+ this._loaderClass = null;
106
+ this._dataSource = null;
107
+ this._stashBuffer = null;
108
+ this._stashUsed = this._stashSize = this._bufferSize = this._stashByteStart = 0;
109
+ this._currentRange = null;
110
+ this._speedSampler = null;
111
+
112
+ this._isEarlyEofReconnecting = false;
113
+
114
+ this._onDataArrival = null;
115
+ this._onSeeked = null;
116
+ this._onError = null;
117
+ this._onComplete = null;
118
+ this._onRedirect = null;
119
+ this._onRecoveredEarlyEof = null;
120
+
121
+ this._extraData = null;
122
+ }
123
+
124
+ isWorking() {
125
+ return this._loader && this._loader.isWorking() && !this._paused;
126
+ }
127
+
128
+ isPaused() {
129
+ return this._paused;
130
+ }
131
+
132
+ get status() {
133
+ return this._loader.status;
134
+ }
135
+
136
+ get extraData() {
137
+ return this._extraData;
138
+ }
139
+
140
+ set extraData(data) {
141
+ this._extraData = data;
142
+ }
143
+
144
+ // prototype: function onDataArrival(chunks: ArrayBuffer, byteStart: number): number
145
+ get onDataArrival() {
146
+ return this._onDataArrival;
147
+ }
148
+
149
+ set onDataArrival(callback) {
150
+ this._onDataArrival = callback;
151
+ }
152
+
153
+ get onSeeked() {
154
+ return this._onSeeked;
155
+ }
156
+
157
+ set onSeeked(callback) {
158
+ this._onSeeked = callback;
159
+ }
160
+
161
+ // prototype: function onError(type: number, info: {code: number, msg: string}): void
162
+ get onError() {
163
+ return this._onError;
164
+ }
165
+
166
+ set onError(callback) {
167
+ this._onError = callback;
168
+ }
169
+
170
+ get onComplete() {
171
+ return this._onComplete;
172
+ }
173
+
174
+ set onComplete(callback) {
175
+ this._onComplete = callback;
176
+ }
177
+
178
+ get onRedirect() {
179
+ return this._onRedirect;
180
+ }
181
+
182
+ set onRedirect(callback) {
183
+ this._onRedirect = callback;
184
+ }
185
+
186
+ get onRecoveredEarlyEof() {
187
+ return this._onRecoveredEarlyEof;
188
+ }
189
+
190
+ set onRecoveredEarlyEof(callback) {
191
+ this._onRecoveredEarlyEof = callback;
192
+ }
193
+
194
+ get currentURL() {
195
+ return this._dataSource.url;
196
+ }
197
+
198
+ get hasRedirect() {
199
+ return (this._redirectedURL != null || this._dataSource.redirectedURL != undefined);
200
+ }
201
+
202
+ get currentRedirectedURL() {
203
+ return this._redirectedURL || this._dataSource.redirectedURL;
204
+ }
205
+
206
+ // in KB/s
207
+ get currentSpeed() {
208
+ if (this._loaderClass === RangeLoader) {
209
+ // SpeedSampler is inaccuracy if loader is RangeLoader
210
+ return this._loader.currentSpeed;
211
+ }
212
+ return this._speedSampler.lastSecondKBps;
213
+ }
214
+
215
+ get loaderType() {
216
+ return this._loader.type;
217
+ }
218
+
219
+ _selectSeekHandler() {
220
+ let config = this._config;
221
+
222
+ if (config.seekType === 'range') {
223
+ this._seekHandler = new RangeSeekHandler(this._config.rangeLoadZeroStart);
224
+ } else if (config.seekType === 'param') {
225
+ let paramStart = config.seekParamStart || 'bstart';
226
+ let paramEnd = config.seekParamEnd || 'bend';
227
+
228
+ this._seekHandler = new ParamSeekHandler(paramStart, paramEnd);
229
+ } else if (config.seekType === 'custom') {
230
+ if (typeof config.customSeekHandler !== 'function') {
231
+ throw new InvalidArgumentException('Custom seekType specified in config but invalid customSeekHandler!');
232
+ }
233
+ this._seekHandler = new config.customSeekHandler();
234
+ } else {
235
+ throw new InvalidArgumentException(`Invalid seekType in config: ${config.seekType}`);
236
+ }
237
+ }
238
+
239
+ _selectLoader() {
240
+ if (this._config.customLoader != null) {
241
+ this._loaderClass = this._config.customLoader;
242
+ } else if (this._isWebSocketURL) {
243
+ this._loaderClass = WebSocketLoader;
244
+ } else if (FetchStreamLoader.isSupported()) {
245
+ this._loaderClass = FetchStreamLoader;
246
+ } else if (MozChunkedLoader.isSupported()) {
247
+ this._loaderClass = MozChunkedLoader;
248
+ } else if (RangeLoader.isSupported()) {
249
+ this._loaderClass = RangeLoader;
250
+ } else {
251
+ throw new RuntimeException('Your browser doesn\'t support xhr with arraybuffer responseType!');
252
+ }
253
+ }
254
+
255
+ _createLoader() {
256
+ this._loader = new this._loaderClass(this._seekHandler, this._config);
257
+ if (this._loader.needStashBuffer === false) {
258
+ this._enableStash = false;
259
+ }
260
+ this._loader.onContentLengthKnown = this._onContentLengthKnown.bind(this);
261
+ this._loader.onURLRedirect = this._onURLRedirect.bind(this);
262
+ this._loader.onDataArrival = this._onLoaderChunkArrival.bind(this);
263
+ this._loader.onComplete = this._onLoaderComplete.bind(this);
264
+ this._loader.onError = this._onLoaderError.bind(this);
265
+ }
266
+
267
+ open(optionalFrom) {
268
+ this._currentRange = {from: 0, to: -1};
269
+ if (optionalFrom) {
270
+ this._currentRange.from = optionalFrom;
271
+ }
272
+
273
+ this._speedSampler.reset();
274
+ if (!optionalFrom) {
275
+ this._fullRequestFlag = true;
276
+ }
277
+
278
+ this._loader.open(this._dataSource, Object.assign({}, this._currentRange));
279
+ }
280
+
281
+ abort() {
282
+ this._loader.abort();
283
+
284
+ if (this._paused) {
285
+ this._paused = false;
286
+ this._resumeFrom = 0;
287
+ }
288
+ }
289
+
290
+ pause() {
291
+ if (this.isWorking()) {
292
+ this._loader.abort();
293
+
294
+ if (this._stashUsed !== 0) {
295
+ this._resumeFrom = this._stashByteStart;
296
+ this._currentRange.to = this._stashByteStart - 1;
297
+ } else {
298
+ this._resumeFrom = this._currentRange.to + 1;
299
+ }
300
+ this._stashUsed = 0;
301
+ this._stashByteStart = 0;
302
+ this._paused = true;
303
+ }
304
+ }
305
+
306
+ resume() {
307
+ if (this._paused) {
308
+ this._paused = false;
309
+ let bytes = this._resumeFrom;
310
+ this._resumeFrom = 0;
311
+ this._internalSeek(bytes, true);
312
+ }
313
+ }
314
+
315
+ seek(bytes) {
316
+ this._paused = false;
317
+ this._stashUsed = 0;
318
+ this._stashByteStart = 0;
319
+ this._internalSeek(bytes, true);
320
+ }
321
+
322
+ /**
323
+ * When seeking request is from media seeking, unconsumed stash data should be dropped
324
+ * However, stash data shouldn't be dropped if seeking requested from http reconnection
325
+ *
326
+ * @dropUnconsumed: Ignore and discard all unconsumed data in stash buffer
327
+ */
328
+ _internalSeek(bytes, dropUnconsumed) {
329
+ if (this._loader.isWorking()) {
330
+ this._loader.abort();
331
+ }
332
+
333
+ // dispatch & flush stash buffer before seek
334
+ this._flushStashBuffer(dropUnconsumed);
335
+
336
+ this._loader.destroy();
337
+ this._loader = null;
338
+
339
+ let requestRange = {from: bytes, to: -1};
340
+ this._currentRange = {from: requestRange.from, to: -1};
341
+
342
+ this._speedSampler.reset();
343
+ this._stashSize = this._stashInitialSize;
344
+ this._createLoader();
345
+ this._loader.open(this._dataSource, requestRange);
346
+
347
+ if (this._onSeeked) {
348
+ this._onSeeked();
349
+ }
350
+ }
351
+
352
+ updateUrl(url) {
353
+ if (!url || typeof url !== 'string' || url.length === 0) {
354
+ throw new InvalidArgumentException('Url must be a non-empty string!');
355
+ }
356
+
357
+ this._dataSource.url = url;
358
+
359
+ // TODO: replace with new url
360
+ }
361
+
362
+ _expandBuffer(expectedBytes) {
363
+ let bufferNewSize = this._stashSize;
364
+ while (bufferNewSize + 1024 * 1024 * 1 < expectedBytes) {
365
+ bufferNewSize *= 2;
366
+ }
367
+
368
+ bufferNewSize += 1024 * 1024 * 1; // bufferSize = stashSize + 1MB
369
+ if (bufferNewSize === this._bufferSize) {
370
+ return;
371
+ }
372
+
373
+ let newBuffer = new ArrayBuffer(bufferNewSize);
374
+
375
+ if (this._stashUsed > 0) { // copy existing data into new buffer
376
+ let stashOldArray = new Uint8Array(this._stashBuffer, 0, this._stashUsed);
377
+ let stashNewArray = new Uint8Array(newBuffer, 0, bufferNewSize);
378
+ stashNewArray.set(stashOldArray, 0);
379
+ }
380
+
381
+ this._stashBuffer = newBuffer;
382
+ this._bufferSize = bufferNewSize;
383
+ }
384
+
385
+ _normalizeSpeed(input) {
386
+ let list = this._speedNormalizeList;
387
+ let last = list.length - 1;
388
+ let mid = 0;
389
+ let lbound = 0;
390
+ let ubound = last;
391
+
392
+ if (input < list[0]) {
393
+ return list[0];
394
+ }
395
+
396
+ // binary search
397
+ while (lbound <= ubound) {
398
+ mid = lbound + Math.floor((ubound - lbound) / 2);
399
+ if (mid === last || (input >= list[mid] && input < list[mid + 1])) {
400
+ return list[mid];
401
+ } else if (list[mid] < input) {
402
+ lbound = mid + 1;
403
+ } else {
404
+ ubound = mid - 1;
405
+ }
406
+ }
407
+ }
408
+
409
+ _adjustStashSize(normalized) {
410
+ let stashSizeKB = 0;
411
+
412
+ if (this._config.isLive) {
413
+ // live stream: always use 1/8 normalized speed for size of stashSizeKB
414
+ stashSizeKB = normalized / 8;
415
+ } else {
416
+ if (normalized < 512) {
417
+ stashSizeKB = normalized;
418
+ } else if (normalized >= 512 && normalized <= 1024) {
419
+ stashSizeKB = Math.floor(normalized * 1.5);
420
+ } else {
421
+ stashSizeKB = normalized * 2;
422
+ }
423
+ }
424
+
425
+ if (stashSizeKB > 8192) {
426
+ stashSizeKB = 8192;
427
+ }
428
+
429
+ let bufferSize = stashSizeKB * 1024 + 1024 * 1024 * 1; // stashSize + 1MB
430
+ if (this._bufferSize < bufferSize) {
431
+ this._expandBuffer(bufferSize);
432
+ }
433
+ this._stashSize = stashSizeKB * 1024;
434
+ }
435
+
436
+ _dispatchChunks(chunks, byteStart) {
437
+ this._currentRange.to = byteStart + chunks.byteLength - 1;
438
+ return this._onDataArrival(chunks, byteStart);
439
+ }
440
+
441
+ _onURLRedirect(redirectedURL) {
442
+ this._redirectedURL = redirectedURL;
443
+ if (this._onRedirect) {
444
+ this._onRedirect(redirectedURL);
445
+ }
446
+ }
447
+
448
+ _onContentLengthKnown(contentLength) {
449
+ if (contentLength && this._fullRequestFlag) {
450
+ this._totalLength = contentLength;
451
+ this._fullRequestFlag = false;
452
+ }
453
+ }
454
+
455
+ _onLoaderChunkArrival(chunk, byteStart, receivedLength) {
456
+ if (!this._onDataArrival) {
457
+ throw new IllegalStateException('IOController: No existing consumer (onDataArrival) callback!');
458
+ }
459
+ if (this._paused) {
460
+ return;
461
+ }
462
+ if (this._isEarlyEofReconnecting) {
463
+ // Auto-reconnect for EarlyEof succeed, notify to upper-layer by callback
464
+ this._isEarlyEofReconnecting = false;
465
+ if (this._onRecoveredEarlyEof) {
466
+ this._onRecoveredEarlyEof();
467
+ }
468
+ }
469
+
470
+ this._speedSampler.addBytes(chunk.byteLength);
471
+
472
+ // adjust stash buffer size according to network speed dynamically
473
+ let KBps = this._speedSampler.lastSecondKBps;
474
+ if (KBps !== 0) {
475
+ let normalized = this._normalizeSpeed(KBps);
476
+ if (this._speedNormalized !== normalized) {
477
+ this._speedNormalized = normalized;
478
+ this._adjustStashSize(normalized);
479
+ }
480
+ }
481
+
482
+ if (!this._enableStash) { // disable stash
483
+ if (this._stashUsed === 0) {
484
+ // dispatch chunk directly to consumer;
485
+ // check ret value (consumed bytes) and stash unconsumed to stashBuffer
486
+ let consumed = this._dispatchChunks(chunk, byteStart);
487
+ if (consumed < chunk.byteLength) { // unconsumed data remain.
488
+ let remain = chunk.byteLength - consumed;
489
+ if (remain > this._bufferSize) {
490
+ this._expandBuffer(remain);
491
+ }
492
+ let stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize);
493
+ stashArray.set(new Uint8Array(chunk, consumed), 0);
494
+ this._stashUsed += remain;
495
+ this._stashByteStart = byteStart + consumed;
496
+ }
497
+ } else {
498
+ // else: Merge chunk into stashBuffer, and dispatch stashBuffer to consumer.
499
+ if (this._stashUsed + chunk.byteLength > this._bufferSize) {
500
+ this._expandBuffer(this._stashUsed + chunk.byteLength);
501
+ }
502
+ let stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize);
503
+ stashArray.set(new Uint8Array(chunk), this._stashUsed);
504
+ this._stashUsed += chunk.byteLength;
505
+ let consumed = this._dispatchChunks(this._stashBuffer.slice(0, this._stashUsed), this._stashByteStart);
506
+ if (consumed < this._stashUsed && consumed > 0) { // unconsumed data remain
507
+ let remainArray = new Uint8Array(this._stashBuffer, consumed);
508
+ stashArray.set(remainArray, 0);
509
+ }
510
+ this._stashUsed -= consumed;
511
+ this._stashByteStart += consumed;
512
+ }
513
+ } else { // enable stash
514
+ if (this._stashUsed === 0 && this._stashByteStart === 0) { // seeked? or init chunk?
515
+ // This is the first chunk after seek action
516
+ this._stashByteStart = byteStart;
517
+ }
518
+ if (this._stashUsed + chunk.byteLength <= this._stashSize) {
519
+ // just stash
520
+ let stashArray = new Uint8Array(this._stashBuffer, 0, this._stashSize);
521
+ stashArray.set(new Uint8Array(chunk), this._stashUsed);
522
+ this._stashUsed += chunk.byteLength;
523
+ } else { // stashUsed + chunkSize > stashSize, size limit exceeded
524
+ let stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize);
525
+ if (this._stashUsed > 0) { // There're stash datas in buffer
526
+ // dispatch the whole stashBuffer, and stash remain data
527
+ // then append chunk to stashBuffer (stash)
528
+ let buffer = this._stashBuffer.slice(0, this._stashUsed);
529
+ let consumed = this._dispatchChunks(buffer, this._stashByteStart);
530
+ if (consumed < buffer.byteLength) {
531
+ if (consumed > 0) {
532
+ let remainArray = new Uint8Array(buffer, consumed);
533
+ stashArray.set(remainArray, 0);
534
+ this._stashUsed = remainArray.byteLength;
535
+ this._stashByteStart += consumed;
536
+ }
537
+ } else {
538
+ this._stashUsed = 0;
539
+ this._stashByteStart += consumed;
540
+ }
541
+ if (this._stashUsed + chunk.byteLength > this._bufferSize) {
542
+ this._expandBuffer(this._stashUsed + chunk.byteLength);
543
+ stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize);
544
+ }
545
+ stashArray.set(new Uint8Array(chunk), this._stashUsed);
546
+ this._stashUsed += chunk.byteLength;
547
+ } else { // stash buffer empty, but chunkSize > stashSize (oh, holy shit)
548
+ // dispatch chunk directly and stash remain data
549
+ let consumed = this._dispatchChunks(chunk, byteStart);
550
+ if (consumed < chunk.byteLength) {
551
+ let remain = chunk.byteLength - consumed;
552
+ if (remain > this._bufferSize) {
553
+ this._expandBuffer(remain);
554
+ stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize);
555
+ }
556
+ stashArray.set(new Uint8Array(chunk, consumed), 0);
557
+ this._stashUsed += remain;
558
+ this._stashByteStart = byteStart + consumed;
559
+ }
560
+ }
561
+ }
562
+ }
563
+ }
564
+
565
+ _flushStashBuffer(dropUnconsumed) {
566
+ if (this._stashUsed > 0) {
567
+ let buffer = this._stashBuffer.slice(0, this._stashUsed);
568
+ let consumed = this._dispatchChunks(buffer, this._stashByteStart);
569
+ let remain = buffer.byteLength - consumed;
570
+
571
+ if (consumed < buffer.byteLength) {
572
+ if (dropUnconsumed) {
573
+ Log.w(this.TAG, `${remain} bytes unconsumed data remain when flush buffer, dropped`);
574
+ } else {
575
+ if (consumed > 0) {
576
+ let stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize);
577
+ let remainArray = new Uint8Array(buffer, consumed);
578
+ stashArray.set(remainArray, 0);
579
+ this._stashUsed = remainArray.byteLength;
580
+ this._stashByteStart += consumed;
581
+ }
582
+ return 0;
583
+ }
584
+ }
585
+ this._stashUsed = 0;
586
+ this._stashByteStart = 0;
587
+ return remain;
588
+ }
589
+ return 0;
590
+ }
591
+
592
+ _onLoaderComplete(from, to) {
593
+ // Force-flush stash buffer, and drop unconsumed data
594
+ this._flushStashBuffer(true);
595
+
596
+ if (this._onComplete) {
597
+ this._onComplete(this._extraData);
598
+ }
599
+ }
600
+
601
+ _onLoaderError(type, data) {
602
+ Log.e(this.TAG, `Loader error, code = ${data.code}, msg = ${data.msg}`);
603
+
604
+ this._flushStashBuffer(false);
605
+
606
+ if (this._isEarlyEofReconnecting) {
607
+ // Auto-reconnect for EarlyEof failed, throw UnrecoverableEarlyEof error to upper-layer
608
+ this._isEarlyEofReconnecting = false;
609
+ type = LoaderErrors.UNRECOVERABLE_EARLY_EOF;
610
+ }
611
+
612
+ switch (type) {
613
+ case LoaderErrors.EARLY_EOF: {
614
+ if (!this._config.isLive) {
615
+ // Do internal http reconnect if not live stream
616
+ if (this._totalLength) {
617
+ let nextFrom = this._currentRange.to + 1;
618
+ if (nextFrom < this._totalLength) {
619
+ Log.w(this.TAG, 'Connection lost, trying reconnect...');
620
+ this._isEarlyEofReconnecting = true;
621
+ this._internalSeek(nextFrom, false);
622
+ }
623
+ return;
624
+ }
625
+ // else: We don't know totalLength, throw UnrecoverableEarlyEof
626
+ }
627
+ // live stream: throw UnrecoverableEarlyEof error to upper-layer
628
+ type = LoaderErrors.UNRECOVERABLE_EARLY_EOF;
629
+ break;
630
+ }
631
+ case LoaderErrors.UNRECOVERABLE_EARLY_EOF:
632
+ case LoaderErrors.CONNECTING_TIMEOUT:
633
+ case LoaderErrors.HTTP_STATUS_CODE_INVALID:
634
+ case LoaderErrors.EXCEPTION:
635
+ break;
636
+ }
637
+
638
+ if (this._onError) {
639
+ this._onError(type, data);
640
+ } else {
641
+ throw new RuntimeException('IOException: ' + data.msg);
642
+ }
643
+ }
644
+
645
+ }
646
+
647
+ export default IOController;