hls.js 1.5.7-0.canary.10040 → 1.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -2
- package/dist/hls-demo.js +0 -10
- package/dist/hls-demo.js.map +1 -1
- package/dist/hls.js +1283 -2293
- package/dist/hls.js.d.ts +84 -97
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +1030 -1435
- package/dist/hls.light.js.map +1 -1
- package/dist/hls.light.min.js +1 -1
- package/dist/hls.light.min.js.map +1 -1
- package/dist/hls.light.mjs +809 -1209
- package/dist/hls.light.mjs.map +1 -1
- package/dist/hls.min.js +1 -1
- package/dist/hls.min.js.map +1 -1
- package/dist/hls.mjs +1039 -2030
- package/dist/hls.mjs.map +1 -1
- package/dist/hls.worker.js +1 -1
- package/dist/hls.worker.js.map +1 -1
- package/package.json +22 -22
- package/src/config.ts +2 -3
- package/src/controller/abr-controller.ts +20 -24
- package/src/controller/audio-stream-controller.ts +74 -68
- package/src/controller/audio-track-controller.ts +1 -1
- package/src/controller/base-playlist-controller.ts +8 -20
- package/src/controller/base-stream-controller.ts +36 -157
- package/src/controller/buffer-controller.ts +99 -226
- package/src/controller/buffer-operation-queue.ts +19 -16
- package/src/controller/cap-level-controller.ts +2 -2
- package/src/controller/cmcd-controller.ts +6 -27
- package/src/controller/content-steering-controller.ts +6 -8
- package/src/controller/eme-controller.ts +22 -9
- package/src/controller/error-controller.ts +8 -6
- package/src/controller/fps-controller.ts +3 -2
- package/src/controller/fragment-tracker.ts +11 -15
- package/src/controller/gap-controller.ts +16 -43
- package/src/controller/latency-controller.ts +11 -9
- package/src/controller/level-controller.ts +18 -12
- package/src/controller/stream-controller.ts +31 -36
- package/src/controller/subtitle-stream-controller.ts +40 -28
- package/src/controller/subtitle-track-controller.ts +3 -5
- package/src/controller/timeline-controller.ts +30 -23
- package/src/crypt/aes-crypto.ts +2 -21
- package/src/crypt/decrypter.ts +18 -32
- package/src/crypt/fast-aes-key.ts +5 -24
- package/src/demux/audio/adts.ts +4 -9
- package/src/demux/sample-aes.ts +0 -2
- package/src/demux/transmuxer-interface.ts +12 -4
- package/src/demux/transmuxer-worker.ts +4 -4
- package/src/demux/transmuxer.ts +3 -16
- package/src/demux/tsdemuxer.ts +37 -71
- package/src/demux/video/avc-video-parser.ts +119 -208
- package/src/demux/video/base-video-parser.ts +2 -134
- package/src/demux/video/exp-golomb.ts +208 -0
- package/src/events.ts +0 -7
- package/src/hls.ts +37 -49
- package/src/loader/fragment-loader.ts +2 -9
- package/src/loader/key-loader.ts +0 -2
- package/src/loader/level-key.ts +9 -10
- package/src/loader/playlist-loader.ts +5 -4
- package/src/remux/mp4-generator.ts +1 -196
- package/src/remux/mp4-remuxer.ts +7 -23
- package/src/task-loop.ts +2 -5
- package/src/types/component-api.ts +0 -2
- package/src/types/demuxer.ts +0 -3
- package/src/types/events.ts +0 -4
- package/src/utils/buffer-helper.ts +31 -12
- package/src/utils/codecs.ts +5 -34
- package/src/utils/logger.ts +24 -54
- package/src/utils/mp4-tools.ts +2 -4
- package/src/crypt/decrypter-aes-mode.ts +0 -4
- package/src/demux/video/hevc-video-parser.ts +0 -746
- package/src/utils/encryption-methods-util.ts +0 -21
package/dist/hls.light.js
CHANGED
@@ -5,21 +5,6 @@
|
|
5
5
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Hls = factory());
|
6
6
|
})(this, (function () { 'use strict';
|
7
7
|
|
8
|
-
function _construct(t, e, r) {
|
9
|
-
if (_isNativeReflectConstruct()) return Reflect.construct.apply(null, arguments);
|
10
|
-
var o = [null];
|
11
|
-
o.push.apply(o, e);
|
12
|
-
var p = new (t.bind.apply(t, o))();
|
13
|
-
return r && _setPrototypeOf(p, r.prototype), p;
|
14
|
-
}
|
15
|
-
function _isNativeReflectConstruct() {
|
16
|
-
try {
|
17
|
-
var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
|
18
|
-
} catch (t) {}
|
19
|
-
return (_isNativeReflectConstruct = function () {
|
20
|
-
return !!t;
|
21
|
-
})();
|
22
|
-
}
|
23
8
|
function ownKeys(e, r) {
|
24
9
|
var t = Object.keys(e);
|
25
10
|
if (Object.getOwnPropertySymbols) {
|
@@ -118,6 +103,32 @@
|
|
118
103
|
};
|
119
104
|
return _setPrototypeOf(o, p);
|
120
105
|
}
|
106
|
+
function _isNativeReflectConstruct() {
|
107
|
+
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
|
108
|
+
if (Reflect.construct.sham) return false;
|
109
|
+
if (typeof Proxy === "function") return true;
|
110
|
+
try {
|
111
|
+
Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
|
112
|
+
return true;
|
113
|
+
} catch (e) {
|
114
|
+
return false;
|
115
|
+
}
|
116
|
+
}
|
117
|
+
function _construct(Parent, args, Class) {
|
118
|
+
if (_isNativeReflectConstruct()) {
|
119
|
+
_construct = Reflect.construct.bind();
|
120
|
+
} else {
|
121
|
+
_construct = function _construct(Parent, args, Class) {
|
122
|
+
var a = [null];
|
123
|
+
a.push.apply(a, args);
|
124
|
+
var Constructor = Function.bind.apply(Parent, a);
|
125
|
+
var instance = new Constructor();
|
126
|
+
if (Class) _setPrototypeOf(instance, Class.prototype);
|
127
|
+
return instance;
|
128
|
+
};
|
129
|
+
}
|
130
|
+
return _construct.apply(null, arguments);
|
131
|
+
}
|
121
132
|
function _isNativeFunction(fn) {
|
122
133
|
try {
|
123
134
|
return Function.toString.call(fn).indexOf("[native code]") !== -1;
|
@@ -352,7 +363,6 @@
|
|
352
363
|
Events["MEDIA_ATTACHED"] = "hlsMediaAttached";
|
353
364
|
Events["MEDIA_DETACHING"] = "hlsMediaDetaching";
|
354
365
|
Events["MEDIA_DETACHED"] = "hlsMediaDetached";
|
355
|
-
Events["MEDIA_ENDED"] = "hlsMediaEnded";
|
356
366
|
Events["BUFFER_RESET"] = "hlsBufferReset";
|
357
367
|
Events["BUFFER_CODECS"] = "hlsBufferCodecs";
|
358
368
|
Events["BUFFER_CREATED"] = "hlsBufferCreated";
|
@@ -466,6 +476,61 @@
|
|
466
476
|
return ErrorDetails;
|
467
477
|
}({});
|
468
478
|
|
479
|
+
var noop = function noop() {};
|
480
|
+
var fakeLogger = {
|
481
|
+
trace: noop,
|
482
|
+
debug: noop,
|
483
|
+
log: noop,
|
484
|
+
warn: noop,
|
485
|
+
info: noop,
|
486
|
+
error: noop
|
487
|
+
};
|
488
|
+
var exportedLogger = fakeLogger;
|
489
|
+
|
490
|
+
// let lastCallTime;
|
491
|
+
// function formatMsgWithTimeInfo(type, msg) {
|
492
|
+
// const now = Date.now();
|
493
|
+
// const diff = lastCallTime ? '+' + (now - lastCallTime) : '0';
|
494
|
+
// lastCallTime = now;
|
495
|
+
// msg = (new Date(now)).toISOString() + ' | [' + type + '] > ' + msg + ' ( ' + diff + ' ms )';
|
496
|
+
// return msg;
|
497
|
+
// }
|
498
|
+
|
499
|
+
function consolePrintFn(type) {
|
500
|
+
var func = self.console[type];
|
501
|
+
if (func) {
|
502
|
+
return func.bind(self.console, "[" + type + "] >");
|
503
|
+
}
|
504
|
+
return noop;
|
505
|
+
}
|
506
|
+
function exportLoggerFunctions(debugConfig) {
|
507
|
+
for (var _len = arguments.length, functions = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
508
|
+
functions[_key - 1] = arguments[_key];
|
509
|
+
}
|
510
|
+
functions.forEach(function (type) {
|
511
|
+
exportedLogger[type] = debugConfig[type] ? debugConfig[type].bind(debugConfig) : consolePrintFn(type);
|
512
|
+
});
|
513
|
+
}
|
514
|
+
function enableLogs(debugConfig, id) {
|
515
|
+
// check that console is available
|
516
|
+
if (typeof console === 'object' && debugConfig === true || typeof debugConfig === 'object') {
|
517
|
+
exportLoggerFunctions(debugConfig,
|
518
|
+
// Remove out from list here to hard-disable a log-level
|
519
|
+
// 'trace',
|
520
|
+
'debug', 'log', 'info', 'warn', 'error');
|
521
|
+
// Some browsers don't allow to use bind on console object anyway
|
522
|
+
// fallback to default if needed
|
523
|
+
try {
|
524
|
+
exportedLogger.log("Debug logs enabled for \"" + id + "\" in hls.js version " + "1.5.7");
|
525
|
+
} catch (e) {
|
526
|
+
exportedLogger = fakeLogger;
|
527
|
+
}
|
528
|
+
} else {
|
529
|
+
exportedLogger = fakeLogger;
|
530
|
+
}
|
531
|
+
}
|
532
|
+
var logger = exportedLogger;
|
533
|
+
|
469
534
|
var DECIMAL_RESOLUTION_REGEX = /^(\d+)x(\d+)$/;
|
470
535
|
var ATTR_LIST_REGEX = /(.+?)=(".*?"|.*?)(?:,|$)/g;
|
471
536
|
|
@@ -554,77 +619,6 @@
|
|
554
619
|
return AttrList;
|
555
620
|
}();
|
556
621
|
|
557
|
-
var Logger = function Logger(label, logger) {
|
558
|
-
this.trace = void 0;
|
559
|
-
this.debug = void 0;
|
560
|
-
this.log = void 0;
|
561
|
-
this.warn = void 0;
|
562
|
-
this.info = void 0;
|
563
|
-
this.error = void 0;
|
564
|
-
var lb = "[" + label + "]:";
|
565
|
-
this.trace = noop;
|
566
|
-
this.debug = logger.debug.bind(null, lb);
|
567
|
-
this.log = logger.log.bind(null, lb);
|
568
|
-
this.warn = logger.warn.bind(null, lb);
|
569
|
-
this.info = logger.info.bind(null, lb);
|
570
|
-
this.error = logger.error.bind(null, lb);
|
571
|
-
};
|
572
|
-
var noop = function noop() {};
|
573
|
-
var fakeLogger = {
|
574
|
-
trace: noop,
|
575
|
-
debug: noop,
|
576
|
-
log: noop,
|
577
|
-
warn: noop,
|
578
|
-
info: noop,
|
579
|
-
error: noop
|
580
|
-
};
|
581
|
-
function createLogger() {
|
582
|
-
return _extends({}, fakeLogger);
|
583
|
-
}
|
584
|
-
|
585
|
-
// let lastCallTime;
|
586
|
-
// function formatMsgWithTimeInfo(type, msg) {
|
587
|
-
// const now = Date.now();
|
588
|
-
// const diff = lastCallTime ? '+' + (now - lastCallTime) : '0';
|
589
|
-
// lastCallTime = now;
|
590
|
-
// msg = (new Date(now)).toISOString() + ' | [' + type + '] > ' + msg + ' ( ' + diff + ' ms )';
|
591
|
-
// return msg;
|
592
|
-
// }
|
593
|
-
|
594
|
-
function consolePrintFn(type, id) {
|
595
|
-
var func = self.console[type];
|
596
|
-
return func ? func.bind(self.console, (id ? '[' + id + '] ' : '') + "[" + type + "] >") : noop;
|
597
|
-
}
|
598
|
-
function getLoggerFn(key, debugConfig, id) {
|
599
|
-
return debugConfig[key] ? debugConfig[key].bind(debugConfig) : consolePrintFn(key, id);
|
600
|
-
}
|
601
|
-
var exportedLogger = createLogger();
|
602
|
-
function enableLogs(debugConfig, context, id) {
|
603
|
-
// check that console is available
|
604
|
-
var newLogger = createLogger();
|
605
|
-
if (typeof console === 'object' && debugConfig === true || typeof debugConfig === 'object') {
|
606
|
-
var keys = [
|
607
|
-
// Remove out from list here to hard-disable a log-level
|
608
|
-
// 'trace',
|
609
|
-
'debug', 'log', 'info', 'warn', 'error'];
|
610
|
-
keys.forEach(function (key) {
|
611
|
-
newLogger[key] = getLoggerFn(key, debugConfig, id);
|
612
|
-
});
|
613
|
-
// Some browsers don't allow to use bind on console object anyway
|
614
|
-
// fallback to default if needed
|
615
|
-
try {
|
616
|
-
newLogger.log("Debug logs enabled for \"" + context + "\" in hls.js version " + "1.5.7-0.canary.10040");
|
617
|
-
} catch (e) {
|
618
|
-
/* log fn threw an exception. All logger methods are no-ops. */
|
619
|
-
return createLogger();
|
620
|
-
}
|
621
|
-
}
|
622
|
-
// global exported logger uses the log methods from last call to `enableLogs`
|
623
|
-
_extends(exportedLogger, newLogger);
|
624
|
-
return newLogger;
|
625
|
-
}
|
626
|
-
var logger = exportedLogger;
|
627
|
-
|
628
622
|
// Avoid exporting const enum so that these values can be inlined
|
629
623
|
|
630
624
|
function isDateRangeCueAttribute(attrName) {
|
@@ -1179,30 +1173,10 @@
|
|
1179
1173
|
return LevelDetails;
|
1180
1174
|
}();
|
1181
1175
|
|
1182
|
-
var DecrypterAesMode = {
|
1183
|
-
cbc: 0,
|
1184
|
-
ctr: 1
|
1185
|
-
};
|
1186
|
-
|
1187
|
-
function isFullSegmentEncryption(method) {
|
1188
|
-
return method === 'AES-128' || method === 'AES-256' || method === 'AES-256-CTR';
|
1189
|
-
}
|
1190
|
-
function getAesModeFromFullSegmentMethod(method) {
|
1191
|
-
switch (method) {
|
1192
|
-
case 'AES-128':
|
1193
|
-
case 'AES-256':
|
1194
|
-
return DecrypterAesMode.cbc;
|
1195
|
-
case 'AES-256-CTR':
|
1196
|
-
return DecrypterAesMode.ctr;
|
1197
|
-
default:
|
1198
|
-
throw new Error("invalid full segment method " + method);
|
1199
|
-
}
|
1200
|
-
}
|
1201
|
-
|
1202
1176
|
// This file is inserted as a shim for modules which we do not want to include into the distro.
|
1203
1177
|
// This replacement is done in the "alias" plugin of the rollup config.
|
1204
1178
|
var empty = undefined;
|
1205
|
-
var
|
1179
|
+
var Cues = /*@__PURE__*/getDefaultExportFromCjs(empty);
|
1206
1180
|
|
1207
1181
|
function sliceUint8(array, start, end) {
|
1208
1182
|
// @ts-expect-error This polyfills IE11 usage of Uint8Array slice.
|
@@ -1837,7 +1811,7 @@
|
|
1837
1811
|
{
|
1838
1812
|
var codecBox = findBox(sampleEntries, [fourCC])[0];
|
1839
1813
|
var esdsBox = findBox(codecBox.subarray(28), ['esds'])[0];
|
1840
|
-
if (esdsBox && esdsBox.length >
|
1814
|
+
if (esdsBox && esdsBox.length > 12) {
|
1841
1815
|
var i = 4;
|
1842
1816
|
// ES Descriptor tag
|
1843
1817
|
if (esdsBox[i++] !== 0x03) {
|
@@ -1952,9 +1926,7 @@
|
|
1952
1926
|
}
|
1953
1927
|
function skipBERInteger(bytes, i) {
|
1954
1928
|
var limit = i + 5;
|
1955
|
-
while (bytes[i++] & 0x80 && i < limit) {
|
1956
|
-
/* do nothing */
|
1957
|
-
}
|
1929
|
+
while (bytes[i++] & 0x80 && i < limit) {}
|
1958
1930
|
return i;
|
1959
1931
|
}
|
1960
1932
|
function toHex(x) {
|
@@ -2656,13 +2628,13 @@
|
|
2656
2628
|
this.keyFormatVersions = formatversions;
|
2657
2629
|
this.iv = iv;
|
2658
2630
|
this.encrypted = method ? method !== 'NONE' : false;
|
2659
|
-
this.isCommonEncryption = this.encrypted &&
|
2631
|
+
this.isCommonEncryption = this.encrypted && method !== 'AES-128';
|
2660
2632
|
}
|
2661
2633
|
var _proto = LevelKey.prototype;
|
2662
2634
|
_proto.isSupported = function isSupported() {
|
2663
2635
|
// If it's Segment encryption or No encryption, just select that key system
|
2664
2636
|
if (this.method) {
|
2665
|
-
if (
|
2637
|
+
if (this.method === 'AES-128' || this.method === 'NONE') {
|
2666
2638
|
return true;
|
2667
2639
|
}
|
2668
2640
|
if (this.keyFormat === 'identity') {
|
@@ -2676,13 +2648,14 @@
|
|
2676
2648
|
if (!this.encrypted || !this.uri) {
|
2677
2649
|
return null;
|
2678
2650
|
}
|
2679
|
-
if (
|
2651
|
+
if (this.method === 'AES-128' && this.uri && !this.iv) {
|
2680
2652
|
if (typeof sn !== 'number') {
|
2681
2653
|
// We are fetching decryption data for a initialization segment
|
2682
|
-
// If the segment was encrypted with AES-128
|
2654
|
+
// If the segment was encrypted with AES-128
|
2683
2655
|
// It must have an IV defined. We cannot substitute the Segment Number in.
|
2684
|
-
|
2685
|
-
|
2656
|
+
if (this.method === 'AES-128' && !this.iv) {
|
2657
|
+
logger.warn("missing IV for initialization segment with method=\"" + this.method + "\" - compliance issue");
|
2658
|
+
}
|
2686
2659
|
// Explicitly set sn to resulting value from implicit conversions 'initSegment' values for IV generation.
|
2687
2660
|
sn = 0;
|
2688
2661
|
}
|
@@ -2844,28 +2817,23 @@
|
|
2844
2817
|
if (CODEC_COMPATIBLE_NAMES[lowerCaseCodec]) {
|
2845
2818
|
return CODEC_COMPATIBLE_NAMES[lowerCaseCodec];
|
2846
2819
|
}
|
2820
|
+
|
2821
|
+
// Idealy fLaC and Opus would be first (spec-compliant) but
|
2822
|
+
// some browsers will report that fLaC is supported then fail.
|
2823
|
+
// see: https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
|
2847
2824
|
var codecsToCheck = {
|
2848
|
-
// Idealy fLaC and Opus would be first (spec-compliant) but
|
2849
|
-
// some browsers will report that fLaC is supported then fail.
|
2850
|
-
// see: https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
|
2851
2825
|
flac: ['flac', 'fLaC', 'FLAC'],
|
2852
|
-
opus: ['opus', 'Opus']
|
2853
|
-
// Replace audio codec info if browser does not support mp4a.40.34,
|
2854
|
-
// and demuxer can fallback to 'audio/mpeg' or 'audio/mp4;codecs="mp3"'
|
2855
|
-
'mp4a.40.34': ['mp3']
|
2826
|
+
opus: ['opus', 'Opus']
|
2856
2827
|
}[lowerCaseCodec];
|
2857
2828
|
for (var i = 0; i < codecsToCheck.length; i++) {
|
2858
|
-
var _getMediaSource;
|
2859
2829
|
if (isCodecMediaSourceSupported(codecsToCheck[i], 'audio', preferManagedMediaSource)) {
|
2860
2830
|
CODEC_COMPATIBLE_NAMES[lowerCaseCodec] = codecsToCheck[i];
|
2861
2831
|
return codecsToCheck[i];
|
2862
|
-
} else if (codecsToCheck[i] === 'mp3' && (_getMediaSource = getMediaSource(preferManagedMediaSource)) != null && _getMediaSource.isTypeSupported('audio/mpeg')) {
|
2863
|
-
return '';
|
2864
2832
|
}
|
2865
2833
|
}
|
2866
2834
|
return lowerCaseCodec;
|
2867
2835
|
}
|
2868
|
-
var AUDIO_CODEC_REGEXP = /flac|opus
|
2836
|
+
var AUDIO_CODEC_REGEXP = /flac|opus/i;
|
2869
2837
|
function getCodecCompatibleName(codec, preferManagedMediaSource) {
|
2870
2838
|
if (preferManagedMediaSource === void 0) {
|
2871
2839
|
preferManagedMediaSource = true;
|
@@ -2893,18 +2861,6 @@
|
|
2893
2861
|
}
|
2894
2862
|
return codec;
|
2895
2863
|
}
|
2896
|
-
function getM2TSSupportedAudioTypes(preferManagedMediaSource) {
|
2897
|
-
var MediaSource = getMediaSource(preferManagedMediaSource) || {
|
2898
|
-
isTypeSupported: function isTypeSupported() {
|
2899
|
-
return false;
|
2900
|
-
}
|
2901
|
-
};
|
2902
|
-
return {
|
2903
|
-
mpeg: MediaSource.isTypeSupported('audio/mpeg'),
|
2904
|
-
mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'),
|
2905
|
-
ac3: false
|
2906
|
-
};
|
2907
|
-
}
|
2908
2864
|
|
2909
2865
|
var MASTER_PLAYLIST_REGEX = /#EXT-X-STREAM-INF:([^\r\n]*)(?:[\r\n](?:#[^\r\n]*)?)*([^\r\n]+)|#EXT-X-(SESSION-DATA|SESSION-KEY|DEFINE|CONTENT-STEERING|START):([^\r\n]*)[\r\n]+/g;
|
2910
2866
|
var MASTER_PLAYLIST_MEDIA_REGEX = /#EXT-X-MEDIA:(.*)/g;
|
@@ -3705,10 +3661,10 @@
|
|
3705
3661
|
var loaderContext = loader.context;
|
3706
3662
|
if (loaderContext && loaderContext.url === context.url && loaderContext.level === context.level) {
|
3707
3663
|
// same URL can't overlap
|
3708
|
-
|
3664
|
+
logger.trace('[playlist-loader]: playlist request ongoing');
|
3709
3665
|
return;
|
3710
3666
|
}
|
3711
|
-
|
3667
|
+
logger.log("[playlist-loader]: aborting previous loader for type: " + context.type);
|
3712
3668
|
loader.abort();
|
3713
3669
|
}
|
3714
3670
|
|
@@ -3818,7 +3774,7 @@
|
|
3818
3774
|
// alt audio rendition in which quality levels (main)
|
3819
3775
|
// contains both audio+video. but with mixed audio track not signaled
|
3820
3776
|
if (!embeddedAudioFound && levels[0].audioCodec && !levels[0].attrs.AUDIO) {
|
3821
|
-
|
3777
|
+
logger.log('[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one');
|
3822
3778
|
audioTracks.unshift({
|
3823
3779
|
type: 'main',
|
3824
3780
|
name: 'main',
|
@@ -3918,7 +3874,7 @@
|
|
3918
3874
|
message += " id: " + context.id + " group-id: \"" + context.groupId + "\"";
|
3919
3875
|
}
|
3920
3876
|
var error = new Error(message);
|
3921
|
-
|
3877
|
+
logger.warn("[playlist-loader]: " + message);
|
3922
3878
|
var details = ErrorDetails.UNKNOWN;
|
3923
3879
|
var fatal = false;
|
3924
3880
|
var loader = this.getInternalLoader(context);
|
@@ -4479,43 +4435,8 @@
|
|
4479
4435
|
this.currentTime = 0;
|
4480
4436
|
this.stallCount = 0;
|
4481
4437
|
this._latency = null;
|
4482
|
-
this.
|
4483
|
-
|
4484
|
-
levelDetails = _this.levelDetails;
|
4485
|
-
if (!media || !levelDetails) {
|
4486
|
-
return;
|
4487
|
-
}
|
4488
|
-
_this.currentTime = media.currentTime;
|
4489
|
-
var latency = _this.computeLatency();
|
4490
|
-
if (latency === null) {
|
4491
|
-
return;
|
4492
|
-
}
|
4493
|
-
_this._latency = latency;
|
4494
|
-
|
4495
|
-
// Adapt playbackRate to meet target latency in low-latency mode
|
4496
|
-
var _this$config = _this.config,
|
4497
|
-
lowLatencyMode = _this$config.lowLatencyMode,
|
4498
|
-
maxLiveSyncPlaybackRate = _this$config.maxLiveSyncPlaybackRate;
|
4499
|
-
if (!lowLatencyMode || maxLiveSyncPlaybackRate === 1 || !levelDetails.live) {
|
4500
|
-
return;
|
4501
|
-
}
|
4502
|
-
var targetLatency = _this.targetLatency;
|
4503
|
-
if (targetLatency === null) {
|
4504
|
-
return;
|
4505
|
-
}
|
4506
|
-
var distanceFromTarget = latency - targetLatency;
|
4507
|
-
// Only adjust playbackRate when within one target duration of targetLatency
|
4508
|
-
// and more than one second from under-buffering.
|
4509
|
-
// Playback further than one target duration from target can be considered DVR playback.
|
4510
|
-
var liveMinLatencyDuration = Math.min(_this.maxLatency, targetLatency + levelDetails.targetduration);
|
4511
|
-
var inLiveRange = distanceFromTarget < liveMinLatencyDuration;
|
4512
|
-
if (inLiveRange && distanceFromTarget > 0.05 && _this.forwardBufferLength > 1) {
|
4513
|
-
var max = Math.min(2, Math.max(1.0, maxLiveSyncPlaybackRate));
|
4514
|
-
var rate = Math.round(2 / (1 + Math.exp(-0.75 * distanceFromTarget - _this.edgeStalled)) * 20) / 20;
|
4515
|
-
media.playbackRate = Math.min(max, Math.max(1, rate));
|
4516
|
-
} else if (media.playbackRate !== 1 && media.playbackRate !== 0) {
|
4517
|
-
media.playbackRate = 1;
|
4518
|
-
}
|
4438
|
+
this.timeupdateHandler = function () {
|
4439
|
+
return _this.timeupdate();
|
4519
4440
|
};
|
4520
4441
|
this.hls = hls;
|
4521
4442
|
this.config = hls.config;
|
@@ -4527,7 +4448,7 @@
|
|
4527
4448
|
this.onMediaDetaching();
|
4528
4449
|
this.levelDetails = null;
|
4529
4450
|
// @ts-ignore
|
4530
|
-
this.hls = null;
|
4451
|
+
this.hls = this.timeupdateHandler = null;
|
4531
4452
|
};
|
4532
4453
|
_proto.registerListeners = function registerListeners() {
|
4533
4454
|
this.hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
|
@@ -4545,11 +4466,11 @@
|
|
4545
4466
|
};
|
4546
4467
|
_proto.onMediaAttached = function onMediaAttached(event, data) {
|
4547
4468
|
this.media = data.media;
|
4548
|
-
this.media.addEventListener('timeupdate', this.
|
4469
|
+
this.media.addEventListener('timeupdate', this.timeupdateHandler);
|
4549
4470
|
};
|
4550
4471
|
_proto.onMediaDetaching = function onMediaDetaching() {
|
4551
4472
|
if (this.media) {
|
4552
|
-
this.media.removeEventListener('timeupdate', this.
|
4473
|
+
this.media.removeEventListener('timeupdate', this.timeupdateHandler);
|
4553
4474
|
this.media = null;
|
4554
4475
|
}
|
4555
4476
|
};
|
@@ -4562,10 +4483,10 @@
|
|
4562
4483
|
var details = _ref.details;
|
4563
4484
|
this.levelDetails = details;
|
4564
4485
|
if (details.advanced) {
|
4565
|
-
this.
|
4486
|
+
this.timeupdate();
|
4566
4487
|
}
|
4567
4488
|
if (!details.live && this.media) {
|
4568
|
-
this.media.removeEventListener('timeupdate', this.
|
4489
|
+
this.media.removeEventListener('timeupdate', this.timeupdateHandler);
|
4569
4490
|
}
|
4570
4491
|
};
|
4571
4492
|
_proto.onError = function onError(event, data) {
|
@@ -4575,7 +4496,45 @@
|
|
4575
4496
|
}
|
4576
4497
|
this.stallCount++;
|
4577
4498
|
if ((_this$levelDetails = this.levelDetails) != null && _this$levelDetails.live) {
|
4578
|
-
|
4499
|
+
logger.warn('[playback-rate-controller]: Stall detected, adjusting target latency');
|
4500
|
+
}
|
4501
|
+
};
|
4502
|
+
_proto.timeupdate = function timeupdate() {
|
4503
|
+
var media = this.media,
|
4504
|
+
levelDetails = this.levelDetails;
|
4505
|
+
if (!media || !levelDetails) {
|
4506
|
+
return;
|
4507
|
+
}
|
4508
|
+
this.currentTime = media.currentTime;
|
4509
|
+
var latency = this.computeLatency();
|
4510
|
+
if (latency === null) {
|
4511
|
+
return;
|
4512
|
+
}
|
4513
|
+
this._latency = latency;
|
4514
|
+
|
4515
|
+
// Adapt playbackRate to meet target latency in low-latency mode
|
4516
|
+
var _this$config = this.config,
|
4517
|
+
lowLatencyMode = _this$config.lowLatencyMode,
|
4518
|
+
maxLiveSyncPlaybackRate = _this$config.maxLiveSyncPlaybackRate;
|
4519
|
+
if (!lowLatencyMode || maxLiveSyncPlaybackRate === 1 || !levelDetails.live) {
|
4520
|
+
return;
|
4521
|
+
}
|
4522
|
+
var targetLatency = this.targetLatency;
|
4523
|
+
if (targetLatency === null) {
|
4524
|
+
return;
|
4525
|
+
}
|
4526
|
+
var distanceFromTarget = latency - targetLatency;
|
4527
|
+
// Only adjust playbackRate when within one target duration of targetLatency
|
4528
|
+
// and more than one second from under-buffering.
|
4529
|
+
// Playback further than one target duration from target can be considered DVR playback.
|
4530
|
+
var liveMinLatencyDuration = Math.min(this.maxLatency, targetLatency + levelDetails.targetduration);
|
4531
|
+
var inLiveRange = distanceFromTarget < liveMinLatencyDuration;
|
4532
|
+
if (inLiveRange && distanceFromTarget > 0.05 && this.forwardBufferLength > 1) {
|
4533
|
+
var max = Math.min(2, Math.max(1.0, maxLiveSyncPlaybackRate));
|
4534
|
+
var rate = Math.round(2 / (1 + Math.exp(-0.75 * distanceFromTarget - this.edgeStalled)) * 20) / 20;
|
4535
|
+
media.playbackRate = Math.min(max, Math.max(1, rate));
|
4536
|
+
} else if (media.playbackRate !== 1 && media.playbackRate !== 0) {
|
4537
|
+
media.playbackRate = 1;
|
4579
4538
|
}
|
4580
4539
|
};
|
4581
4540
|
_proto.estimateLiveEdge = function estimateLiveEdge() {
|
@@ -5483,17 +5442,19 @@
|
|
5483
5442
|
MoveAllAlternatesMatchingHDCP: 2,
|
5484
5443
|
SwitchToSDR: 4
|
5485
5444
|
}; // Reserved for future use
|
5486
|
-
var ErrorController = /*#__PURE__*/function (
|
5487
|
-
_inheritsLoose(ErrorController, _Logger);
|
5445
|
+
var ErrorController = /*#__PURE__*/function () {
|
5488
5446
|
function ErrorController(hls) {
|
5489
|
-
|
5490
|
-
|
5491
|
-
|
5492
|
-
|
5493
|
-
|
5494
|
-
|
5495
|
-
|
5496
|
-
|
5447
|
+
this.hls = void 0;
|
5448
|
+
this.playlistError = 0;
|
5449
|
+
this.penalizedRenditions = {};
|
5450
|
+
this.log = void 0;
|
5451
|
+
this.warn = void 0;
|
5452
|
+
this.error = void 0;
|
5453
|
+
this.hls = hls;
|
5454
|
+
this.log = logger.log.bind(logger, "[info]:");
|
5455
|
+
this.warn = logger.warn.bind(logger, "[warning]:");
|
5456
|
+
this.error = logger.error.bind(logger, "[error]:");
|
5457
|
+
this.registerListeners();
|
5497
5458
|
}
|
5498
5459
|
var _proto = ErrorController.prototype;
|
5499
5460
|
_proto.registerListeners = function registerListeners() {
|
@@ -5849,19 +5810,19 @@
|
|
5849
5810
|
}
|
5850
5811
|
};
|
5851
5812
|
return ErrorController;
|
5852
|
-
}(
|
5813
|
+
}();
|
5853
5814
|
|
5854
|
-
var BasePlaylistController = /*#__PURE__*/function (
|
5855
|
-
_inheritsLoose(BasePlaylistController, _Logger);
|
5815
|
+
var BasePlaylistController = /*#__PURE__*/function () {
|
5856
5816
|
function BasePlaylistController(hls, logPrefix) {
|
5857
|
-
|
5858
|
-
|
5859
|
-
|
5860
|
-
|
5861
|
-
|
5862
|
-
|
5863
|
-
|
5864
|
-
|
5817
|
+
this.hls = void 0;
|
5818
|
+
this.timer = -1;
|
5819
|
+
this.requestScheduled = -1;
|
5820
|
+
this.canLoad = false;
|
5821
|
+
this.log = void 0;
|
5822
|
+
this.warn = void 0;
|
5823
|
+
this.log = logger.log.bind(logger, logPrefix + ":");
|
5824
|
+
this.warn = logger.warn.bind(logger, logPrefix + ":");
|
5825
|
+
this.hls = hls;
|
5865
5826
|
}
|
5866
5827
|
var _proto = BasePlaylistController.prototype;
|
5867
5828
|
_proto.destroy = function destroy() {
|
@@ -5894,7 +5855,7 @@
|
|
5894
5855
|
try {
|
5895
5856
|
uri = new self.URL(attr.URI, previous.url).href;
|
5896
5857
|
} catch (error) {
|
5897
|
-
|
5858
|
+
logger.warn("Could not construct new URL for Rendition Report: " + error);
|
5898
5859
|
uri = attr.URI || '';
|
5899
5860
|
}
|
5900
5861
|
// Use exact match. Otherwise, the last partial match, if any, will be used
|
@@ -5933,7 +5894,7 @@
|
|
5933
5894
|
return this.timer === -1 && this.requestScheduled === -1 && this.shouldLoadPlaylist(playlist);
|
5934
5895
|
};
|
5935
5896
|
_proto.playlistLoaded = function playlistLoaded(index, data, previousDetails) {
|
5936
|
-
var
|
5897
|
+
var _this = this;
|
5937
5898
|
var details = data.details,
|
5938
5899
|
stats = data.stats;
|
5939
5900
|
|
@@ -5980,12 +5941,7 @@
|
|
5980
5941
|
var cdnAge = lastAdvanced + details.ageHeader;
|
5981
5942
|
var currentGoal = Math.min(cdnAge - details.partTarget, details.targetduration * 1.5);
|
5982
5943
|
if (currentGoal > 0) {
|
5983
|
-
if (
|
5984
|
-
// Omit segment and part directives when the last response was more than 3 target durations ago,
|
5985
|
-
this.log("Playlist last advanced " + lastAdvanced.toFixed(2) + "s ago. Omitting segment and part directives.");
|
5986
|
-
msn = undefined;
|
5987
|
-
part = undefined;
|
5988
|
-
} else if (previousDetails != null && previousDetails.tuneInGoal && cdnAge - details.partTarget > previousDetails.tuneInGoal) {
|
5944
|
+
if (previousDetails && currentGoal > previousDetails.tuneInGoal) {
|
5989
5945
|
// If we attempted to get the next or latest playlist update, but currentGoal increased,
|
5990
5946
|
// then we either can't catchup, or the "age" header cannot be trusted.
|
5991
5947
|
this.warn("CDN Tune-in goal increased from: " + previousDetails.tuneInGoal + " to: " + currentGoal + " with playlist age: " + details.age);
|
@@ -6043,7 +5999,7 @@
|
|
6043
5999
|
// );
|
6044
6000
|
|
6045
6001
|
this.timer = self.setTimeout(function () {
|
6046
|
-
return
|
6002
|
+
return _this.loadPlaylist(deliveryDirectives);
|
6047
6003
|
}, estimatedTimeUntilUpdate);
|
6048
6004
|
} else {
|
6049
6005
|
this.clearTimer();
|
@@ -6059,7 +6015,7 @@
|
|
6059
6015
|
return new HlsUrlParameters(msn, part, skip);
|
6060
6016
|
};
|
6061
6017
|
_proto.checkRetry = function checkRetry(errorEvent) {
|
6062
|
-
var
|
6018
|
+
var _this2 = this;
|
6063
6019
|
var errorDetails = errorEvent.details;
|
6064
6020
|
var isTimeout = isTimeoutError(errorEvent);
|
6065
6021
|
var errorAction = errorEvent.errorAction;
|
@@ -6083,7 +6039,7 @@
|
|
6083
6039
|
var delay = getRetryDelay(retryConfig, retryCount);
|
6084
6040
|
// Schedule level/track reload
|
6085
6041
|
this.timer = self.setTimeout(function () {
|
6086
|
-
return
|
6042
|
+
return _this2.loadPlaylist();
|
6087
6043
|
}, delay);
|
6088
6044
|
this.warn("Retrying playlist loading " + (retryCount + 1) + "/" + retryConfig.maxNumRetry + " after \"" + errorDetails + "\" in " + delay + "ms");
|
6089
6045
|
}
|
@@ -6094,7 +6050,7 @@
|
|
6094
6050
|
return retry;
|
6095
6051
|
};
|
6096
6052
|
return BasePlaylistController;
|
6097
|
-
}(
|
6053
|
+
}();
|
6098
6054
|
|
6099
6055
|
/*
|
6100
6056
|
* compute an Exponential Weighted moving average
|
@@ -6468,33 +6424,30 @@
|
|
6468
6424
|
}, {});
|
6469
6425
|
}
|
6470
6426
|
|
6471
|
-
var AbrController = /*#__PURE__*/function (
|
6472
|
-
_inheritsLoose(AbrController, _Logger);
|
6427
|
+
var AbrController = /*#__PURE__*/function () {
|
6473
6428
|
function AbrController(_hls) {
|
6474
|
-
var _this;
|
6475
|
-
|
6476
|
-
|
6477
|
-
|
6478
|
-
|
6479
|
-
|
6480
|
-
|
6481
|
-
|
6482
|
-
|
6483
|
-
|
6484
|
-
|
6485
|
-
|
6486
|
-
|
6487
|
-
|
6488
|
-
_this.bwEstimator = void 0;
|
6429
|
+
var _this = this;
|
6430
|
+
this.hls = void 0;
|
6431
|
+
this.lastLevelLoadSec = 0;
|
6432
|
+
this.lastLoadedFragLevel = -1;
|
6433
|
+
this.firstSelection = -1;
|
6434
|
+
this._nextAutoLevel = -1;
|
6435
|
+
this.nextAutoLevelKey = '';
|
6436
|
+
this.audioTracksByGroup = null;
|
6437
|
+
this.codecTiers = null;
|
6438
|
+
this.timer = -1;
|
6439
|
+
this.fragCurrent = null;
|
6440
|
+
this.partCurrent = null;
|
6441
|
+
this.bitrateTestDelay = 0;
|
6442
|
+
this.bwEstimator = void 0;
|
6489
6443
|
/*
|
6490
6444
|
This method monitors the download rate of the current fragment, and will downswitch if that fragment will not load
|
6491
6445
|
quickly enough to prevent underbuffering
|
6492
6446
|
*/
|
6493
|
-
|
6494
|
-
var
|
6495
|
-
|
6496
|
-
|
6497
|
-
hls = _assertThisInitialize.hls;
|
6447
|
+
this._abandonRulesCheck = function () {
|
6448
|
+
var frag = _this.fragCurrent,
|
6449
|
+
part = _this.partCurrent,
|
6450
|
+
hls = _this.hls;
|
6498
6451
|
var autoLevelEnabled = hls.autoLevelEnabled,
|
6499
6452
|
media = hls.media;
|
6500
6453
|
if (!frag || !media) {
|
@@ -6583,22 +6536,21 @@
|
|
6583
6536
|
_this.resetEstimator(nextLoadLevelBitrate);
|
6584
6537
|
}
|
6585
6538
|
_this.clearTimer();
|
6586
|
-
|
6539
|
+
logger.warn("[abr] Fragment " + frag.sn + (part ? ' part ' + part.index : '') + " of level " + frag.level + " is loading too slowly;\n Time to underbuffer: " + bufferStarvationDelay.toFixed(3) + " s\n Estimated load time for current fragment: " + fragLoadedDelay.toFixed(3) + " s\n Estimated load time for down switch fragment: " + fragLevelNextLoadedDelay.toFixed(3) + " s\n TTFB estimate: " + (ttfb | 0) + " ms\n Current BW estimate: " + (isFiniteNumber(bwEstimate) ? bwEstimate | 0 : 'Unknown') + " bps\n New BW estimate: " + (_this.getBwEstimate() | 0) + " bps\n Switching to level " + nextLoadLevel + " @ " + (nextLoadLevelBitrate | 0) + " bps");
|
6587
6540
|
hls.trigger(Events.FRAG_LOAD_EMERGENCY_ABORTED, {
|
6588
6541
|
frag: frag,
|
6589
6542
|
part: part,
|
6590
6543
|
stats: stats
|
6591
6544
|
});
|
6592
6545
|
};
|
6593
|
-
|
6594
|
-
|
6595
|
-
|
6596
|
-
return _this;
|
6546
|
+
this.hls = _hls;
|
6547
|
+
this.bwEstimator = this.initEstimator();
|
6548
|
+
this.registerListeners();
|
6597
6549
|
}
|
6598
6550
|
var _proto = AbrController.prototype;
|
6599
6551
|
_proto.resetEstimator = function resetEstimator(abrEwmaDefaultEstimate) {
|
6600
6552
|
if (abrEwmaDefaultEstimate) {
|
6601
|
-
|
6553
|
+
logger.log("setting initial bwe to " + abrEwmaDefaultEstimate);
|
6602
6554
|
this.hls.config.abrEwmaDefaultEstimate = abrEwmaDefaultEstimate;
|
6603
6555
|
}
|
6604
6556
|
this.firstSelection = -1;
|
@@ -6813,9 +6765,6 @@
|
|
6813
6765
|
var fragCurrent = this.fragCurrent,
|
6814
6766
|
partCurrent = this.partCurrent,
|
6815
6767
|
hls = this.hls;
|
6816
|
-
if (hls.levels.length <= 1) {
|
6817
|
-
return hls.loadLevel;
|
6818
|
-
}
|
6819
6768
|
var maxAutoLevel = hls.maxAutoLevel,
|
6820
6769
|
config = hls.config,
|
6821
6770
|
minAutoLevel = hls.minAutoLevel;
|
@@ -6846,13 +6795,13 @@
|
|
6846
6795
|
// cap maxLoadingDelay and ensure it is not bigger 'than bitrate test' frag duration
|
6847
6796
|
var maxLoadingDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxLoadingDelay) : config.maxLoadingDelay;
|
6848
6797
|
maxStarvationDelay = maxLoadingDelay - bitrateTestDelay;
|
6849
|
-
|
6798
|
+
logger.info("[abr] bitrate test took " + Math.round(1000 * bitrateTestDelay) + "ms, set first fragment max fetchDuration to " + Math.round(1000 * maxStarvationDelay) + " ms");
|
6850
6799
|
// don't use conservative factor on bitrate test
|
6851
6800
|
bwFactor = bwUpFactor = 1;
|
6852
6801
|
}
|
6853
6802
|
}
|
6854
6803
|
var bestLevel = this.findBestLevel(avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay, maxStarvationDelay, bwFactor, bwUpFactor);
|
6855
|
-
|
6804
|
+
logger.info("[abr] " + (bufferStarvationDelay ? 'rebuffering expected' : 'buffer is empty') + ", optimal quality level " + bestLevel);
|
6856
6805
|
if (bestLevel > -1) {
|
6857
6806
|
return bestLevel;
|
6858
6807
|
}
|
@@ -6920,7 +6869,7 @@
|
|
6920
6869
|
currentVideoRange = preferHDR ? videoRanges[videoRanges.length - 1] : videoRanges[0];
|
6921
6870
|
currentFrameRate = minFramerate;
|
6922
6871
|
currentBw = Math.max(currentBw, minBitrate);
|
6923
|
-
|
6872
|
+
logger.log("[abr] picked start tier " + JSON.stringify(startTier));
|
6924
6873
|
} else {
|
6925
6874
|
currentCodecSet = level == null ? void 0 : level.codecSet;
|
6926
6875
|
currentVideoRange = level == null ? void 0 : level.videoRange;
|
@@ -6973,9 +6922,9 @@
|
|
6973
6922
|
var forcedAutoLevel = _this2.forcedAutoLevel;
|
6974
6923
|
if (i !== loadLevel && (forcedAutoLevel === -1 || forcedAutoLevel !== loadLevel)) {
|
6975
6924
|
if (levelsSkipped.length) {
|
6976
|
-
|
6925
|
+
logger.trace("[abr] Skipped level(s) " + levelsSkipped.join(',') + " of " + maxAutoLevel + " max with CODECS and VIDEO-RANGE:\"" + levels[levelsSkipped[0]].codecs + "\" " + levels[levelsSkipped[0]].videoRange + "; not compatible with \"" + level.codecs + "\" " + currentVideoRange);
|
6977
6926
|
}
|
6978
|
-
|
6927
|
+
logger.info("[abr] switch candidate:" + selectionBaseLevel + "->" + i + " adjustedbw(" + Math.round(adjustedbw) + ")-bitrate=" + Math.round(adjustedbw - bitrate) + " ttfb:" + ttfbEstimateSec.toFixed(1) + " avgDuration:" + avgDuration.toFixed(1) + " maxFetchDuration:" + maxFetchDuration.toFixed(1) + " fetchDuration:" + fetchDuration.toFixed(1) + " firstSelection:" + firstSelection + " codecSet:" + currentCodecSet + " videoRange:" + currentVideoRange + " hls.loadLevel:" + loadLevel);
|
6979
6928
|
}
|
6980
6929
|
if (firstSelection) {
|
6981
6930
|
_this2.firstSelection = i;
|
@@ -7009,7 +6958,7 @@
|
|
7009
6958
|
}
|
7010
6959
|
var firstLevel = this.hls.firstLevel;
|
7011
6960
|
var clamped = Math.min(Math.max(firstLevel, minAutoLevel), maxAutoLevel);
|
7012
|
-
|
6961
|
+
logger.warn("[abr] Could not find best starting auto level. Defaulting to first in playlist " + firstLevel + " clamped to " + clamped);
|
7013
6962
|
return clamped;
|
7014
6963
|
}
|
7015
6964
|
}, {
|
@@ -7062,7 +7011,7 @@
|
|
7062
7011
|
}
|
7063
7012
|
}]);
|
7064
7013
|
return AbrController;
|
7065
|
-
}(
|
7014
|
+
}();
|
7066
7015
|
|
7067
7016
|
/**
|
7068
7017
|
* Provides methods dealing with buffer length retrieval for example.
|
@@ -7087,29 +7036,40 @@
|
|
7087
7036
|
* Return true if `media`'s buffered include `position`
|
7088
7037
|
*/
|
7089
7038
|
BufferHelper.isBuffered = function isBuffered(media, position) {
|
7090
|
-
|
7091
|
-
|
7092
|
-
|
7093
|
-
|
7094
|
-
|
7039
|
+
try {
|
7040
|
+
if (media) {
|
7041
|
+
var buffered = BufferHelper.getBuffered(media);
|
7042
|
+
for (var i = 0; i < buffered.length; i++) {
|
7043
|
+
if (position >= buffered.start(i) && position <= buffered.end(i)) {
|
7044
|
+
return true;
|
7045
|
+
}
|
7095
7046
|
}
|
7096
7047
|
}
|
7048
|
+
} catch (error) {
|
7049
|
+
// this is to catch
|
7050
|
+
// InvalidStateError: Failed to read the 'buffered' property from 'SourceBuffer':
|
7051
|
+
// This SourceBuffer has been removed from the parent media source
|
7097
7052
|
}
|
7098
7053
|
return false;
|
7099
7054
|
};
|
7100
7055
|
BufferHelper.bufferInfo = function bufferInfo(media, pos, maxHoleDuration) {
|
7101
|
-
|
7102
|
-
|
7103
|
-
|
7056
|
+
try {
|
7057
|
+
if (media) {
|
7058
|
+
var vbuffered = BufferHelper.getBuffered(media);
|
7104
7059
|
var buffered = [];
|
7105
|
-
|
7060
|
+
var i;
|
7061
|
+
for (i = 0; i < vbuffered.length; i++) {
|
7106
7062
|
buffered.push({
|
7107
7063
|
start: vbuffered.start(i),
|
7108
7064
|
end: vbuffered.end(i)
|
7109
7065
|
});
|
7110
7066
|
}
|
7111
|
-
return
|
7067
|
+
return this.bufferedInfo(buffered, pos, maxHoleDuration);
|
7112
7068
|
}
|
7069
|
+
} catch (error) {
|
7070
|
+
// this is to catch
|
7071
|
+
// InvalidStateError: Failed to read the 'buffered' property from 'SourceBuffer':
|
7072
|
+
// This SourceBuffer has been removed from the parent media source
|
7113
7073
|
}
|
7114
7074
|
return {
|
7115
7075
|
len: 0,
|
@@ -7122,7 +7082,12 @@
|
|
7122
7082
|
pos = Math.max(0, pos);
|
7123
7083
|
// sort on buffer.start/smaller end (IE does not always return sorted buffered range)
|
7124
7084
|
buffered.sort(function (a, b) {
|
7125
|
-
|
7085
|
+
var diff = a.start - b.start;
|
7086
|
+
if (diff) {
|
7087
|
+
return diff;
|
7088
|
+
} else {
|
7089
|
+
return b.end - a.end;
|
7090
|
+
}
|
7126
7091
|
});
|
7127
7092
|
var buffered2 = [];
|
7128
7093
|
if (maxHoleDuration) {
|
@@ -7190,7 +7155,7 @@
|
|
7190
7155
|
*/;
|
7191
7156
|
BufferHelper.getBuffered = function getBuffered(media) {
|
7192
7157
|
try {
|
7193
|
-
return media.buffered
|
7158
|
+
return media.buffered;
|
7194
7159
|
} catch (e) {
|
7195
7160
|
logger.log('failed to get media.buffered', e);
|
7196
7161
|
return noopBuffered;
|
@@ -7217,23 +7182,24 @@
|
|
7217
7182
|
this.executeNext(type);
|
7218
7183
|
}
|
7219
7184
|
};
|
7185
|
+
_proto.insertAbort = function insertAbort(operation, type) {
|
7186
|
+
var queue = this.queues[type];
|
7187
|
+
queue.unshift(operation);
|
7188
|
+
this.executeNext(type);
|
7189
|
+
};
|
7220
7190
|
_proto.appendBlocker = function appendBlocker(type) {
|
7221
|
-
var
|
7222
|
-
|
7223
|
-
|
7224
|
-
execute: resolve,
|
7225
|
-
onStart: function onStart() {},
|
7226
|
-
onComplete: function onComplete() {},
|
7227
|
-
onError: function onError() {}
|
7228
|
-
};
|
7229
|
-
_this.append(operation, type);
|
7191
|
+
var execute;
|
7192
|
+
var promise = new Promise(function (resolve) {
|
7193
|
+
execute = resolve;
|
7230
7194
|
});
|
7231
|
-
|
7232
|
-
|
7233
|
-
|
7234
|
-
|
7235
|
-
|
7236
|
-
}
|
7195
|
+
var operation = {
|
7196
|
+
execute: execute,
|
7197
|
+
onStart: function onStart() {},
|
7198
|
+
onComplete: function onComplete() {},
|
7199
|
+
onError: function onError() {}
|
7200
|
+
};
|
7201
|
+
this.append(operation, type);
|
7202
|
+
return promise;
|
7237
7203
|
};
|
7238
7204
|
_proto.executeNext = function executeNext(type) {
|
7239
7205
|
var queue = this.queues[type];
|
@@ -7266,69 +7232,61 @@
|
|
7266
7232
|
}();
|
7267
7233
|
|
7268
7234
|
var VIDEO_CODEC_PROFILE_REPLACE = /(avc[1234]|hvc1|hev1|dvh[1e]|vp09|av01)(?:\.[^.,]+)+/;
|
7269
|
-
var BufferController = /*#__PURE__*/function (
|
7270
|
-
|
7271
|
-
|
7272
|
-
var _this;
|
7273
|
-
_this = _Logger.call(this, 'buffer-controller', hls.logger) || this;
|
7235
|
+
var BufferController = /*#__PURE__*/function () {
|
7236
|
+
function BufferController(hls) {
|
7237
|
+
var _this = this;
|
7274
7238
|
// The level details used to determine duration, target-duration and live
|
7275
|
-
|
7239
|
+
this.details = null;
|
7276
7240
|
// cache the self generated object url to detect hijack of video tag
|
7277
|
-
|
7241
|
+
this._objectUrl = null;
|
7278
7242
|
// A queue of buffer operations which require the SourceBuffer to not be updating upon execution
|
7279
|
-
|
7243
|
+
this.operationQueue = void 0;
|
7280
7244
|
// References to event listeners for each SourceBuffer, so that they can be referenced for event removal
|
7281
|
-
|
7282
|
-
|
7283
|
-
_this.fragmentTracker = void 0;
|
7245
|
+
this.listeners = void 0;
|
7246
|
+
this.hls = void 0;
|
7284
7247
|
// The number of BUFFER_CODEC events received before any sourceBuffers are created
|
7285
|
-
|
7248
|
+
this.bufferCodecEventsExpected = 0;
|
7286
7249
|
// The total number of BUFFER_CODEC events received
|
7287
|
-
|
7250
|
+
this._bufferCodecEventsTotal = 0;
|
7288
7251
|
// A reference to the attached media element
|
7289
|
-
|
7252
|
+
this.media = null;
|
7290
7253
|
// A reference to the active media source
|
7291
|
-
|
7254
|
+
this.mediaSource = null;
|
7292
7255
|
// Last MP3 audio chunk appended
|
7293
|
-
|
7294
|
-
|
7295
|
-
_this.blockedAudioAppend = null;
|
7296
|
-
// Keep track of video append position for unblocking audio
|
7297
|
-
_this.lastVideoAppendEnd = 0;
|
7298
|
-
_this.appendSource = void 0;
|
7256
|
+
this.lastMpegAudioChunk = null;
|
7257
|
+
this.appendSource = void 0;
|
7299
7258
|
// counters
|
7300
|
-
|
7259
|
+
this.appendErrors = {
|
7301
7260
|
audio: 0,
|
7302
7261
|
video: 0,
|
7303
7262
|
audiovideo: 0
|
7304
7263
|
};
|
7305
|
-
|
7306
|
-
|
7307
|
-
|
7308
|
-
|
7264
|
+
this.tracks = {};
|
7265
|
+
this.pendingTracks = {};
|
7266
|
+
this.sourceBuffer = void 0;
|
7267
|
+
this.log = void 0;
|
7268
|
+
this.warn = void 0;
|
7269
|
+
this.error = void 0;
|
7270
|
+
this._onEndStreaming = function (event) {
|
7309
7271
|
if (!_this.hls) {
|
7310
7272
|
return;
|
7311
7273
|
}
|
7312
7274
|
_this.hls.pauseBuffering();
|
7313
7275
|
};
|
7314
|
-
|
7276
|
+
this._onStartStreaming = function (event) {
|
7315
7277
|
if (!_this.hls) {
|
7316
7278
|
return;
|
7317
7279
|
}
|
7318
7280
|
_this.hls.resumeBuffering();
|
7319
7281
|
};
|
7320
7282
|
// Keep as arrow functions so that we can directly reference these functions directly as event listeners
|
7321
|
-
|
7322
|
-
var
|
7323
|
-
|
7324
|
-
mediaSource = _assertThisInitialize.mediaSource;
|
7283
|
+
this._onMediaSourceOpen = function () {
|
7284
|
+
var media = _this.media,
|
7285
|
+
mediaSource = _this.mediaSource;
|
7325
7286
|
_this.log('Media source opened');
|
7326
7287
|
if (media) {
|
7327
7288
|
media.removeEventListener('emptied', _this._onMediaEmptied);
|
7328
|
-
|
7329
|
-
if (durationAndRange) {
|
7330
|
-
_this.updateMediaSource(durationAndRange);
|
7331
|
-
}
|
7289
|
+
_this.updateMediaElementDuration();
|
7332
7290
|
_this.hls.trigger(Events.MEDIA_ATTACHED, {
|
7333
7291
|
media: media,
|
7334
7292
|
mediaSource: mediaSource
|
@@ -7340,26 +7298,27 @@
|
|
7340
7298
|
}
|
7341
7299
|
_this.checkPendingTracks();
|
7342
7300
|
};
|
7343
|
-
|
7301
|
+
this._onMediaSourceClose = function () {
|
7344
7302
|
_this.log('Media source closed');
|
7345
7303
|
};
|
7346
|
-
|
7304
|
+
this._onMediaSourceEnded = function () {
|
7347
7305
|
_this.log('Media source ended');
|
7348
7306
|
};
|
7349
|
-
|
7350
|
-
var
|
7351
|
-
|
7352
|
-
_objectUrl = _assertThisInitialize2._objectUrl;
|
7307
|
+
this._onMediaEmptied = function () {
|
7308
|
+
var mediaSrc = _this.mediaSrc,
|
7309
|
+
_objectUrl = _this._objectUrl;
|
7353
7310
|
if (mediaSrc !== _objectUrl) {
|
7354
|
-
|
7311
|
+
logger.error("Media element src was set while attaching MediaSource (" + _objectUrl + " > " + mediaSrc + ")");
|
7355
7312
|
}
|
7356
7313
|
};
|
7357
|
-
|
7358
|
-
|
7359
|
-
|
7360
|
-
|
7361
|
-
|
7362
|
-
|
7314
|
+
this.hls = hls;
|
7315
|
+
var logPrefix = '[buffer-controller]';
|
7316
|
+
this.appendSource = hls.config.preferManagedMediaSource && typeof self !== 'undefined' && self.ManagedMediaSource;
|
7317
|
+
this.log = logger.log.bind(logger, logPrefix);
|
7318
|
+
this.warn = logger.warn.bind(logger, logPrefix);
|
7319
|
+
this.error = logger.error.bind(logger, logPrefix);
|
7320
|
+
this._initSourceBuffer();
|
7321
|
+
this.registerListeners();
|
7363
7322
|
}
|
7364
7323
|
var _proto = BufferController.prototype;
|
7365
7324
|
_proto.hasSourceTypes = function hasSourceTypes() {
|
@@ -7370,13 +7329,7 @@
|
|
7370
7329
|
this.details = null;
|
7371
7330
|
this.lastMpegAudioChunk = null;
|
7372
7331
|
// @ts-ignore
|
7373
|
-
this.hls =
|
7374
|
-
// @ts-ignore
|
7375
|
-
this._onMediaSourceOpen = this._onMediaSourceClose = null;
|
7376
|
-
// @ts-ignore
|
7377
|
-
this._onMediaSourceEnded = null;
|
7378
|
-
// @ts-ignore
|
7379
|
-
this._onStartStreaming = this._onEndStreaming = null;
|
7332
|
+
this.hls = null;
|
7380
7333
|
};
|
7381
7334
|
_proto.registerListeners = function registerListeners() {
|
7382
7335
|
var hls = this.hls;
|
@@ -7422,8 +7375,6 @@
|
|
7422
7375
|
audiovideo: 0
|
7423
7376
|
};
|
7424
7377
|
this.lastMpegAudioChunk = null;
|
7425
|
-
this.blockedAudioAppend = null;
|
7426
|
-
this.lastVideoAppendEnd = 0;
|
7427
7378
|
};
|
7428
7379
|
_proto.onManifestLoading = function onManifestLoading() {
|
7429
7380
|
this.bufferCodecEventsExpected = this._bufferCodecEventsTotal = 0;
|
@@ -7452,8 +7403,10 @@
|
|
7452
7403
|
ms.addEventListener('sourceopen', this._onMediaSourceOpen);
|
7453
7404
|
ms.addEventListener('sourceended', this._onMediaSourceEnded);
|
7454
7405
|
ms.addEventListener('sourceclose', this._onMediaSourceClose);
|
7455
|
-
|
7456
|
-
|
7406
|
+
if (this.appendSource) {
|
7407
|
+
ms.addEventListener('startstreaming', this._onStartStreaming);
|
7408
|
+
ms.addEventListener('endstreaming', this._onEndStreaming);
|
7409
|
+
}
|
7457
7410
|
|
7458
7411
|
// cache the locally generated object url
|
7459
7412
|
var objectUrl = this._objectUrl = self.URL.createObjectURL(ms);
|
@@ -7498,8 +7451,10 @@
|
|
7498
7451
|
mediaSource.removeEventListener('sourceopen', this._onMediaSourceOpen);
|
7499
7452
|
mediaSource.removeEventListener('sourceended', this._onMediaSourceEnded);
|
7500
7453
|
mediaSource.removeEventListener('sourceclose', this._onMediaSourceClose);
|
7501
|
-
|
7502
|
-
|
7454
|
+
if (this.appendSource) {
|
7455
|
+
mediaSource.removeEventListener('startstreaming', this._onStartStreaming);
|
7456
|
+
mediaSource.removeEventListener('endstreaming', this._onEndStreaming);
|
7457
|
+
}
|
7503
7458
|
|
7504
7459
|
// Detach properly the MediaSource from the HTMLMediaElement as
|
7505
7460
|
// suggested in https://github.com/w3c/media-source/issues/53.
|
@@ -7536,7 +7491,6 @@
|
|
7536
7491
|
_this2.resetBuffer(type);
|
7537
7492
|
});
|
7538
7493
|
this._initSourceBuffer();
|
7539
|
-
this.hls.resumeBuffering();
|
7540
7494
|
};
|
7541
7495
|
_proto.resetBuffer = function resetBuffer(type) {
|
7542
7496
|
var sb = this.sourceBuffer[type];
|
@@ -7561,10 +7515,9 @@
|
|
7561
7515
|
var trackNames = Object.keys(data);
|
7562
7516
|
trackNames.forEach(function (trackName) {
|
7563
7517
|
if (sourceBufferCount) {
|
7564
|
-
var _track$buffer;
|
7565
7518
|
// check if SourceBuffer codec needs to change
|
7566
7519
|
var track = _this3.tracks[trackName];
|
7567
|
-
if (track && typeof
|
7520
|
+
if (track && typeof track.buffer.changeType === 'function') {
|
7568
7521
|
var _trackCodec;
|
7569
7522
|
var _data$trackName = data[trackName],
|
7570
7523
|
id = _data$trackName.id,
|
@@ -7578,7 +7531,7 @@
|
|
7578
7531
|
var nextCodec = (_trackCodec = trackCodec) == null ? void 0 : _trackCodec.replace(VIDEO_CODEC_PROFILE_REPLACE, '$1');
|
7579
7532
|
if (trackCodec && currentCodec !== nextCodec) {
|
7580
7533
|
if (trackName.slice(0, 5) === 'audio') {
|
7581
|
-
trackCodec = getCodecCompatibleName(trackCodec, _this3.
|
7534
|
+
trackCodec = getCodecCompatibleName(trackCodec, _this3.appendSource);
|
7582
7535
|
}
|
7583
7536
|
var mimeType = container + ";codecs=" + trackCodec;
|
7584
7537
|
_this3.appendChangeType(trackName, mimeType);
|
@@ -7632,52 +7585,17 @@
|
|
7632
7585
|
};
|
7633
7586
|
operationQueue.append(operation, type, !!this.pendingTracks[type]);
|
7634
7587
|
};
|
7635
|
-
_proto.blockAudio = function blockAudio(partOrFrag) {
|
7636
|
-
var _this$fragmentTracker,
|
7637
|
-
_this5 = this;
|
7638
|
-
var pStart = partOrFrag.start;
|
7639
|
-
var pTime = pStart + partOrFrag.duration * 0.05;
|
7640
|
-
var atGap = ((_this$fragmentTracker = this.fragmentTracker.getAppendedFrag(pStart, PlaylistLevelType.MAIN)) == null ? void 0 : _this$fragmentTracker.gap) === true;
|
7641
|
-
if (atGap) {
|
7642
|
-
return;
|
7643
|
-
}
|
7644
|
-
var op = {
|
7645
|
-
execute: function execute() {
|
7646
|
-
var _this5$fragmentTracke;
|
7647
|
-
if (_this5.lastVideoAppendEnd > pTime || _this5.sourceBuffer.video && BufferHelper.isBuffered(_this5.sourceBuffer.video, pTime) || ((_this5$fragmentTracke = _this5.fragmentTracker.getAppendedFrag(pTime, PlaylistLevelType.MAIN)) == null ? void 0 : _this5$fragmentTracke.gap) === true) {
|
7648
|
-
_this5.blockedAudioAppend = null;
|
7649
|
-
_this5.operationQueue.shiftAndExecuteNext('audio');
|
7650
|
-
}
|
7651
|
-
},
|
7652
|
-
onStart: function onStart() {},
|
7653
|
-
onComplete: function onComplete() {},
|
7654
|
-
onError: function onError() {}
|
7655
|
-
};
|
7656
|
-
this.blockedAudioAppend = {
|
7657
|
-
op: op,
|
7658
|
-
frag: partOrFrag
|
7659
|
-
};
|
7660
|
-
this.operationQueue.append(op, 'audio', true);
|
7661
|
-
};
|
7662
|
-
_proto.unblockAudio = function unblockAudio() {
|
7663
|
-
var blockedAudioAppend = this.blockedAudioAppend;
|
7664
|
-
if (blockedAudioAppend) {
|
7665
|
-
this.blockedAudioAppend = null;
|
7666
|
-
this.operationQueue.unblockAudio(blockedAudioAppend.op);
|
7667
|
-
}
|
7668
|
-
};
|
7669
7588
|
_proto.onBufferAppending = function onBufferAppending(event, eventData) {
|
7670
|
-
var
|
7671
|
-
var
|
7589
|
+
var _this5 = this;
|
7590
|
+
var hls = this.hls,
|
7591
|
+
operationQueue = this.operationQueue,
|
7672
7592
|
tracks = this.tracks;
|
7673
7593
|
var data = eventData.data,
|
7674
7594
|
type = eventData.type,
|
7675
|
-
parent = eventData.parent,
|
7676
7595
|
frag = eventData.frag,
|
7677
7596
|
part = eventData.part,
|
7678
7597
|
chunkMeta = eventData.chunkMeta;
|
7679
7598
|
var chunkStats = chunkMeta.buffering[type];
|
7680
|
-
var sn = frag.sn;
|
7681
7599
|
var bufferAppendingStart = self.performance.now();
|
7682
7600
|
chunkStats.start = bufferAppendingStart;
|
7683
7601
|
var fragBuffering = frag.stats.buffering;
|
@@ -7700,50 +7618,21 @@
|
|
7700
7618
|
checkTimestampOffset = !this.lastMpegAudioChunk || chunkMeta.id === 1 || this.lastMpegAudioChunk.sn !== chunkMeta.sn;
|
7701
7619
|
this.lastMpegAudioChunk = chunkMeta;
|
7702
7620
|
}
|
7703
|
-
|
7704
|
-
// Block audio append until overlapping video append
|
7705
|
-
var videoSb = this.sourceBuffer.video;
|
7706
|
-
if (videoSb && sn !== 'initSegment') {
|
7707
|
-
var partOrFrag = part || frag;
|
7708
|
-
var blockedAudioAppend = this.blockedAudioAppend;
|
7709
|
-
if (type === 'audio' && parent !== 'main' && !this.blockedAudioAppend) {
|
7710
|
-
var pStart = partOrFrag.start;
|
7711
|
-
var pTime = pStart + partOrFrag.duration * 0.05;
|
7712
|
-
var vbuffered = videoSb.buffered;
|
7713
|
-
var vappending = this.operationQueue.current('video');
|
7714
|
-
if (!vbuffered.length && !vappending) {
|
7715
|
-
// wait for video before appending audio
|
7716
|
-
this.blockAudio(partOrFrag);
|
7717
|
-
} else if (!vappending && !BufferHelper.isBuffered(videoSb, pTime) && this.lastVideoAppendEnd < pTime) {
|
7718
|
-
// audio is ahead of video
|
7719
|
-
this.blockAudio(partOrFrag);
|
7720
|
-
}
|
7721
|
-
} else if (type === 'video') {
|
7722
|
-
var videoAppendEnd = partOrFrag.end;
|
7723
|
-
if (blockedAudioAppend) {
|
7724
|
-
var audioStart = blockedAudioAppend.frag.start;
|
7725
|
-
if (videoAppendEnd > audioStart || videoAppendEnd < this.lastVideoAppendEnd || BufferHelper.isBuffered(videoSb, audioStart)) {
|
7726
|
-
this.unblockAudio();
|
7727
|
-
}
|
7728
|
-
}
|
7729
|
-
this.lastVideoAppendEnd = videoAppendEnd;
|
7730
|
-
}
|
7731
|
-
}
|
7732
|
-
var fragStart = (part || frag).start;
|
7621
|
+
var fragStart = frag.start;
|
7733
7622
|
var operation = {
|
7734
7623
|
execute: function execute() {
|
7735
7624
|
chunkStats.executeStart = self.performance.now();
|
7736
7625
|
if (checkTimestampOffset) {
|
7737
|
-
var sb =
|
7626
|
+
var sb = _this5.sourceBuffer[type];
|
7738
7627
|
if (sb) {
|
7739
7628
|
var delta = fragStart - sb.timestampOffset;
|
7740
7629
|
if (Math.abs(delta) >= 0.1) {
|
7741
|
-
|
7630
|
+
_this5.log("Updating audio SourceBuffer timestampOffset to " + fragStart + " (delta: " + delta + ") sn: " + frag.sn + ")");
|
7742
7631
|
sb.timestampOffset = fragStart;
|
7743
7632
|
}
|
7744
7633
|
}
|
7745
7634
|
}
|
7746
|
-
|
7635
|
+
_this5.appendExecutor(data, type);
|
7747
7636
|
},
|
7748
7637
|
onStart: function onStart() {
|
7749
7638
|
// logger.debug(`[buffer-controller]: ${type} SourceBuffer updatestart`);
|
@@ -7758,19 +7647,19 @@
|
|
7758
7647
|
if (partBuffering && partBuffering.first === 0) {
|
7759
7648
|
partBuffering.first = end;
|
7760
7649
|
}
|
7761
|
-
var sourceBuffer =
|
7650
|
+
var sourceBuffer = _this5.sourceBuffer;
|
7762
7651
|
var timeRanges = {};
|
7763
7652
|
for (var _type in sourceBuffer) {
|
7764
7653
|
timeRanges[_type] = BufferHelper.getBuffered(sourceBuffer[_type]);
|
7765
7654
|
}
|
7766
|
-
|
7655
|
+
_this5.appendErrors[type] = 0;
|
7767
7656
|
if (type === 'audio' || type === 'video') {
|
7768
|
-
|
7657
|
+
_this5.appendErrors.audiovideo = 0;
|
7769
7658
|
} else {
|
7770
|
-
|
7771
|
-
|
7659
|
+
_this5.appendErrors.audio = 0;
|
7660
|
+
_this5.appendErrors.video = 0;
|
7772
7661
|
}
|
7773
|
-
|
7662
|
+
_this5.hls.trigger(Events.BUFFER_APPENDED, {
|
7774
7663
|
type: type,
|
7775
7664
|
frag: frag,
|
7776
7665
|
part: part,
|
@@ -7798,57 +7687,51 @@
|
|
7798
7687
|
// let's stop appending any segments, and report BUFFER_FULL_ERROR error
|
7799
7688
|
event.details = ErrorDetails.BUFFER_FULL_ERROR;
|
7800
7689
|
} else {
|
7801
|
-
var appendErrorCount = ++
|
7690
|
+
var appendErrorCount = ++_this5.appendErrors[type];
|
7802
7691
|
event.details = ErrorDetails.BUFFER_APPEND_ERROR;
|
7803
7692
|
/* with UHD content, we could get loop of quota exceeded error until
|
7804
7693
|
browser is able to evict some data from sourcebuffer. Retrying can help recover.
|
7805
7694
|
*/
|
7806
|
-
|
7807
|
-
if (appendErrorCount >=
|
7695
|
+
_this5.warn("Failed " + appendErrorCount + "/" + hls.config.appendErrorMaxRetry + " times to append segment in \"" + type + "\" sourceBuffer");
|
7696
|
+
if (appendErrorCount >= hls.config.appendErrorMaxRetry) {
|
7808
7697
|
event.fatal = true;
|
7809
7698
|
}
|
7810
7699
|
}
|
7811
|
-
|
7700
|
+
hls.trigger(Events.ERROR, event);
|
7812
7701
|
}
|
7813
7702
|
};
|
7814
7703
|
operationQueue.append(operation, type, !!this.pendingTracks[type]);
|
7815
7704
|
};
|
7816
|
-
_proto.getFlushOp = function getFlushOp(type, start, end) {
|
7817
|
-
var _this7 = this;
|
7818
|
-
return {
|
7819
|
-
execute: function execute() {
|
7820
|
-
_this7.removeExecutor(type, start, end);
|
7821
|
-
},
|
7822
|
-
onStart: function onStart() {
|
7823
|
-
// logger.debug(`[buffer-controller]: Started flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`);
|
7824
|
-
},
|
7825
|
-
onComplete: function onComplete() {
|
7826
|
-
// logger.debug(`[buffer-controller]: Finished flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`);
|
7827
|
-
_this7.hls.trigger(Events.BUFFER_FLUSHED, {
|
7828
|
-
type: type
|
7829
|
-
});
|
7830
|
-
},
|
7831
|
-
onError: function onError(error) {
|
7832
|
-
_this7.warn("Failed to remove from " + type + " SourceBuffer", error);
|
7833
|
-
}
|
7834
|
-
};
|
7835
|
-
};
|
7836
7705
|
_proto.onBufferFlushing = function onBufferFlushing(event, data) {
|
7837
|
-
var
|
7706
|
+
var _this6 = this;
|
7838
7707
|
var operationQueue = this.operationQueue;
|
7839
|
-
var
|
7840
|
-
|
7841
|
-
|
7842
|
-
|
7843
|
-
|
7708
|
+
var flushOperation = function flushOperation(type) {
|
7709
|
+
return {
|
7710
|
+
execute: _this6.removeExecutor.bind(_this6, type, data.startOffset, data.endOffset),
|
7711
|
+
onStart: function onStart() {
|
7712
|
+
// logger.debug(`[buffer-controller]: Started flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`);
|
7713
|
+
},
|
7714
|
+
onComplete: function onComplete() {
|
7715
|
+
// logger.debug(`[buffer-controller]: Finished flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`);
|
7716
|
+
_this6.hls.trigger(Events.BUFFER_FLUSHED, {
|
7717
|
+
type: type
|
7718
|
+
});
|
7719
|
+
},
|
7720
|
+
onError: function onError(error) {
|
7721
|
+
_this6.warn("Failed to remove from " + type + " SourceBuffer", error);
|
7722
|
+
}
|
7723
|
+
};
|
7724
|
+
};
|
7725
|
+
if (data.type) {
|
7726
|
+
operationQueue.append(flushOperation(data.type), data.type);
|
7844
7727
|
} else {
|
7845
|
-
this.getSourceBufferTypes().forEach(function (
|
7846
|
-
operationQueue.append(
|
7728
|
+
this.getSourceBufferTypes().forEach(function (type) {
|
7729
|
+
operationQueue.append(flushOperation(type), type);
|
7847
7730
|
});
|
7848
7731
|
}
|
7849
7732
|
};
|
7850
7733
|
_proto.onFragParsed = function onFragParsed(event, data) {
|
7851
|
-
var
|
7734
|
+
var _this7 = this;
|
7852
7735
|
var frag = data.frag,
|
7853
7736
|
part = data.part;
|
7854
7737
|
var buffersAppendedTo = [];
|
@@ -7870,7 +7753,7 @@
|
|
7870
7753
|
part.stats.buffering.end = now;
|
7871
7754
|
}
|
7872
7755
|
var stats = part ? part.stats : frag.stats;
|
7873
|
-
|
7756
|
+
_this7.hls.trigger(Events.FRAG_BUFFERED, {
|
7874
7757
|
frag: frag,
|
7875
7758
|
part: part,
|
7876
7759
|
stats: stats,
|
@@ -7890,17 +7773,14 @@
|
|
7890
7773
|
// an undefined data.type will mark all buffers as EOS.
|
7891
7774
|
;
|
7892
7775
|
_proto.onBufferEos = function onBufferEos(event, data) {
|
7893
|
-
var
|
7894
|
-
if (data.type === 'video') {
|
7895
|
-
this.unblockAudio();
|
7896
|
-
}
|
7776
|
+
var _this8 = this;
|
7897
7777
|
var ended = this.getSourceBufferTypes().reduce(function (acc, type) {
|
7898
|
-
var sb =
|
7778
|
+
var sb = _this8.sourceBuffer[type];
|
7899
7779
|
if (sb && (!data.type || data.type === type)) {
|
7900
7780
|
sb.ending = true;
|
7901
7781
|
if (!sb.ended) {
|
7902
7782
|
sb.ended = true;
|
7903
|
-
|
7783
|
+
_this8.log(type + " sourceBuffer now EOS");
|
7904
7784
|
}
|
7905
7785
|
}
|
7906
7786
|
return acc && !!(!sb || sb.ended);
|
@@ -7908,42 +7788,35 @@
|
|
7908
7788
|
if (ended) {
|
7909
7789
|
this.log("Queueing mediaSource.endOfStream()");
|
7910
7790
|
this.blockBuffers(function () {
|
7911
|
-
|
7912
|
-
var sb =
|
7791
|
+
_this8.getSourceBufferTypes().forEach(function (type) {
|
7792
|
+
var sb = _this8.sourceBuffer[type];
|
7913
7793
|
if (sb) {
|
7914
7794
|
sb.ending = false;
|
7915
7795
|
}
|
7916
7796
|
});
|
7917
|
-
var mediaSource =
|
7797
|
+
var mediaSource = _this8.mediaSource;
|
7918
7798
|
if (!mediaSource || mediaSource.readyState !== 'open') {
|
7919
7799
|
if (mediaSource) {
|
7920
|
-
|
7800
|
+
_this8.log("Could not call mediaSource.endOfStream(). mediaSource.readyState: " + mediaSource.readyState);
|
7921
7801
|
}
|
7922
7802
|
return;
|
7923
7803
|
}
|
7924
|
-
|
7804
|
+
_this8.log("Calling mediaSource.endOfStream()");
|
7925
7805
|
// Allow this to throw and be caught by the enqueueing function
|
7926
7806
|
mediaSource.endOfStream();
|
7927
7807
|
});
|
7928
7808
|
}
|
7929
7809
|
};
|
7930
7810
|
_proto.onLevelUpdated = function onLevelUpdated(event, _ref) {
|
7931
|
-
var _this11 = this;
|
7932
7811
|
var details = _ref.details;
|
7933
7812
|
if (!details.fragments.length) {
|
7934
7813
|
return;
|
7935
7814
|
}
|
7936
7815
|
this.details = details;
|
7937
|
-
var durationAndRange = this.getDurationAndRange();
|
7938
|
-
if (!durationAndRange) {
|
7939
|
-
return;
|
7940
|
-
}
|
7941
7816
|
if (this.getSourceBufferTypes().length) {
|
7942
|
-
this.blockBuffers(
|
7943
|
-
return _this11.updateMediaSource(durationAndRange);
|
7944
|
-
});
|
7817
|
+
this.blockBuffers(this.updateMediaElementDuration.bind(this));
|
7945
7818
|
} else {
|
7946
|
-
this.
|
7819
|
+
this.updateMediaElementDuration();
|
7947
7820
|
}
|
7948
7821
|
};
|
7949
7822
|
_proto.trimBuffers = function trimBuffers() {
|
@@ -7976,7 +7849,7 @@
|
|
7976
7849
|
}
|
7977
7850
|
};
|
7978
7851
|
_proto.flushBackBuffer = function flushBackBuffer(currentTime, targetDuration, targetBackBufferPosition) {
|
7979
|
-
var
|
7852
|
+
var _this9 = this;
|
7980
7853
|
var details = this.details,
|
7981
7854
|
sourceBuffer = this.sourceBuffer;
|
7982
7855
|
var sourceBufferTypes = this.getSourceBufferTypes();
|
@@ -7986,20 +7859,20 @@
|
|
7986
7859
|
var buffered = BufferHelper.getBuffered(sb);
|
7987
7860
|
// when target buffer start exceeds actual buffer start
|
7988
7861
|
if (buffered.length > 0 && targetBackBufferPosition > buffered.start(0)) {
|
7989
|
-
|
7862
|
+
_this9.hls.trigger(Events.BACK_BUFFER_REACHED, {
|
7990
7863
|
bufferEnd: targetBackBufferPosition
|
7991
7864
|
});
|
7992
7865
|
|
7993
7866
|
// Support for deprecated event:
|
7994
7867
|
if (details != null && details.live) {
|
7995
|
-
|
7868
|
+
_this9.hls.trigger(Events.LIVE_BACK_BUFFER_REACHED, {
|
7996
7869
|
bufferEnd: targetBackBufferPosition
|
7997
7870
|
});
|
7998
7871
|
} else if (sb.ended && buffered.end(buffered.length - 1) - currentTime < targetDuration * 2) {
|
7999
|
-
|
7872
|
+
_this9.log("Cannot flush " + type + " back buffer while SourceBuffer is in ended state");
|
8000
7873
|
return;
|
8001
7874
|
}
|
8002
|
-
|
7875
|
+
_this9.hls.trigger(Events.BUFFER_FLUSHING, {
|
8003
7876
|
startOffset: 0,
|
8004
7877
|
endOffset: targetBackBufferPosition,
|
8005
7878
|
type: type
|
@@ -8009,7 +7882,7 @@
|
|
8009
7882
|
});
|
8010
7883
|
};
|
8011
7884
|
_proto.flushFrontBuffer = function flushFrontBuffer(currentTime, targetDuration, targetFrontBufferPosition) {
|
8012
|
-
var
|
7885
|
+
var _this10 = this;
|
8013
7886
|
var sourceBuffer = this.sourceBuffer;
|
8014
7887
|
var sourceBufferTypes = this.getSourceBufferTypes();
|
8015
7888
|
sourceBufferTypes.forEach(function (type) {
|
@@ -8027,10 +7900,10 @@
|
|
8027
7900
|
if (targetFrontBufferPosition > bufferStart || currentTime >= bufferStart && currentTime <= bufferEnd) {
|
8028
7901
|
return;
|
8029
7902
|
} else if (sb.ended && currentTime - bufferEnd < 2 * targetDuration) {
|
8030
|
-
|
7903
|
+
_this10.log("Cannot flush " + type + " front buffer while SourceBuffer is in ended state");
|
8031
7904
|
return;
|
8032
7905
|
}
|
8033
|
-
|
7906
|
+
_this10.hls.trigger(Events.BUFFER_FLUSHING, {
|
8034
7907
|
startOffset: bufferStart,
|
8035
7908
|
endOffset: Infinity,
|
8036
7909
|
type: type
|
@@ -8044,9 +7917,9 @@
|
|
8044
7917
|
* 'liveDurationInfinity` is set to `true`
|
8045
7918
|
* More details: https://github.com/video-dev/hls.js/issues/355
|
8046
7919
|
*/;
|
8047
|
-
_proto.
|
7920
|
+
_proto.updateMediaElementDuration = function updateMediaElementDuration() {
|
8048
7921
|
if (!this.details || !this.media || !this.mediaSource || this.mediaSource.readyState !== 'open') {
|
8049
|
-
return
|
7922
|
+
return;
|
8050
7923
|
}
|
8051
7924
|
var details = this.details,
|
8052
7925
|
hls = this.hls,
|
@@ -8058,40 +7931,25 @@
|
|
8058
7931
|
if (details.live && hls.config.liveDurationInfinity) {
|
8059
7932
|
// Override duration to Infinity
|
8060
7933
|
mediaSource.duration = Infinity;
|
8061
|
-
|
8062
|
-
if (len && details.live && !!mediaSource.setLiveSeekableRange) {
|
8063
|
-
var start = Math.max(0, details.fragments[0].start);
|
8064
|
-
var end = Math.max(start, start + details.totalduration);
|
8065
|
-
return {
|
8066
|
-
duration: Infinity,
|
8067
|
-
start: start,
|
8068
|
-
end: end
|
8069
|
-
};
|
8070
|
-
}
|
8071
|
-
return {
|
8072
|
-
duration: Infinity
|
8073
|
-
};
|
7934
|
+
this.updateSeekableRange(details);
|
8074
7935
|
} else if (levelDuration > msDuration && levelDuration > mediaDuration || !isFiniteNumber(mediaDuration)) {
|
8075
|
-
|
8076
|
-
|
8077
|
-
|
7936
|
+
// levelDuration was the last value we set.
|
7937
|
+
// not using mediaSource.duration as the browser may tweak this value
|
7938
|
+
// only update Media Source duration if its value increase, this is to avoid
|
7939
|
+
// flushing already buffered portion when switching between quality level
|
7940
|
+
this.log("Updating Media Source duration to " + levelDuration.toFixed(3));
|
7941
|
+
mediaSource.duration = levelDuration;
|
8078
7942
|
}
|
8079
|
-
return null;
|
8080
7943
|
};
|
8081
|
-
_proto.
|
8082
|
-
var
|
8083
|
-
|
8084
|
-
|
8085
|
-
if (
|
8086
|
-
|
8087
|
-
|
8088
|
-
|
8089
|
-
|
8090
|
-
}
|
8091
|
-
this.mediaSource.duration = duration;
|
8092
|
-
if (start !== undefined && end !== undefined) {
|
8093
|
-
this.log("Media Source duration is set to " + this.mediaSource.duration + ". Setting seekable range to " + start + "-" + end + ".");
|
8094
|
-
this.mediaSource.setLiveSeekableRange(start, end);
|
7944
|
+
_proto.updateSeekableRange = function updateSeekableRange(levelDetails) {
|
7945
|
+
var mediaSource = this.mediaSource;
|
7946
|
+
var fragments = levelDetails.fragments;
|
7947
|
+
var len = fragments.length;
|
7948
|
+
if (len && levelDetails.live && mediaSource != null && mediaSource.setLiveSeekableRange) {
|
7949
|
+
var start = Math.max(0, fragments[0].start);
|
7950
|
+
var end = Math.max(start, start + levelDetails.totalduration);
|
7951
|
+
this.log("Media Source duration is set to " + mediaSource.duration + ". Setting seekable range to " + start + "-" + end + ".");
|
7952
|
+
mediaSource.setLiveSeekableRange(start, end);
|
8095
7953
|
}
|
8096
7954
|
};
|
8097
7955
|
_proto.checkPendingTracks = function checkPendingTracks() {
|
@@ -8130,7 +7988,7 @@
|
|
8130
7988
|
}
|
8131
7989
|
};
|
8132
7990
|
_proto.createSourceBuffers = function createSourceBuffers(tracks) {
|
8133
|
-
var
|
7991
|
+
var _this11 = this;
|
8134
7992
|
var sourceBuffer = this.sourceBuffer,
|
8135
7993
|
mediaSource = this.mediaSource;
|
8136
7994
|
if (!mediaSource) {
|
@@ -8146,28 +8004,30 @@
|
|
8146
8004
|
var codec = track.levelCodec || track.codec;
|
8147
8005
|
if (codec) {
|
8148
8006
|
if (trackName.slice(0, 5) === 'audio') {
|
8149
|
-
codec = getCodecCompatibleName(codec,
|
8007
|
+
codec = getCodecCompatibleName(codec, _this11.appendSource);
|
8150
8008
|
}
|
8151
8009
|
}
|
8152
8010
|
var mimeType = track.container + ";codecs=" + codec;
|
8153
|
-
|
8011
|
+
_this11.log("creating sourceBuffer(" + mimeType + ")");
|
8154
8012
|
try {
|
8155
8013
|
var sb = sourceBuffer[trackName] = mediaSource.addSourceBuffer(mimeType);
|
8156
8014
|
var sbName = trackName;
|
8157
|
-
|
8158
|
-
|
8159
|
-
|
8015
|
+
_this11.addBufferListener(sbName, 'updatestart', _this11._onSBUpdateStart);
|
8016
|
+
_this11.addBufferListener(sbName, 'updateend', _this11._onSBUpdateEnd);
|
8017
|
+
_this11.addBufferListener(sbName, 'error', _this11._onSBUpdateError);
|
8160
8018
|
// ManagedSourceBuffer bufferedchange event
|
8161
|
-
|
8162
|
-
|
8163
|
-
|
8164
|
-
|
8165
|
-
|
8166
|
-
|
8167
|
-
|
8168
|
-
|
8169
|
-
|
8170
|
-
|
8019
|
+
if (_this11.appendSource) {
|
8020
|
+
_this11.addBufferListener(sbName, 'bufferedchange', function (type, event) {
|
8021
|
+
// If media was ejected check for a change. Added ranges are redundant with changes on 'updateend' event.
|
8022
|
+
var removedRanges = event.removedRanges;
|
8023
|
+
if (removedRanges != null && removedRanges.length) {
|
8024
|
+
_this11.hls.trigger(Events.BUFFER_FLUSHED, {
|
8025
|
+
type: trackName
|
8026
|
+
});
|
8027
|
+
}
|
8028
|
+
});
|
8029
|
+
}
|
8030
|
+
_this11.tracks[trackName] = {
|
8171
8031
|
buffer: sb,
|
8172
8032
|
codec: codec,
|
8173
8033
|
container: track.container,
|
@@ -8176,8 +8036,8 @@
|
|
8176
8036
|
id: track.id
|
8177
8037
|
};
|
8178
8038
|
} catch (err) {
|
8179
|
-
|
8180
|
-
|
8039
|
+
_this11.error("error while trying to add sourceBuffer: " + err.message);
|
8040
|
+
_this11.hls.trigger(Events.ERROR, {
|
8181
8041
|
type: ErrorTypes.MEDIA_ERROR,
|
8182
8042
|
details: ErrorDetails.BUFFER_ADD_CODEC_ERROR,
|
8183
8043
|
fatal: false,
|
@@ -8265,7 +8125,6 @@
|
|
8265
8125
|
}
|
8266
8126
|
return;
|
8267
8127
|
}
|
8268
|
-
sb.ending = false;
|
8269
8128
|
sb.ended = false;
|
8270
8129
|
sb.appendBuffer(data);
|
8271
8130
|
}
|
@@ -8275,7 +8134,7 @@
|
|
8275
8134
|
// upon completion, since we already do it here
|
8276
8135
|
;
|
8277
8136
|
_proto.blockBuffers = function blockBuffers(onUnblocked, buffers) {
|
8278
|
-
var
|
8137
|
+
var _this12 = this;
|
8279
8138
|
if (buffers === void 0) {
|
8280
8139
|
buffers = this.getSourceBufferTypes();
|
8281
8140
|
}
|
@@ -8290,15 +8149,11 @@
|
|
8290
8149
|
var blockingOperations = buffers.map(function (type) {
|
8291
8150
|
return operationQueue.appendBlocker(type);
|
8292
8151
|
});
|
8293
|
-
|
8294
|
-
if (audioBlocked) {
|
8295
|
-
this.unblockAudio();
|
8296
|
-
}
|
8297
|
-
Promise.all(blockingOperations).then(function (result) {
|
8152
|
+
Promise.all(blockingOperations).then(function () {
|
8298
8153
|
// logger.debug(`[buffer-controller]: Blocking operation resolved; unblocking ${buffers} SourceBuffer`);
|
8299
8154
|
onUnblocked();
|
8300
|
-
buffers.forEach(function (type
|
8301
|
-
var sb =
|
8155
|
+
buffers.forEach(function (type) {
|
8156
|
+
var sb = _this12.sourceBuffer[type];
|
8302
8157
|
// Only cycle the queue if the SB is not updating. There's a bug in Chrome which sets the SB updating flag to
|
8303
8158
|
// true when changing the MediaSource duration (https://bugs.chromium.org/p/chromium/issues/detail?id=959359&can=2&q=mediasource%20duration)
|
8304
8159
|
// While this is a workaround, it's probably useful to have around
|
@@ -8341,7 +8196,7 @@
|
|
8341
8196
|
}
|
8342
8197
|
}]);
|
8343
8198
|
return BufferController;
|
8344
|
-
}(
|
8199
|
+
}();
|
8345
8200
|
function removeSourceChildren(node) {
|
8346
8201
|
var sourceChildren = node.querySelectorAll('source');
|
8347
8202
|
[].slice.call(sourceChildren).forEach(function (source) {
|
@@ -8465,10 +8320,10 @@
|
|
8465
8320
|
var hls = this.hls;
|
8466
8321
|
var maxLevel = this.getMaxLevel(levels.length - 1);
|
8467
8322
|
if (maxLevel !== this.autoLevelCapping) {
|
8468
|
-
|
8323
|
+
logger.log("Setting autoLevelCapping to " + maxLevel + ": " + levels[maxLevel].height + "p@" + levels[maxLevel].bitrate + " for media " + this.mediaWidth + "x" + this.mediaHeight);
|
8469
8324
|
}
|
8470
8325
|
hls.autoLevelCapping = maxLevel;
|
8471
|
-
if (hls.
|
8326
|
+
if (hls.autoLevelCapping > this.autoLevelCapping && this.streamController) {
|
8472
8327
|
// if auto level capping has a higher value for the previous one, flush the buffer using nextLevelSwitch
|
8473
8328
|
// usually happen when the user go to the fullscreen mode.
|
8474
8329
|
this.streamController.nextLevelSwitch();
|
@@ -8655,10 +8510,10 @@
|
|
8655
8510
|
totalDroppedFrames: droppedFrames
|
8656
8511
|
});
|
8657
8512
|
if (droppedFPS > 0) {
|
8658
|
-
//
|
8513
|
+
// logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod));
|
8659
8514
|
if (currentDropped > hls.config.fpsDroppedMonitoringThreshold * currentDecoded) {
|
8660
8515
|
var currentLevel = hls.currentLevel;
|
8661
|
-
|
8516
|
+
logger.warn('drop FPS ratio greater than max allowed value for currentLevel: ' + currentLevel);
|
8662
8517
|
if (currentLevel > 0 && (hls.autoLevelCapping === -1 || hls.autoLevelCapping >= currentLevel)) {
|
8663
8518
|
currentLevel = currentLevel - 1;
|
8664
8519
|
hls.trigger(Events.FPS_DROP_LEVEL_CAPPING, {
|
@@ -8692,28 +8547,26 @@
|
|
8692
8547
|
}();
|
8693
8548
|
|
8694
8549
|
var PATHWAY_PENALTY_DURATION_MS = 300000;
|
8695
|
-
var ContentSteeringController = /*#__PURE__*/function (
|
8696
|
-
_inheritsLoose(ContentSteeringController, _Logger);
|
8550
|
+
var ContentSteeringController = /*#__PURE__*/function () {
|
8697
8551
|
function ContentSteeringController(hls) {
|
8698
|
-
|
8699
|
-
|
8700
|
-
|
8701
|
-
|
8702
|
-
|
8703
|
-
|
8704
|
-
|
8705
|
-
|
8706
|
-
|
8707
|
-
|
8708
|
-
|
8709
|
-
|
8710
|
-
|
8711
|
-
|
8712
|
-
|
8713
|
-
|
8714
|
-
|
8715
|
-
|
8716
|
-
return _this;
|
8552
|
+
this.hls = void 0;
|
8553
|
+
this.log = void 0;
|
8554
|
+
this.loader = null;
|
8555
|
+
this.uri = null;
|
8556
|
+
this.pathwayId = '.';
|
8557
|
+
this.pathwayPriority = null;
|
8558
|
+
this.timeToLoad = 300;
|
8559
|
+
this.reloadTimer = -1;
|
8560
|
+
this.updated = 0;
|
8561
|
+
this.started = false;
|
8562
|
+
this.enabled = true;
|
8563
|
+
this.levels = null;
|
8564
|
+
this.audioTracks = null;
|
8565
|
+
this.subtitleTracks = null;
|
8566
|
+
this.penalizedPathways = {};
|
8567
|
+
this.hls = hls;
|
8568
|
+
this.log = logger.log.bind(logger, "[content-steering]:");
|
8569
|
+
this.registerListeners();
|
8717
8570
|
}
|
8718
8571
|
var _proto = ContentSteeringController.prototype;
|
8719
8572
|
_proto.registerListeners = function registerListeners() {
|
@@ -8834,7 +8687,7 @@
|
|
8834
8687
|
errorAction.resolved = this.pathwayId !== errorPathway;
|
8835
8688
|
}
|
8836
8689
|
if (!errorAction.resolved) {
|
8837
|
-
|
8690
|
+
logger.warn("Could not resolve " + data.details + " (\"" + data.error.message + "\") with content-steering for Pathway: " + errorPathway + " levels: " + (levels ? levels.length : levels) + " priorities: " + JSON.stringify(pathwayPriority) + " penalized: " + JSON.stringify(this.penalizedPathways));
|
8838
8691
|
}
|
8839
8692
|
}
|
8840
8693
|
};
|
@@ -8914,7 +8767,7 @@
|
|
8914
8767
|
return defaultPathway;
|
8915
8768
|
};
|
8916
8769
|
_proto.clonePathways = function clonePathways(pathwayClones) {
|
8917
|
-
var
|
8770
|
+
var _this = this;
|
8918
8771
|
var levels = this.levels;
|
8919
8772
|
if (!levels) {
|
8920
8773
|
return;
|
@@ -8930,7 +8783,7 @@
|
|
8930
8783
|
})) {
|
8931
8784
|
return;
|
8932
8785
|
}
|
8933
|
-
var clonedVariants =
|
8786
|
+
var clonedVariants = _this.getLevelsForPathway(baseId).map(function (baseLevel) {
|
8934
8787
|
var attributes = new AttrList(baseLevel.attrs);
|
8935
8788
|
attributes['PATHWAY-ID'] = cloneId;
|
8936
8789
|
var clonedAudioGroupId = attributes.AUDIO && attributes.AUDIO + "_clone_" + cloneId;
|
@@ -8967,12 +8820,12 @@
|
|
8967
8820
|
return clonedLevel;
|
8968
8821
|
});
|
8969
8822
|
levels.push.apply(levels, clonedVariants);
|
8970
|
-
cloneRenditionGroups(
|
8971
|
-
cloneRenditionGroups(
|
8823
|
+
cloneRenditionGroups(_this.audioTracks, audioGroupCloneMap, uriReplacement, cloneId);
|
8824
|
+
cloneRenditionGroups(_this.subtitleTracks, subtitleGroupCloneMap, uriReplacement, cloneId);
|
8972
8825
|
});
|
8973
8826
|
};
|
8974
8827
|
_proto.loadSteeringManifest = function loadSteeringManifest(uri) {
|
8975
|
-
var
|
8828
|
+
var _this2 = this;
|
8976
8829
|
var config = this.hls.config;
|
8977
8830
|
var Loader = config.loader;
|
8978
8831
|
if (this.loader) {
|
@@ -9007,87 +8860,87 @@
|
|
9007
8860
|
};
|
9008
8861
|
var callbacks = {
|
9009
8862
|
onSuccess: function onSuccess(response, stats, context, networkDetails) {
|
9010
|
-
|
8863
|
+
_this2.log("Loaded steering manifest: \"" + url + "\"");
|
9011
8864
|
var steeringData = response.data;
|
9012
|
-
if (
|
9013
|
-
|
8865
|
+
if (steeringData.VERSION !== 1) {
|
8866
|
+
_this2.log("Steering VERSION " + steeringData.VERSION + " not supported!");
|
9014
8867
|
return;
|
9015
8868
|
}
|
9016
|
-
|
9017
|
-
|
8869
|
+
_this2.updated = performance.now();
|
8870
|
+
_this2.timeToLoad = steeringData.TTL;
|
9018
8871
|
var reloadUri = steeringData['RELOAD-URI'],
|
9019
8872
|
pathwayClones = steeringData['PATHWAY-CLONES'],
|
9020
8873
|
pathwayPriority = steeringData['PATHWAY-PRIORITY'];
|
9021
8874
|
if (reloadUri) {
|
9022
8875
|
try {
|
9023
|
-
|
8876
|
+
_this2.uri = new self.URL(reloadUri, url).href;
|
9024
8877
|
} catch (error) {
|
9025
|
-
|
9026
|
-
|
8878
|
+
_this2.enabled = false;
|
8879
|
+
_this2.log("Failed to parse Steering Manifest RELOAD-URI: " + reloadUri);
|
9027
8880
|
return;
|
9028
8881
|
}
|
9029
8882
|
}
|
9030
|
-
|
8883
|
+
_this2.scheduleRefresh(_this2.uri || context.url);
|
9031
8884
|
if (pathwayClones) {
|
9032
|
-
|
8885
|
+
_this2.clonePathways(pathwayClones);
|
9033
8886
|
}
|
9034
8887
|
var loadedSteeringData = {
|
9035
8888
|
steeringManifest: steeringData,
|
9036
8889
|
url: url.toString()
|
9037
8890
|
};
|
9038
|
-
|
8891
|
+
_this2.hls.trigger(Events.STEERING_MANIFEST_LOADED, loadedSteeringData);
|
9039
8892
|
if (pathwayPriority) {
|
9040
|
-
|
8893
|
+
_this2.updatePathwayPriority(pathwayPriority);
|
9041
8894
|
}
|
9042
8895
|
},
|
9043
8896
|
onError: function onError(error, context, networkDetails, stats) {
|
9044
|
-
|
9045
|
-
|
8897
|
+
_this2.log("Error loading steering manifest: " + error.code + " " + error.text + " (" + context.url + ")");
|
8898
|
+
_this2.stopLoad();
|
9046
8899
|
if (error.code === 410) {
|
9047
|
-
|
9048
|
-
|
8900
|
+
_this2.enabled = false;
|
8901
|
+
_this2.log("Steering manifest " + context.url + " no longer available");
|
9049
8902
|
return;
|
9050
8903
|
}
|
9051
|
-
var ttl =
|
8904
|
+
var ttl = _this2.timeToLoad * 1000;
|
9052
8905
|
if (error.code === 429) {
|
9053
|
-
var loader =
|
8906
|
+
var loader = _this2.loader;
|
9054
8907
|
if (typeof (loader == null ? void 0 : loader.getResponseHeader) === 'function') {
|
9055
8908
|
var retryAfter = loader.getResponseHeader('Retry-After');
|
9056
8909
|
if (retryAfter) {
|
9057
8910
|
ttl = parseFloat(retryAfter) * 1000;
|
9058
8911
|
}
|
9059
8912
|
}
|
9060
|
-
|
8913
|
+
_this2.log("Steering manifest " + context.url + " rate limited");
|
9061
8914
|
return;
|
9062
8915
|
}
|
9063
|
-
|
8916
|
+
_this2.scheduleRefresh(_this2.uri || context.url, ttl);
|
9064
8917
|
},
|
9065
8918
|
onTimeout: function onTimeout(stats, context, networkDetails) {
|
9066
|
-
|
9067
|
-
|
8919
|
+
_this2.log("Timeout loading steering manifest (" + context.url + ")");
|
8920
|
+
_this2.scheduleRefresh(_this2.uri || context.url);
|
9068
8921
|
}
|
9069
8922
|
};
|
9070
8923
|
this.log("Requesting steering manifest: " + url);
|
9071
8924
|
this.loader.load(context, loaderConfig, callbacks);
|
9072
8925
|
};
|
9073
8926
|
_proto.scheduleRefresh = function scheduleRefresh(uri, ttlMs) {
|
9074
|
-
var
|
8927
|
+
var _this3 = this;
|
9075
8928
|
if (ttlMs === void 0) {
|
9076
8929
|
ttlMs = this.timeToLoad * 1000;
|
9077
8930
|
}
|
9078
8931
|
this.clearTimeout();
|
9079
8932
|
this.reloadTimer = self.setTimeout(function () {
|
9080
|
-
var
|
9081
|
-
var media = (
|
8933
|
+
var _this3$hls;
|
8934
|
+
var media = (_this3$hls = _this3.hls) == null ? void 0 : _this3$hls.media;
|
9082
8935
|
if (media && !media.ended) {
|
9083
|
-
|
8936
|
+
_this3.loadSteeringManifest(uri);
|
9084
8937
|
return;
|
9085
8938
|
}
|
9086
|
-
|
8939
|
+
_this3.scheduleRefresh(uri, _this3.timeToLoad * 1000);
|
9087
8940
|
}, ttlMs);
|
9088
8941
|
};
|
9089
8942
|
return ContentSteeringController;
|
9090
|
-
}(
|
8943
|
+
}();
|
9091
8944
|
function cloneRenditionGroups(tracks, groupCloneMap, uriReplacement, cloneId) {
|
9092
8945
|
if (!tracks) {
|
9093
8946
|
return;
|
@@ -9923,7 +9776,7 @@
|
|
9923
9776
|
});
|
9924
9777
|
function timelineConfig() {
|
9925
9778
|
return {
|
9926
|
-
cueHandler:
|
9779
|
+
cueHandler: Cues,
|
9927
9780
|
// used by timeline-controller
|
9928
9781
|
enableWebVTT: false,
|
9929
9782
|
// used by timeline-controller
|
@@ -9954,7 +9807,7 @@
|
|
9954
9807
|
/**
|
9955
9808
|
* @ignore
|
9956
9809
|
*/
|
9957
|
-
function mergeConfig(defaultConfig, userConfig
|
9810
|
+
function mergeConfig(defaultConfig, userConfig) {
|
9958
9811
|
if ((userConfig.liveSyncDurationCount || userConfig.liveMaxLatencyDurationCount) && (userConfig.liveSyncDuration || userConfig.liveMaxLatencyDuration)) {
|
9959
9812
|
throw new Error("Illegal hls.js config: don't mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration");
|
9960
9813
|
}
|
@@ -10024,7 +9877,7 @@
|
|
10024
9877
|
/**
|
10025
9878
|
* @ignore
|
10026
9879
|
*/
|
10027
|
-
function enableStreamingMode(config
|
9880
|
+
function enableStreamingMode(config) {
|
10028
9881
|
var currentLoader = config.loader;
|
10029
9882
|
if (currentLoader !== FetchLoader && currentLoader !== XhrLoader) {
|
10030
9883
|
// If a developer has configured their own loader, respect that choice
|
@@ -10041,11 +9894,12 @@
|
|
10041
9894
|
}
|
10042
9895
|
}
|
10043
9896
|
|
9897
|
+
var chromeOrFirefox;
|
10044
9898
|
var LevelController = /*#__PURE__*/function (_BasePlaylistControll) {
|
10045
9899
|
_inheritsLoose(LevelController, _BasePlaylistControll);
|
10046
9900
|
function LevelController(hls, contentSteeringController) {
|
10047
9901
|
var _this;
|
10048
|
-
_this = _BasePlaylistControll.call(this, hls, 'level-controller') || this;
|
9902
|
+
_this = _BasePlaylistControll.call(this, hls, '[level-controller]') || this;
|
10049
9903
|
_this._levels = [];
|
10050
9904
|
_this._firstLevel = -1;
|
10051
9905
|
_this._maxAutoLevel = -1;
|
@@ -10114,13 +9968,21 @@
|
|
10114
9968
|
var videoCodecFound = false;
|
10115
9969
|
var audioCodecFound = false;
|
10116
9970
|
data.levels.forEach(function (levelParsed) {
|
10117
|
-
var _videoCodec;
|
9971
|
+
var _audioCodec, _videoCodec;
|
10118
9972
|
var attributes = levelParsed.attrs;
|
9973
|
+
|
9974
|
+
// erase audio codec info if browser does not support mp4a.40.34.
|
9975
|
+
// demuxer will autodetect codec and fallback to mpeg/audio
|
10119
9976
|
var audioCodec = levelParsed.audioCodec,
|
10120
9977
|
videoCodec = levelParsed.videoCodec;
|
9978
|
+
if (((_audioCodec = audioCodec) == null ? void 0 : _audioCodec.indexOf('mp4a.40.34')) !== -1) {
|
9979
|
+
chromeOrFirefox || (chromeOrFirefox = /chrome|firefox/i.test(navigator.userAgent));
|
9980
|
+
if (chromeOrFirefox) {
|
9981
|
+
levelParsed.audioCodec = audioCodec = undefined;
|
9982
|
+
}
|
9983
|
+
}
|
10121
9984
|
if (audioCodec) {
|
10122
|
-
|
10123
|
-
levelParsed.audioCodec = audioCodec = getCodecCompatibleName(audioCodec, preferManagedMediaSource) || undefined;
|
9985
|
+
levelParsed.audioCodec = audioCodec = getCodecCompatibleName(audioCodec, preferManagedMediaSource);
|
10124
9986
|
}
|
10125
9987
|
if (((_videoCodec = videoCodec) == null ? void 0 : _videoCodec.indexOf('avc1')) === 0) {
|
10126
9988
|
videoCodec = levelParsed.videoCodec = convertAVC1ToAVCOTI(videoCodec);
|
@@ -10352,12 +10214,7 @@
|
|
10352
10214
|
if (curLevel.fragmentError === 0) {
|
10353
10215
|
curLevel.loadError = 0;
|
10354
10216
|
}
|
10355
|
-
|
10356
|
-
var previousDetails = curLevel.details;
|
10357
|
-
if (previousDetails === data.details && previousDetails.advanced) {
|
10358
|
-
previousDetails = undefined;
|
10359
|
-
}
|
10360
|
-
this.playlistLoaded(level, data, previousDetails);
|
10217
|
+
this.playlistLoaded(level, data, curLevel.details);
|
10361
10218
|
} else if ((_data$deliveryDirecti2 = data.deliveryDirectives) != null && _data$deliveryDirecti2.skip) {
|
10362
10219
|
// received a delta playlist update that cannot be merged
|
10363
10220
|
details.deltaUpdateFailed = true;
|
@@ -10657,14 +10514,11 @@
|
|
10657
10514
|
* If not found any Fragment, return null
|
10658
10515
|
*/;
|
10659
10516
|
_proto.getBufferedFrag = function getBufferedFrag(position, levelType) {
|
10660
|
-
return this.getFragAtPos(position, levelType, true);
|
10661
|
-
};
|
10662
|
-
_proto.getFragAtPos = function getFragAtPos(position, levelType, buffered) {
|
10663
10517
|
var fragments = this.fragments;
|
10664
10518
|
var keys = Object.keys(fragments);
|
10665
10519
|
for (var i = keys.length; i--;) {
|
10666
10520
|
var fragmentEntity = fragments[keys[i]];
|
10667
|
-
if ((fragmentEntity == null ? void 0 : fragmentEntity.body.type) === levelType &&
|
10521
|
+
if ((fragmentEntity == null ? void 0 : fragmentEntity.body.type) === levelType && fragmentEntity.buffered) {
|
10668
10522
|
var frag = fragmentEntity.body;
|
10669
10523
|
if (frag.start <= position && position <= frag.end) {
|
10670
10524
|
return frag;
|
@@ -10914,10 +10768,10 @@
|
|
10914
10768
|
};
|
10915
10769
|
};
|
10916
10770
|
_proto.onBufferAppended = function onBufferAppended(event, data) {
|
10771
|
+
var _this3 = this;
|
10917
10772
|
var frag = data.frag,
|
10918
10773
|
part = data.part,
|
10919
|
-
timeRanges = data.timeRanges
|
10920
|
-
type = data.type;
|
10774
|
+
timeRanges = data.timeRanges;
|
10921
10775
|
if (frag.sn === 'initSegment') {
|
10922
10776
|
return;
|
10923
10777
|
}
|
@@ -10931,8 +10785,10 @@
|
|
10931
10785
|
}
|
10932
10786
|
// Store the latest timeRanges loaded in the buffer
|
10933
10787
|
this.timeRanges = timeRanges;
|
10934
|
-
|
10935
|
-
|
10788
|
+
Object.keys(timeRanges).forEach(function (elementaryStream) {
|
10789
|
+
var timeRange = timeRanges[elementaryStream];
|
10790
|
+
_this3.detectEvictedFragments(elementaryStream, timeRange, playlistType, part);
|
10791
|
+
});
|
10936
10792
|
};
|
10937
10793
|
_proto.onFragBuffered = function onFragBuffered(event, data) {
|
10938
10794
|
this.detectPartialFragments(data);
|
@@ -10946,12 +10802,12 @@
|
|
10946
10802
|
return !!((_this$activePartLists = this.activePartLists[type]) != null && _this$activePartLists.length);
|
10947
10803
|
};
|
10948
10804
|
_proto.removeFragmentsInRange = function removeFragmentsInRange(start, end, playlistType, withGapOnly, unbufferedOnly) {
|
10949
|
-
var
|
10805
|
+
var _this4 = this;
|
10950
10806
|
if (withGapOnly && !this.hasGaps) {
|
10951
10807
|
return;
|
10952
10808
|
}
|
10953
10809
|
Object.keys(this.fragments).forEach(function (key) {
|
10954
|
-
var fragmentEntity =
|
10810
|
+
var fragmentEntity = _this4.fragments[key];
|
10955
10811
|
if (!fragmentEntity) {
|
10956
10812
|
return;
|
10957
10813
|
}
|
@@ -10960,7 +10816,7 @@
|
|
10960
10816
|
return;
|
10961
10817
|
}
|
10962
10818
|
if (frag.start < end && frag.end > start && (fragmentEntity.buffered || unbufferedOnly)) {
|
10963
|
-
|
10819
|
+
_this4.removeFragment(frag);
|
10964
10820
|
}
|
10965
10821
|
});
|
10966
10822
|
};
|
@@ -11273,8 +11129,8 @@
|
|
11273
11129
|
var _frag$decryptdata;
|
11274
11130
|
var byteRangeStart = start;
|
11275
11131
|
var byteRangeEnd = end;
|
11276
|
-
if (frag.sn === 'initSegment' &&
|
11277
|
-
// MAP segment encrypted with method 'AES-128'
|
11132
|
+
if (frag.sn === 'initSegment' && ((_frag$decryptdata = frag.decryptdata) == null ? void 0 : _frag$decryptdata.method) === 'AES-128') {
|
11133
|
+
// MAP segment encrypted with method 'AES-128', when served with HTTP Range,
|
11278
11134
|
// has the unencrypted size specified in the range.
|
11279
11135
|
// Ref: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-6.3.6
|
11280
11136
|
var fragmentLen = end - start;
|
@@ -11307,9 +11163,6 @@
|
|
11307
11163
|
(part ? part : frag).stats.aborted = true;
|
11308
11164
|
return new LoadError(errorData);
|
11309
11165
|
}
|
11310
|
-
function isMethodFullSegmentAesCbc(method) {
|
11311
|
-
return method === 'AES-128' || method === 'AES-256';
|
11312
|
-
}
|
11313
11166
|
var LoadError = /*#__PURE__*/function (_Error) {
|
11314
11167
|
_inheritsLoose(LoadError, _Error);
|
11315
11168
|
function LoadError(data) {
|
@@ -11466,8 +11319,6 @@
|
|
11466
11319
|
}
|
11467
11320
|
return this.loadKeyEME(keyInfo, frag);
|
11468
11321
|
case 'AES-128':
|
11469
|
-
case 'AES-256':
|
11470
|
-
case 'AES-256-CTR':
|
11471
11322
|
return this.loadKeyHTTP(keyInfo, frag);
|
11472
11323
|
default:
|
11473
11324
|
return Promise.reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error("Key supplied with unsupported METHOD: \"" + decryptdata.method + "\"")));
|
@@ -11601,17 +11452,13 @@
|
|
11601
11452
|
* we are limiting the task execution per call stack to exactly one, but scheduling/post-poning further
|
11602
11453
|
* task processing on the next main loop iteration (also known as "next tick" in the Node/JS runtime lingo).
|
11603
11454
|
*/
|
11604
|
-
var TaskLoop = /*#__PURE__*/function (
|
11605
|
-
|
11606
|
-
|
11607
|
-
|
11608
|
-
|
11609
|
-
|
11610
|
-
|
11611
|
-
_this._tickInterval = null;
|
11612
|
-
_this._tickCallCount = 0;
|
11613
|
-
_this._boundTick = _this.tick.bind(_assertThisInitialized(_this));
|
11614
|
-
return _this;
|
11455
|
+
var TaskLoop = /*#__PURE__*/function () {
|
11456
|
+
function TaskLoop() {
|
11457
|
+
this._boundTick = void 0;
|
11458
|
+
this._tickTimer = null;
|
11459
|
+
this._tickInterval = null;
|
11460
|
+
this._tickCallCount = 0;
|
11461
|
+
this._boundTick = this.tick.bind(this);
|
11615
11462
|
}
|
11616
11463
|
var _proto = TaskLoop.prototype;
|
11617
11464
|
_proto.destroy = function destroy() {
|
@@ -11697,7 +11544,7 @@
|
|
11697
11544
|
*/;
|
11698
11545
|
_proto.doTick = function doTick() {};
|
11699
11546
|
return TaskLoop;
|
11700
|
-
}(
|
11547
|
+
}();
|
11701
11548
|
|
11702
11549
|
var ChunkMetadata = function ChunkMetadata(level, sn, id, size, part, partial) {
|
11703
11550
|
if (size === void 0) {
|
@@ -11883,65 +11730,37 @@
|
|
11883
11730
|
}
|
11884
11731
|
|
11885
11732
|
var AESCrypto = /*#__PURE__*/function () {
|
11886
|
-
function AESCrypto(subtle, iv
|
11733
|
+
function AESCrypto(subtle, iv) {
|
11887
11734
|
this.subtle = void 0;
|
11888
11735
|
this.aesIV = void 0;
|
11889
|
-
this.aesMode = void 0;
|
11890
11736
|
this.subtle = subtle;
|
11891
11737
|
this.aesIV = iv;
|
11892
|
-
this.aesMode = aesMode;
|
11893
11738
|
}
|
11894
11739
|
var _proto = AESCrypto.prototype;
|
11895
11740
|
_proto.decrypt = function decrypt(data, key) {
|
11896
|
-
|
11897
|
-
|
11898
|
-
|
11899
|
-
|
11900
|
-
iv: this.aesIV
|
11901
|
-
}, key, data);
|
11902
|
-
case DecrypterAesMode.ctr:
|
11903
|
-
return this.subtle.decrypt({
|
11904
|
-
name: 'AES-CTR',
|
11905
|
-
counter: this.aesIV,
|
11906
|
-
length: 64
|
11907
|
-
},
|
11908
|
-
//64 : NIST SP800-38A standard suggests that the counter should occupy half of the counter block
|
11909
|
-
key, data);
|
11910
|
-
default:
|
11911
|
-
throw new Error("[AESCrypto] invalid aes mode " + this.aesMode);
|
11912
|
-
}
|
11741
|
+
return this.subtle.decrypt({
|
11742
|
+
name: 'AES-CBC',
|
11743
|
+
iv: this.aesIV
|
11744
|
+
}, key, data);
|
11913
11745
|
};
|
11914
11746
|
return AESCrypto;
|
11915
11747
|
}();
|
11916
11748
|
|
11917
11749
|
var FastAESKey = /*#__PURE__*/function () {
|
11918
|
-
function FastAESKey(subtle, key
|
11750
|
+
function FastAESKey(subtle, key) {
|
11919
11751
|
this.subtle = void 0;
|
11920
11752
|
this.key = void 0;
|
11921
|
-
this.aesMode = void 0;
|
11922
11753
|
this.subtle = subtle;
|
11923
11754
|
this.key = key;
|
11924
|
-
this.aesMode = aesMode;
|
11925
11755
|
}
|
11926
11756
|
var _proto = FastAESKey.prototype;
|
11927
11757
|
_proto.expandKey = function expandKey() {
|
11928
|
-
var subtleAlgoName = getSubtleAlgoName(this.aesMode);
|
11929
11758
|
return this.subtle.importKey('raw', this.key, {
|
11930
|
-
name:
|
11759
|
+
name: 'AES-CBC'
|
11931
11760
|
}, false, ['encrypt', 'decrypt']);
|
11932
11761
|
};
|
11933
11762
|
return FastAESKey;
|
11934
11763
|
}();
|
11935
|
-
function getSubtleAlgoName(aesMode) {
|
11936
|
-
switch (aesMode) {
|
11937
|
-
case DecrypterAesMode.cbc:
|
11938
|
-
return 'AES-CBC';
|
11939
|
-
case DecrypterAesMode.ctr:
|
11940
|
-
return 'AES-CTR';
|
11941
|
-
default:
|
11942
|
-
throw new Error("[FastAESKey] invalid aes mode " + aesMode);
|
11943
|
-
}
|
11944
|
-
}
|
11945
11764
|
|
11946
11765
|
// PKCS7
|
11947
11766
|
function removePadding(array) {
|
@@ -12194,8 +12013,7 @@
|
|
12194
12013
|
this.currentIV = null;
|
12195
12014
|
this.currentResult = null;
|
12196
12015
|
this.useSoftware = void 0;
|
12197
|
-
this.
|
12198
|
-
this.enableSoftwareAES = config.enableSoftwareAES;
|
12016
|
+
this.useSoftware = config.enableSoftwareAES;
|
12199
12017
|
this.removePKCS7Padding = removePKCS7Padding;
|
12200
12018
|
// built in decryptor expects PKCS7 padding
|
12201
12019
|
if (removePKCS7Padding) {
|
@@ -12208,7 +12026,9 @@
|
|
12208
12026
|
/* no-op */
|
12209
12027
|
}
|
12210
12028
|
}
|
12211
|
-
|
12029
|
+
if (this.subtle === null) {
|
12030
|
+
this.useSoftware = true;
|
12031
|
+
}
|
12212
12032
|
}
|
12213
12033
|
var _proto = Decrypter.prototype;
|
12214
12034
|
_proto.destroy = function destroy() {
|
@@ -12245,11 +12065,11 @@
|
|
12245
12065
|
this.softwareDecrypter = null;
|
12246
12066
|
}
|
12247
12067
|
};
|
12248
|
-
_proto.decrypt = function decrypt(data, key, iv
|
12068
|
+
_proto.decrypt = function decrypt(data, key, iv) {
|
12249
12069
|
var _this = this;
|
12250
12070
|
if (this.useSoftware) {
|
12251
12071
|
return new Promise(function (resolve, reject) {
|
12252
|
-
_this.softwareDecrypt(new Uint8Array(data), key, iv
|
12072
|
+
_this.softwareDecrypt(new Uint8Array(data), key, iv);
|
12253
12073
|
var decryptResult = _this.flush();
|
12254
12074
|
if (decryptResult) {
|
12255
12075
|
resolve(decryptResult.buffer);
|
@@ -12258,20 +12078,16 @@
|
|
12258
12078
|
}
|
12259
12079
|
});
|
12260
12080
|
}
|
12261
|
-
return this.webCryptoDecrypt(new Uint8Array(data), key, iv
|
12081
|
+
return this.webCryptoDecrypt(new Uint8Array(data), key, iv);
|
12262
12082
|
}
|
12263
12083
|
|
12264
12084
|
// Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached
|
12265
12085
|
// data is handled in the flush() call
|
12266
12086
|
;
|
12267
|
-
_proto.softwareDecrypt = function softwareDecrypt(data, key, iv
|
12087
|
+
_proto.softwareDecrypt = function softwareDecrypt(data, key, iv) {
|
12268
12088
|
var currentIV = this.currentIV,
|
12269
12089
|
currentResult = this.currentResult,
|
12270
12090
|
remainderData = this.remainderData;
|
12271
|
-
if (aesMode !== DecrypterAesMode.cbc || key.byteLength !== 16) {
|
12272
|
-
logger.warn('SoftwareDecrypt: can only handle AES-128-CBC');
|
12273
|
-
return null;
|
12274
|
-
}
|
12275
12091
|
this.logOnce('JS AES decrypt');
|
12276
12092
|
// The output is staggered during progressive parsing - the current result is cached, and emitted on the next call
|
12277
12093
|
// This is done in order to strip PKCS7 padding, which is found at the end of each segment. We only know we've reached
|
@@ -12304,12 +12120,12 @@
|
|
12304
12120
|
}
|
12305
12121
|
return result;
|
12306
12122
|
};
|
12307
|
-
_proto.webCryptoDecrypt = function webCryptoDecrypt(data, key, iv
|
12123
|
+
_proto.webCryptoDecrypt = function webCryptoDecrypt(data, key, iv) {
|
12308
12124
|
var _this2 = this;
|
12309
12125
|
var subtle = this.subtle;
|
12310
12126
|
if (this.key !== key || !this.fastAesKey) {
|
12311
12127
|
this.key = key;
|
12312
|
-
this.fastAesKey = new FastAESKey(subtle, key
|
12128
|
+
this.fastAesKey = new FastAESKey(subtle, key);
|
12313
12129
|
}
|
12314
12130
|
return this.fastAesKey.expandKey().then(function (aesKey) {
|
12315
12131
|
// decrypt using web crypto
|
@@ -12317,25 +12133,22 @@
|
|
12317
12133
|
return Promise.reject(new Error('web crypto not initialized'));
|
12318
12134
|
}
|
12319
12135
|
_this2.logOnce('WebCrypto AES decrypt');
|
12320
|
-
var crypto = new AESCrypto(subtle, new Uint8Array(iv)
|
12136
|
+
var crypto = new AESCrypto(subtle, new Uint8Array(iv));
|
12321
12137
|
return crypto.decrypt(data.buffer, aesKey);
|
12322
12138
|
}).catch(function (err) {
|
12323
12139
|
logger.warn("[decrypter]: WebCrypto Error, disable WebCrypto API, " + err.name + ": " + err.message);
|
12324
|
-
return _this2.onWebCryptoError(data, key, iv
|
12140
|
+
return _this2.onWebCryptoError(data, key, iv);
|
12325
12141
|
});
|
12326
12142
|
};
|
12327
|
-
_proto.onWebCryptoError = function onWebCryptoError(data, key, iv
|
12328
|
-
|
12329
|
-
|
12330
|
-
|
12331
|
-
|
12332
|
-
|
12333
|
-
|
12334
|
-
if (decryptResult) {
|
12335
|
-
return decryptResult.buffer;
|
12336
|
-
}
|
12143
|
+
_proto.onWebCryptoError = function onWebCryptoError(data, key, iv) {
|
12144
|
+
this.useSoftware = true;
|
12145
|
+
this.logEnabled = true;
|
12146
|
+
this.softwareDecrypt(data, key, iv);
|
12147
|
+
var decryptResult = this.flush();
|
12148
|
+
if (decryptResult) {
|
12149
|
+
return decryptResult.buffer;
|
12337
12150
|
}
|
12338
|
-
throw new Error('WebCrypto
|
12151
|
+
throw new Error('WebCrypto and softwareDecrypt: failed to decrypt data');
|
12339
12152
|
};
|
12340
12153
|
_proto.getValidChunk = function getValidChunk(data) {
|
12341
12154
|
var currentChunk = data;
|
@@ -12389,7 +12202,7 @@
|
|
12389
12202
|
_inheritsLoose(BaseStreamController, _TaskLoop);
|
12390
12203
|
function BaseStreamController(hls, fragmentTracker, keyLoader, logPrefix, playlistType) {
|
12391
12204
|
var _this;
|
12392
|
-
_this = _TaskLoop.call(this
|
12205
|
+
_this = _TaskLoop.call(this) || this;
|
12393
12206
|
_this.hls = void 0;
|
12394
12207
|
_this.fragPrevious = null;
|
12395
12208
|
_this.fragCurrent = null;
|
@@ -12414,96 +12227,25 @@
|
|
12414
12227
|
_this.startFragRequested = false;
|
12415
12228
|
_this.decrypter = void 0;
|
12416
12229
|
_this.initPTS = [];
|
12417
|
-
_this.
|
12418
|
-
_this.
|
12419
|
-
_this.
|
12420
|
-
|
12421
|
-
|
12422
|
-
fragCurrent = _assertThisInitialize.fragCurrent,
|
12423
|
-
media = _assertThisInitialize.media,
|
12424
|
-
mediaBuffer = _assertThisInitialize.mediaBuffer,
|
12425
|
-
state = _assertThisInitialize.state;
|
12426
|
-
var currentTime = media ? media.currentTime : 0;
|
12427
|
-
var bufferInfo = BufferHelper.bufferInfo(mediaBuffer ? mediaBuffer : media, currentTime, config.maxBufferHole);
|
12428
|
-
_this.log("media seeking to " + (isFiniteNumber(currentTime) ? currentTime.toFixed(3) : currentTime) + ", state: " + state);
|
12429
|
-
if (_this.state === State.ENDED) {
|
12430
|
-
_this.resetLoadingState();
|
12431
|
-
} else if (fragCurrent) {
|
12432
|
-
// Seeking while frag load is in progress
|
12433
|
-
var tolerance = config.maxFragLookUpTolerance;
|
12434
|
-
var fragStartOffset = fragCurrent.start - tolerance;
|
12435
|
-
var fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance;
|
12436
|
-
// if seeking out of buffered range or into new one
|
12437
|
-
if (!bufferInfo.len || fragEndOffset < bufferInfo.start || fragStartOffset > bufferInfo.end) {
|
12438
|
-
var pastFragment = currentTime > fragEndOffset;
|
12439
|
-
// if the seek position is outside the current fragment range
|
12440
|
-
if (currentTime < fragStartOffset || pastFragment) {
|
12441
|
-
if (pastFragment && fragCurrent.loader) {
|
12442
|
-
_this.log('seeking outside of buffer while fragment load in progress, cancel fragment load');
|
12443
|
-
fragCurrent.abortRequests();
|
12444
|
-
_this.resetLoadingState();
|
12445
|
-
}
|
12446
|
-
_this.fragPrevious = null;
|
12447
|
-
}
|
12448
|
-
}
|
12449
|
-
}
|
12450
|
-
if (media) {
|
12451
|
-
// Remove gap fragments
|
12452
|
-
_this.fragmentTracker.removeFragmentsInRange(currentTime, Infinity, _this.playlistType, true);
|
12453
|
-
_this.lastCurrentTime = currentTime;
|
12454
|
-
if (!_this.loadingParts) {
|
12455
|
-
var bufferEnd = Math.max(bufferInfo.end, currentTime);
|
12456
|
-
var shouldLoadParts = _this.shouldLoadParts(_this.getLevelDetails(), bufferEnd);
|
12457
|
-
if (shouldLoadParts) {
|
12458
|
-
_this.log("LL-Part loading ON after seeking to " + currentTime.toFixed(2) + " with buffer @" + bufferEnd.toFixed(2));
|
12459
|
-
_this.loadingParts = shouldLoadParts;
|
12460
|
-
}
|
12461
|
-
}
|
12462
|
-
}
|
12463
|
-
|
12464
|
-
// in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target
|
12465
|
-
if (!_this.loadedmetadata && !bufferInfo.len) {
|
12466
|
-
_this.nextLoadPosition = _this.startPosition = currentTime;
|
12467
|
-
}
|
12468
|
-
|
12469
|
-
// Async tick to speed up processing
|
12470
|
-
_this.tickImmediate();
|
12471
|
-
};
|
12472
|
-
_this.onMediaEnded = function () {
|
12473
|
-
// reset startPosition and lastCurrentTime to restart playback @ stream beginning
|
12474
|
-
_this.startPosition = _this.lastCurrentTime = 0;
|
12475
|
-
if (_this.playlistType === PlaylistLevelType.MAIN) {
|
12476
|
-
_this.hls.trigger(Events.MEDIA_ENDED, {
|
12477
|
-
stalled: false
|
12478
|
-
});
|
12479
|
-
}
|
12480
|
-
};
|
12230
|
+
_this.onvseeking = null;
|
12231
|
+
_this.onvended = null;
|
12232
|
+
_this.logPrefix = '';
|
12233
|
+
_this.log = void 0;
|
12234
|
+
_this.warn = void 0;
|
12481
12235
|
_this.playlistType = playlistType;
|
12236
|
+
_this.logPrefix = logPrefix;
|
12237
|
+
_this.log = logger.log.bind(logger, logPrefix + ":");
|
12238
|
+
_this.warn = logger.warn.bind(logger, logPrefix + ":");
|
12482
12239
|
_this.hls = hls;
|
12483
12240
|
_this.fragmentLoader = new FragmentLoader(hls.config);
|
12484
12241
|
_this.keyLoader = keyLoader;
|
12485
12242
|
_this.fragmentTracker = fragmentTracker;
|
12486
12243
|
_this.config = hls.config;
|
12487
12244
|
_this.decrypter = new Decrypter(hls.config);
|
12245
|
+
hls.on(Events.MANIFEST_LOADED, _this.onManifestLoaded, _assertThisInitialized(_this));
|
12488
12246
|
return _this;
|
12489
12247
|
}
|
12490
12248
|
var _proto = BaseStreamController.prototype;
|
12491
|
-
_proto.registerListeners = function registerListeners() {
|
12492
|
-
var hls = this.hls;
|
12493
|
-
hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
|
12494
|
-
hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
|
12495
|
-
hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
|
12496
|
-
hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
|
12497
|
-
hls.on(Events.ERROR, this.onError, this);
|
12498
|
-
};
|
12499
|
-
_proto.unregisterListeners = function unregisterListeners() {
|
12500
|
-
var hls = this.hls;
|
12501
|
-
hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
|
12502
|
-
hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
|
12503
|
-
hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
|
12504
|
-
hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
|
12505
|
-
hls.off(Events.ERROR, this.onError, this);
|
12506
|
-
};
|
12507
12249
|
_proto.doTick = function doTick() {
|
12508
12250
|
this.onTickEnd();
|
12509
12251
|
};
|
@@ -12527,12 +12269,6 @@
|
|
12527
12269
|
this.clearNextTick();
|
12528
12270
|
this.state = State.STOPPED;
|
12529
12271
|
};
|
12530
|
-
_proto.pauseBuffering = function pauseBuffering() {
|
12531
|
-
this.buffering = false;
|
12532
|
-
};
|
12533
|
-
_proto.resumeBuffering = function resumeBuffering() {
|
12534
|
-
this.buffering = true;
|
12535
|
-
};
|
12536
12272
|
_proto._streamEnded = function _streamEnded(bufferInfo, levelDetails) {
|
12537
12273
|
// If playlist is live, there is another buffered range after the current range, nothing buffered, media is detached,
|
12538
12274
|
// of nothing loading/loaded return false
|
@@ -12563,8 +12299,10 @@
|
|
12563
12299
|
};
|
12564
12300
|
_proto.onMediaAttached = function onMediaAttached(event, data) {
|
12565
12301
|
var media = this.media = this.mediaBuffer = data.media;
|
12566
|
-
|
12567
|
-
|
12302
|
+
this.onvseeking = this.onMediaSeeking.bind(this);
|
12303
|
+
this.onvended = this.onMediaEnded.bind(this);
|
12304
|
+
media.addEventListener('seeking', this.onvseeking);
|
12305
|
+
media.addEventListener('ended', this.onvended);
|
12568
12306
|
var config = this.config;
|
12569
12307
|
if (this.levels && config.autoStartLoad && this.state === State.STOPPED) {
|
12570
12308
|
this.startLoad(config.startPosition);
|
@@ -12578,9 +12316,10 @@
|
|
12578
12316
|
}
|
12579
12317
|
|
12580
12318
|
// remove video listeners
|
12581
|
-
if (media) {
|
12582
|
-
media.removeEventListener('seeking', this.
|
12583
|
-
media.removeEventListener('ended', this.
|
12319
|
+
if (media && this.onvseeking && this.onvended) {
|
12320
|
+
media.removeEventListener('seeking', this.onvseeking);
|
12321
|
+
media.removeEventListener('ended', this.onvended);
|
12322
|
+
this.onvseeking = this.onvended = null;
|
12584
12323
|
}
|
12585
12324
|
if (this.keyLoader) {
|
12586
12325
|
this.keyLoader.detach();
|
@@ -12590,8 +12329,54 @@
|
|
12590
12329
|
this.fragmentTracker.removeAllFragments();
|
12591
12330
|
this.stopLoad();
|
12592
12331
|
};
|
12593
|
-
_proto.
|
12594
|
-
|
12332
|
+
_proto.onMediaSeeking = function onMediaSeeking() {
|
12333
|
+
var config = this.config,
|
12334
|
+
fragCurrent = this.fragCurrent,
|
12335
|
+
media = this.media,
|
12336
|
+
mediaBuffer = this.mediaBuffer,
|
12337
|
+
state = this.state;
|
12338
|
+
var currentTime = media ? media.currentTime : 0;
|
12339
|
+
var bufferInfo = BufferHelper.bufferInfo(mediaBuffer ? mediaBuffer : media, currentTime, config.maxBufferHole);
|
12340
|
+
this.log("media seeking to " + (isFiniteNumber(currentTime) ? currentTime.toFixed(3) : currentTime) + ", state: " + state);
|
12341
|
+
if (this.state === State.ENDED) {
|
12342
|
+
this.resetLoadingState();
|
12343
|
+
} else if (fragCurrent) {
|
12344
|
+
// Seeking while frag load is in progress
|
12345
|
+
var tolerance = config.maxFragLookUpTolerance;
|
12346
|
+
var fragStartOffset = fragCurrent.start - tolerance;
|
12347
|
+
var fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance;
|
12348
|
+
// if seeking out of buffered range or into new one
|
12349
|
+
if (!bufferInfo.len || fragEndOffset < bufferInfo.start || fragStartOffset > bufferInfo.end) {
|
12350
|
+
var pastFragment = currentTime > fragEndOffset;
|
12351
|
+
// if the seek position is outside the current fragment range
|
12352
|
+
if (currentTime < fragStartOffset || pastFragment) {
|
12353
|
+
if (pastFragment && fragCurrent.loader) {
|
12354
|
+
this.log('seeking outside of buffer while fragment load in progress, cancel fragment load');
|
12355
|
+
fragCurrent.abortRequests();
|
12356
|
+
this.resetLoadingState();
|
12357
|
+
}
|
12358
|
+
this.fragPrevious = null;
|
12359
|
+
}
|
12360
|
+
}
|
12361
|
+
}
|
12362
|
+
if (media) {
|
12363
|
+
// Remove gap fragments
|
12364
|
+
this.fragmentTracker.removeFragmentsInRange(currentTime, Infinity, this.playlistType, true);
|
12365
|
+
this.lastCurrentTime = currentTime;
|
12366
|
+
}
|
12367
|
+
|
12368
|
+
// in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target
|
12369
|
+
if (!this.loadedmetadata && !bufferInfo.len) {
|
12370
|
+
this.nextLoadPosition = this.startPosition = currentTime;
|
12371
|
+
}
|
12372
|
+
|
12373
|
+
// Async tick to speed up processing
|
12374
|
+
this.tickImmediate();
|
12375
|
+
};
|
12376
|
+
_proto.onMediaEnded = function onMediaEnded() {
|
12377
|
+
// reset startPosition and lastCurrentTime to restart playback @ stream beginning
|
12378
|
+
this.startPosition = this.lastCurrentTime = 0;
|
12379
|
+
};
|
12595
12380
|
_proto.onManifestLoaded = function onManifestLoaded(event, data) {
|
12596
12381
|
this.startTimeOffset = data.startTimeOffset;
|
12597
12382
|
this.initPTS = [];
|
@@ -12601,7 +12386,7 @@
|
|
12601
12386
|
this.stopLoad();
|
12602
12387
|
_TaskLoop.prototype.onHandlerDestroying.call(this);
|
12603
12388
|
// @ts-ignore
|
12604
|
-
this.hls =
|
12389
|
+
this.hls = null;
|
12605
12390
|
};
|
12606
12391
|
_proto.onHandlerDestroyed = function onHandlerDestroyed() {
|
12607
12392
|
this.state = State.STOPPED;
|
@@ -12731,10 +12516,10 @@
|
|
12731
12516
|
var decryptData = frag.decryptdata;
|
12732
12517
|
|
12733
12518
|
// check to see if the payload needs to be decrypted
|
12734
|
-
if (payload && payload.byteLength > 0 && decryptData != null && decryptData.key && decryptData.iv &&
|
12519
|
+
if (payload && payload.byteLength > 0 && decryptData != null && decryptData.key && decryptData.iv && decryptData.method === 'AES-128') {
|
12735
12520
|
var startTime = self.performance.now();
|
12736
12521
|
// decrypt init segment data
|
12737
|
-
return _this3.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer
|
12522
|
+
return _this3.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer).catch(function (err) {
|
12738
12523
|
hls.trigger(Events.ERROR, {
|
12739
12524
|
type: ErrorTypes.MEDIA_ERROR,
|
12740
12525
|
details: ErrorDetails.FRAG_DECRYPT_ERROR,
|
@@ -12847,7 +12632,7 @@
|
|
12847
12632
|
}
|
12848
12633
|
var keyLoadingPromise = null;
|
12849
12634
|
if (frag.encrypted && !((_frag$decryptdata = frag.decryptdata) != null && _frag$decryptdata.key)) {
|
12850
|
-
this.log("Loading key for " + frag.sn + " of [" + details.startSN + "-" + details.endSN + "], " + (this.
|
12635
|
+
this.log("Loading key for " + frag.sn + " of [" + details.startSN + "-" + details.endSN + "], " + (this.logPrefix === '[stream-controller]' ? 'level' : 'track') + " " + frag.level);
|
12851
12636
|
this.state = State.KEY_LOADING;
|
12852
12637
|
this.fragCurrent = frag;
|
12853
12638
|
keyLoadingPromise = this.keyLoader.load(frag).then(function (keyLoadedData) {
|
@@ -12868,16 +12653,8 @@
|
|
12868
12653
|
} else if (!frag.encrypted && details.encryptedFragments.length) {
|
12869
12654
|
this.keyLoader.loadClear(frag, details.encryptedFragments);
|
12870
12655
|
}
|
12871
|
-
var fragPrevious = this.fragPrevious;
|
12872
|
-
if (frag.sn !== 'initSegment' && (!fragPrevious || frag.sn !== fragPrevious.sn)) {
|
12873
|
-
var shouldLoadParts = this.shouldLoadParts(level.details, frag.end);
|
12874
|
-
if (shouldLoadParts !== this.loadingParts) {
|
12875
|
-
this.log("LL-Part loading " + (shouldLoadParts ? 'ON' : 'OFF') + " loading sn " + (fragPrevious == null ? void 0 : fragPrevious.sn) + "->" + frag.sn);
|
12876
|
-
this.loadingParts = shouldLoadParts;
|
12877
|
-
}
|
12878
|
-
}
|
12879
12656
|
targetBufferTime = Math.max(frag.start, targetBufferTime || 0);
|
12880
|
-
if (this.
|
12657
|
+
if (this.config.lowLatencyMode && frag.sn !== 'initSegment') {
|
12881
12658
|
var partList = details.partList;
|
12882
12659
|
if (partList && progressCallback) {
|
12883
12660
|
if (targetBufferTime > frag.end && details.fragmentHint) {
|
@@ -12886,7 +12663,7 @@
|
|
12886
12663
|
var partIndex = this.getNextPart(partList, frag, targetBufferTime);
|
12887
12664
|
if (partIndex > -1) {
|
12888
12665
|
var part = partList[partIndex];
|
12889
|
-
this.log("Loading part sn: " + frag.sn + " p: " + part.index + " cc: " + frag.cc + " of playlist [" + details.startSN + "-" + details.endSN + "] parts [0-" + partIndex + "-" + (partList.length - 1) + "] " + (this.
|
12666
|
+
this.log("Loading part sn: " + frag.sn + " p: " + part.index + " cc: " + frag.cc + " of playlist [" + details.startSN + "-" + details.endSN + "] parts [0-" + partIndex + "-" + (partList.length - 1) + "] " + (this.logPrefix === '[stream-controller]' ? 'level' : 'track') + ": " + frag.level + ", target: " + parseFloat(targetBufferTime.toFixed(3)));
|
12890
12667
|
this.nextLoadPosition = part.start + part.duration;
|
12891
12668
|
this.state = State.FRAG_LOADING;
|
12892
12669
|
var _result;
|
@@ -12919,14 +12696,7 @@
|
|
12919
12696
|
}
|
12920
12697
|
}
|
12921
12698
|
}
|
12922
|
-
|
12923
|
-
this.log("LL-Part loading OFF after next part miss @" + targetBufferTime.toFixed(2));
|
12924
|
-
this.loadingParts = false;
|
12925
|
-
} else if (!frag.url) {
|
12926
|
-
// Selected fragment hint for part but not loading parts
|
12927
|
-
return Promise.resolve(null);
|
12928
|
-
}
|
12929
|
-
this.log("Loading fragment " + frag.sn + " cc: " + frag.cc + " " + (details ? 'of [' + details.startSN + '-' + details.endSN + '] ' : '') + (this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track') + ": " + frag.level + ", target: " + parseFloat(targetBufferTime.toFixed(3)));
|
12699
|
+
this.log("Loading fragment " + frag.sn + " cc: " + frag.cc + " " + (details ? 'of [' + details.startSN + '-' + details.endSN + '] ' : '') + (this.logPrefix === '[stream-controller]' ? 'level' : 'track') + ": " + frag.level + ", target: " + parseFloat(targetBufferTime.toFixed(3)));
|
12930
12700
|
// Don't update nextLoadPosition for fragments which are not buffered
|
12931
12701
|
if (isFiniteNumber(frag.sn) && !this.bitrateTest) {
|
12932
12702
|
this.nextLoadPosition = frag.start + frag.duration;
|
@@ -13028,36 +12798,8 @@
|
|
13028
12798
|
if (part) {
|
13029
12799
|
part.stats.parsing.end = now;
|
13030
12800
|
}
|
13031
|
-
// See if part loading should be disabled/enabled based on buffer and playback position.
|
13032
|
-
if (frag.sn !== 'initSegment') {
|
13033
|
-
var levelDetails = this.getLevelDetails();
|
13034
|
-
var loadingPartsAtEdge = levelDetails && frag.sn > levelDetails.endSN;
|
13035
|
-
var shouldLoadParts = loadingPartsAtEdge || this.shouldLoadParts(levelDetails, frag.end);
|
13036
|
-
if (shouldLoadParts !== this.loadingParts) {
|
13037
|
-
this.log("LL-Part loading " + (shouldLoadParts ? 'ON' : 'OFF') + " after parsing segment ending @" + frag.end.toFixed(2));
|
13038
|
-
this.loadingParts = shouldLoadParts;
|
13039
|
-
}
|
13040
|
-
}
|
13041
12801
|
this.updateLevelTiming(frag, part, level, chunkMeta.partial);
|
13042
12802
|
};
|
13043
|
-
_proto.shouldLoadParts = function shouldLoadParts(details, bufferEnd) {
|
13044
|
-
if (this.config.lowLatencyMode) {
|
13045
|
-
if (!details) {
|
13046
|
-
return this.loadingParts;
|
13047
|
-
}
|
13048
|
-
if (details != null && details.partList) {
|
13049
|
-
var _details$fragmentHint;
|
13050
|
-
// Buffer must be ahead of first part + duration of parts after last segment
|
13051
|
-
// and playback must be at or past segment adjacent to part list
|
13052
|
-
var firstPart = details.partList[0];
|
13053
|
-
var safePartStart = firstPart.end + (((_details$fragmentHint = details.fragmentHint) == null ? void 0 : _details$fragmentHint.duration) || 0);
|
13054
|
-
if (bufferEnd >= safePartStart && this.lastCurrentTime > firstPart.start - firstPart.fragment.duration) {
|
13055
|
-
return true;
|
13056
|
-
}
|
13057
|
-
}
|
13058
|
-
}
|
13059
|
-
return false;
|
13060
|
-
};
|
13061
12803
|
_proto.getCurrentContext = function getCurrentContext(chunkMeta) {
|
13062
12804
|
var levels = this.levels,
|
13063
12805
|
fragCurrent = this.fragCurrent;
|
@@ -13148,7 +12890,7 @@
|
|
13148
12890
|
// Workaround flaw in getting forward buffer when maxBufferHole is smaller than gap at current pos
|
13149
12891
|
if (bufferInfo.len === 0 && bufferInfo.nextStart !== undefined) {
|
13150
12892
|
var bufferedFragAtPos = this.fragmentTracker.getBufferedFrag(pos, type);
|
13151
|
-
if (bufferedFragAtPos &&
|
12893
|
+
if (bufferedFragAtPos && bufferInfo.nextStart < bufferedFragAtPos.end) {
|
13152
12894
|
return BufferHelper.bufferInfo(bufferable, pos, Math.max(bufferInfo.nextStart, maxBufferHole));
|
13153
12895
|
}
|
13154
12896
|
}
|
@@ -13192,8 +12934,7 @@
|
|
13192
12934
|
// find fragment index, contiguous with end of buffer position
|
13193
12935
|
var config = this.config;
|
13194
12936
|
var start = fragments[0].start;
|
13195
|
-
var
|
13196
|
-
var frag = null;
|
12937
|
+
var frag;
|
13197
12938
|
if (levelDetails.live) {
|
13198
12939
|
var initialLiveManifestSize = config.initialLiveManifestSize;
|
13199
12940
|
if (fragLen < initialLiveManifestSize) {
|
@@ -13205,10 +12946,6 @@
|
|
13205
12946
|
// Do not load using live logic if the starting frag is requested - we want to use getFragmentAtPosition() so that
|
13206
12947
|
// we get the fragment matching that start time
|
13207
12948
|
if (!levelDetails.PTSKnown && !this.startFragRequested && this.startPosition === -1 || pos < start) {
|
13208
|
-
if (canLoadParts && !this.loadingParts) {
|
13209
|
-
this.log("LL-Part loading ON for initial live fragment");
|
13210
|
-
this.loadingParts = true;
|
13211
|
-
}
|
13212
12949
|
frag = this.getInitialLiveFragment(levelDetails, fragments);
|
13213
12950
|
this.startPosition = this.nextLoadPosition = frag ? this.hls.liveSyncPosition || frag.start : pos;
|
13214
12951
|
}
|
@@ -13219,7 +12956,7 @@
|
|
13219
12956
|
|
13220
12957
|
// If we haven't run into any special cases already, just load the fragment most closely matching the requested position
|
13221
12958
|
if (!frag) {
|
13222
|
-
var end =
|
12959
|
+
var end = config.lowLatencyMode ? levelDetails.partEnd : levelDetails.fragmentEnd;
|
13223
12960
|
frag = this.getFragmentAtPosition(pos, end, levelDetails);
|
13224
12961
|
}
|
13225
12962
|
return this.mapToInitFragWhenRequired(frag);
|
@@ -13333,7 +13070,7 @@
|
|
13333
13070
|
var fragmentHint = levelDetails.fragmentHint;
|
13334
13071
|
var tolerance = config.maxFragLookUpTolerance;
|
13335
13072
|
var partList = levelDetails.partList;
|
13336
|
-
var loadingParts = !!(
|
13073
|
+
var loadingParts = !!(config.lowLatencyMode && partList != null && partList.length && fragmentHint);
|
13337
13074
|
if (loadingParts && fragmentHint && !this.bitrateTest) {
|
13338
13075
|
// Include incomplete fragment with parts at end
|
13339
13076
|
fragments = fragments.concat(fragmentHint);
|
@@ -13520,7 +13257,7 @@
|
|
13520
13257
|
errorAction.resolved = true;
|
13521
13258
|
}
|
13522
13259
|
} else {
|
13523
|
-
|
13260
|
+
logger.warn(data.details + " reached or exceeded max retry (" + retryCount + ")");
|
13524
13261
|
return;
|
13525
13262
|
}
|
13526
13263
|
} else if ((errorAction == null ? void 0 : errorAction.action) === NetworkErrorAction.SendAlternateToPenaltyBox) {
|
@@ -13588,9 +13325,7 @@
|
|
13588
13325
|
this.log('Reset loading state');
|
13589
13326
|
this.fragCurrent = null;
|
13590
13327
|
this.fragPrevious = null;
|
13591
|
-
|
13592
|
-
this.state = State.IDLE;
|
13593
|
-
}
|
13328
|
+
this.state = State.IDLE;
|
13594
13329
|
};
|
13595
13330
|
_proto.resetStartWhenNotLoaded = function resetStartWhenNotLoaded(level) {
|
13596
13331
|
// if loadedmetadata is not set, it means that first frag request failed
|
@@ -13912,7 +13647,6 @@
|
|
13912
13647
|
*/
|
13913
13648
|
function getAudioConfig(observer, data, offset, audioCodec) {
|
13914
13649
|
var adtsObjectType;
|
13915
|
-
var originalAdtsObjectType;
|
13916
13650
|
var adtsExtensionSamplingIndex;
|
13917
13651
|
var adtsChannelConfig;
|
13918
13652
|
var config;
|
@@ -13920,7 +13654,7 @@
|
|
13920
13654
|
var manifestCodec = audioCodec;
|
13921
13655
|
var adtsSamplingRates = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
|
13922
13656
|
// byte 2
|
13923
|
-
adtsObjectType =
|
13657
|
+
adtsObjectType = ((data[offset + 2] & 0xc0) >>> 6) + 1;
|
13924
13658
|
var adtsSamplingIndex = (data[offset + 2] & 0x3c) >>> 2;
|
13925
13659
|
if (adtsSamplingIndex > adtsSamplingRates.length - 1) {
|
13926
13660
|
var error = new Error("invalid ADTS sampling index:" + adtsSamplingIndex);
|
@@ -13937,8 +13671,8 @@
|
|
13937
13671
|
// byte 3
|
13938
13672
|
adtsChannelConfig |= (data[offset + 3] & 0xc0) >>> 6;
|
13939
13673
|
logger.log("manifest codec:" + audioCodec + ", ADTS type:" + adtsObjectType + ", samplingIndex:" + adtsSamplingIndex);
|
13940
|
-
//
|
13941
|
-
if (/firefox
|
13674
|
+
// firefox: freq less than 24kHz = AAC SBR (HE-AAC)
|
13675
|
+
if (/firefox/i.test(userAgent)) {
|
13942
13676
|
if (adtsSamplingIndex >= 6) {
|
13943
13677
|
adtsObjectType = 5;
|
13944
13678
|
config = new Array(4);
|
@@ -14032,7 +13766,6 @@
|
|
14032
13766
|
samplerate: adtsSamplingRates[adtsSamplingIndex],
|
14033
13767
|
channelCount: adtsChannelConfig,
|
14034
13768
|
codec: 'mp4a.40.' + adtsObjectType,
|
14035
|
-
parsedCodec: 'mp4a.40.' + originalAdtsObjectType,
|
14036
13769
|
manifestCodec: manifestCodec
|
14037
13770
|
};
|
14038
13771
|
}
|
@@ -14087,8 +13820,7 @@
|
|
14087
13820
|
track.channelCount = config.channelCount;
|
14088
13821
|
track.codec = config.codec;
|
14089
13822
|
track.manifestCodec = config.manifestCodec;
|
14090
|
-
track.
|
14091
|
-
logger.log("parsed codec:" + track.parsedCodec + ", codec:" + track.codec + ", rate:" + config.samplerate + ", channels:" + config.channelCount);
|
13823
|
+
logger.log("parsed codec:" + track.codec + ", rate:" + config.samplerate + ", channels:" + config.channelCount);
|
14092
13824
|
}
|
14093
13825
|
}
|
14094
13826
|
function getFrameDuration(samplerate) {
|
@@ -14568,116 +14300,12 @@
|
|
14568
14300
|
logger.log(VideoSample.pts + '/' + VideoSample.dts + ':' + VideoSample.debug);
|
14569
14301
|
}
|
14570
14302
|
};
|
14571
|
-
|
14572
|
-
|
14573
|
-
var state = track.naluState || 0;
|
14574
|
-
var lastState = state;
|
14575
|
-
var units = [];
|
14576
|
-
var i = 0;
|
14577
|
-
var value;
|
14578
|
-
var overflow;
|
14579
|
-
var unitType;
|
14580
|
-
var lastUnitStart = -1;
|
14581
|
-
var lastUnitType = 0;
|
14582
|
-
// logger.log('PES:' + Hex.hexDump(array));
|
14303
|
+
return BaseVideoParser;
|
14304
|
+
}();
|
14583
14305
|
|
14584
|
-
|
14585
|
-
|
14586
|
-
|
14587
|
-
// NALu type is value read from offset 0
|
14588
|
-
lastUnitType = this.getNALuType(array, 0);
|
14589
|
-
state = 0;
|
14590
|
-
i = 1;
|
14591
|
-
}
|
14592
|
-
while (i < len) {
|
14593
|
-
value = array[i++];
|
14594
|
-
// optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case
|
14595
|
-
if (!state) {
|
14596
|
-
state = value ? 0 : 1;
|
14597
|
-
continue;
|
14598
|
-
}
|
14599
|
-
if (state === 1) {
|
14600
|
-
state = value ? 0 : 2;
|
14601
|
-
continue;
|
14602
|
-
}
|
14603
|
-
// here we have state either equal to 2 or 3
|
14604
|
-
if (!value) {
|
14605
|
-
state = 3;
|
14606
|
-
} else if (value === 1) {
|
14607
|
-
overflow = i - state - 1;
|
14608
|
-
if (lastUnitStart >= 0) {
|
14609
|
-
var unit = {
|
14610
|
-
data: array.subarray(lastUnitStart, overflow),
|
14611
|
-
type: lastUnitType
|
14612
|
-
};
|
14613
|
-
// logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength);
|
14614
|
-
units.push(unit);
|
14615
|
-
} else {
|
14616
|
-
// lastUnitStart is undefined => this is the first start code found in this PES packet
|
14617
|
-
// first check if start code delimiter is overlapping between 2 PES packets,
|
14618
|
-
// ie it started in last packet (lastState not zero)
|
14619
|
-
// and ended at the beginning of this PES packet (i <= 4 - lastState)
|
14620
|
-
var lastUnit = this.getLastNalUnit(track.samples);
|
14621
|
-
if (lastUnit) {
|
14622
|
-
if (lastState && i <= 4 - lastState) {
|
14623
|
-
// start delimiter overlapping between PES packets
|
14624
|
-
// strip start delimiter bytes from the end of last NAL unit
|
14625
|
-
// check if lastUnit had a state different from zero
|
14626
|
-
if (lastUnit.state) {
|
14627
|
-
// strip last bytes
|
14628
|
-
lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState);
|
14629
|
-
}
|
14630
|
-
}
|
14631
|
-
// If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit.
|
14632
|
-
|
14633
|
-
if (overflow > 0) {
|
14634
|
-
// logger.log('first NALU found with overflow:' + overflow);
|
14635
|
-
lastUnit.data = appendUint8Array(lastUnit.data, array.subarray(0, overflow));
|
14636
|
-
lastUnit.state = 0;
|
14637
|
-
}
|
14638
|
-
}
|
14639
|
-
}
|
14640
|
-
// check if we can read unit type
|
14641
|
-
if (i < len) {
|
14642
|
-
unitType = this.getNALuType(array, i);
|
14643
|
-
// logger.log('find NALU @ offset:' + i + ',type:' + unitType);
|
14644
|
-
lastUnitStart = i;
|
14645
|
-
lastUnitType = unitType;
|
14646
|
-
state = 0;
|
14647
|
-
} else {
|
14648
|
-
// not enough byte to read unit type. let's read it on next PES parsing
|
14649
|
-
state = -1;
|
14650
|
-
}
|
14651
|
-
} else {
|
14652
|
-
state = 0;
|
14653
|
-
}
|
14654
|
-
}
|
14655
|
-
if (lastUnitStart >= 0 && state >= 0) {
|
14656
|
-
var _unit = {
|
14657
|
-
data: array.subarray(lastUnitStart, len),
|
14658
|
-
type: lastUnitType,
|
14659
|
-
state: state
|
14660
|
-
};
|
14661
|
-
units.push(_unit);
|
14662
|
-
// logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state);
|
14663
|
-
}
|
14664
|
-
// no NALu found
|
14665
|
-
if (units.length === 0) {
|
14666
|
-
// append pes.data to previous NAL unit
|
14667
|
-
var _lastUnit = this.getLastNalUnit(track.samples);
|
14668
|
-
if (_lastUnit) {
|
14669
|
-
_lastUnit.data = appendUint8Array(_lastUnit.data, array);
|
14670
|
-
}
|
14671
|
-
}
|
14672
|
-
track.naluState = state;
|
14673
|
-
return units;
|
14674
|
-
};
|
14675
|
-
return BaseVideoParser;
|
14676
|
-
}();
|
14677
|
-
|
14678
|
-
/**
|
14679
|
-
* Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264.
|
14680
|
-
*/
|
14306
|
+
/**
|
14307
|
+
* Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264.
|
14308
|
+
*/
|
14681
14309
|
|
14682
14310
|
var ExpGolomb = /*#__PURE__*/function () {
|
14683
14311
|
function ExpGolomb(data) {
|
@@ -14826,179 +14454,22 @@
|
|
14826
14454
|
;
|
14827
14455
|
_proto.readUInt = function readUInt() {
|
14828
14456
|
return this.readBits(32);
|
14829
|
-
};
|
14830
|
-
return ExpGolomb;
|
14831
|
-
}();
|
14832
|
-
|
14833
|
-
var AvcVideoParser = /*#__PURE__*/function (_BaseVideoParser) {
|
14834
|
-
_inheritsLoose(AvcVideoParser, _BaseVideoParser);
|
14835
|
-
function AvcVideoParser() {
|
14836
|
-
return _BaseVideoParser.apply(this, arguments) || this;
|
14837
|
-
}
|
14838
|
-
var _proto = AvcVideoParser.prototype;
|
14839
|
-
_proto.parsePES = function parsePES(track, textTrack, pes, last, duration) {
|
14840
|
-
var _this = this;
|
14841
|
-
var units = this.parseNALu(track, pes.data);
|
14842
|
-
var VideoSample = this.VideoSample;
|
14843
|
-
var push;
|
14844
|
-
var spsfound = false;
|
14845
|
-
// free pes.data to save up some memory
|
14846
|
-
pes.data = null;
|
14847
|
-
|
14848
|
-
// if new NAL units found and last sample still there, let's push ...
|
14849
|
-
// this helps parsing streams with missing AUD (only do this if AUD never found)
|
14850
|
-
if (VideoSample && units.length && !track.audFound) {
|
14851
|
-
this.pushAccessUnit(VideoSample, track);
|
14852
|
-
VideoSample = this.VideoSample = this.createVideoSample(false, pes.pts, pes.dts, '');
|
14853
|
-
}
|
14854
|
-
units.forEach(function (unit) {
|
14855
|
-
var _VideoSample2;
|
14856
|
-
switch (unit.type) {
|
14857
|
-
// NDR
|
14858
|
-
case 1:
|
14859
|
-
{
|
14860
|
-
var iskey = false;
|
14861
|
-
push = true;
|
14862
|
-
var data = unit.data;
|
14863
|
-
// only check slice type to detect KF in case SPS found in same packet (any keyframe is preceded by SPS ...)
|
14864
|
-
if (spsfound && data.length > 4) {
|
14865
|
-
// retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR
|
14866
|
-
var sliceType = _this.readSliceType(data);
|
14867
|
-
// 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice
|
14868
|
-
// SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples.
|
14869
|
-
// An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice.
|
14870
|
-
// I slice: A slice that is not an SI slice that is decoded using intra prediction only.
|
14871
|
-
// if (sliceType === 2 || sliceType === 7) {
|
14872
|
-
if (sliceType === 2 || sliceType === 4 || sliceType === 7 || sliceType === 9) {
|
14873
|
-
iskey = true;
|
14874
|
-
}
|
14875
|
-
}
|
14876
|
-
if (iskey) {
|
14877
|
-
var _VideoSample;
|
14878
|
-
// if we have non-keyframe data already, that cannot belong to the same frame as a keyframe, so force a push
|
14879
|
-
if ((_VideoSample = VideoSample) != null && _VideoSample.frame && !VideoSample.key) {
|
14880
|
-
_this.pushAccessUnit(VideoSample, track);
|
14881
|
-
VideoSample = _this.VideoSample = null;
|
14882
|
-
}
|
14883
|
-
}
|
14884
|
-
if (!VideoSample) {
|
14885
|
-
VideoSample = _this.VideoSample = _this.createVideoSample(true, pes.pts, pes.dts, '');
|
14886
|
-
}
|
14887
|
-
VideoSample.frame = true;
|
14888
|
-
VideoSample.key = iskey;
|
14889
|
-
break;
|
14890
|
-
// IDR
|
14891
|
-
}
|
14892
|
-
case 5:
|
14893
|
-
push = true;
|
14894
|
-
// handle PES not starting with AUD
|
14895
|
-
// if we have frame data already, that cannot belong to the same frame, so force a push
|
14896
|
-
if ((_VideoSample2 = VideoSample) != null && _VideoSample2.frame && !VideoSample.key) {
|
14897
|
-
_this.pushAccessUnit(VideoSample, track);
|
14898
|
-
VideoSample = _this.VideoSample = null;
|
14899
|
-
}
|
14900
|
-
if (!VideoSample) {
|
14901
|
-
VideoSample = _this.VideoSample = _this.createVideoSample(true, pes.pts, pes.dts, '');
|
14902
|
-
}
|
14903
|
-
VideoSample.key = true;
|
14904
|
-
VideoSample.frame = true;
|
14905
|
-
break;
|
14906
|
-
// SEI
|
14907
|
-
case 6:
|
14908
|
-
{
|
14909
|
-
push = true;
|
14910
|
-
parseSEIMessageFromNALu(unit.data, 1, pes.pts, textTrack.samples);
|
14911
|
-
break;
|
14912
|
-
// SPS
|
14913
|
-
}
|
14914
|
-
case 7:
|
14915
|
-
{
|
14916
|
-
var _track$pixelRatio, _track$pixelRatio2;
|
14917
|
-
push = true;
|
14918
|
-
spsfound = true;
|
14919
|
-
var sps = unit.data;
|
14920
|
-
var config = _this.readSPS(sps);
|
14921
|
-
if (!track.sps || track.width !== config.width || track.height !== config.height || ((_track$pixelRatio = track.pixelRatio) == null ? void 0 : _track$pixelRatio[0]) !== config.pixelRatio[0] || ((_track$pixelRatio2 = track.pixelRatio) == null ? void 0 : _track$pixelRatio2[1]) !== config.pixelRatio[1]) {
|
14922
|
-
track.width = config.width;
|
14923
|
-
track.height = config.height;
|
14924
|
-
track.pixelRatio = config.pixelRatio;
|
14925
|
-
track.sps = [sps];
|
14926
|
-
track.duration = duration;
|
14927
|
-
var codecarray = sps.subarray(1, 4);
|
14928
|
-
var codecstring = 'avc1.';
|
14929
|
-
for (var i = 0; i < 3; i++) {
|
14930
|
-
var h = codecarray[i].toString(16);
|
14931
|
-
if (h.length < 2) {
|
14932
|
-
h = '0' + h;
|
14933
|
-
}
|
14934
|
-
codecstring += h;
|
14935
|
-
}
|
14936
|
-
track.codec = codecstring;
|
14937
|
-
}
|
14938
|
-
break;
|
14939
|
-
}
|
14940
|
-
// PPS
|
14941
|
-
case 8:
|
14942
|
-
push = true;
|
14943
|
-
track.pps = [unit.data];
|
14944
|
-
break;
|
14945
|
-
// AUD
|
14946
|
-
case 9:
|
14947
|
-
push = true;
|
14948
|
-
track.audFound = true;
|
14949
|
-
if (VideoSample) {
|
14950
|
-
_this.pushAccessUnit(VideoSample, track);
|
14951
|
-
}
|
14952
|
-
VideoSample = _this.VideoSample = _this.createVideoSample(false, pes.pts, pes.dts, '');
|
14953
|
-
break;
|
14954
|
-
// Filler Data
|
14955
|
-
case 12:
|
14956
|
-
push = true;
|
14957
|
-
break;
|
14958
|
-
default:
|
14959
|
-
push = false;
|
14960
|
-
if (VideoSample) {
|
14961
|
-
VideoSample.debug += 'unknown NAL ' + unit.type + ' ';
|
14962
|
-
}
|
14963
|
-
break;
|
14964
|
-
}
|
14965
|
-
if (VideoSample && push) {
|
14966
|
-
var _units = VideoSample.units;
|
14967
|
-
_units.push(unit);
|
14968
|
-
}
|
14969
|
-
});
|
14970
|
-
// if last PES packet, push samples
|
14971
|
-
if (last && VideoSample) {
|
14972
|
-
this.pushAccessUnit(VideoSample, track);
|
14973
|
-
this.VideoSample = null;
|
14974
|
-
}
|
14975
|
-
};
|
14976
|
-
_proto.getNALuType = function getNALuType(data, offset) {
|
14977
|
-
return data[offset] & 0x1f;
|
14978
|
-
};
|
14979
|
-
_proto.readSliceType = function readSliceType(data) {
|
14980
|
-
var eg = new ExpGolomb(data);
|
14981
|
-
// skip NALu type
|
14982
|
-
eg.readUByte();
|
14983
|
-
// discard first_mb_in_slice
|
14984
|
-
eg.readUEG();
|
14985
|
-
// return slice_type
|
14986
|
-
return eg.readUEG();
|
14987
14457
|
}
|
14988
14458
|
|
14989
14459
|
/**
|
14990
|
-
*
|
14460
|
+
* Advance the ExpGolomb decoder past a scaling list. The scaling
|
14461
|
+
* list is optionally transmitted as part of a sequence parameter
|
14991
14462
|
* set and is not relevant to transmuxing.
|
14992
14463
|
* @param count the number of entries in this scaling list
|
14993
14464
|
* @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
|
14994
14465
|
*/;
|
14995
|
-
_proto.skipScalingList = function skipScalingList(count
|
14466
|
+
_proto.skipScalingList = function skipScalingList(count) {
|
14996
14467
|
var lastScale = 8;
|
14997
14468
|
var nextScale = 8;
|
14998
14469
|
var deltaScale;
|
14999
14470
|
for (var j = 0; j < count; j++) {
|
15000
14471
|
if (nextScale !== 0) {
|
15001
|
-
deltaScale =
|
14472
|
+
deltaScale = this.readEG();
|
15002
14473
|
nextScale = (lastScale + deltaScale + 256) % 256;
|
15003
14474
|
}
|
15004
14475
|
lastScale = nextScale === 0 ? lastScale : nextScale;
|
@@ -15013,8 +14484,7 @@
|
|
15013
14484
|
* sequence parameter set, including the dimensions of the
|
15014
14485
|
* associated video frames.
|
15015
14486
|
*/;
|
15016
|
-
_proto.readSPS = function readSPS(
|
15017
|
-
var eg = new ExpGolomb(sps);
|
14487
|
+
_proto.readSPS = function readSPS() {
|
15018
14488
|
var frameCropLeftOffset = 0;
|
15019
14489
|
var frameCropRightOffset = 0;
|
15020
14490
|
var frameCropTopOffset = 0;
|
@@ -15022,13 +14492,13 @@
|
|
15022
14492
|
var numRefFramesInPicOrderCntCycle;
|
15023
14493
|
var scalingListCount;
|
15024
14494
|
var i;
|
15025
|
-
var readUByte =
|
15026
|
-
var readBits =
|
15027
|
-
var readUEG =
|
15028
|
-
var readBoolean =
|
15029
|
-
var skipBits =
|
15030
|
-
var skipEG =
|
15031
|
-
var skipUEG =
|
14495
|
+
var readUByte = this.readUByte.bind(this);
|
14496
|
+
var readBits = this.readBits.bind(this);
|
14497
|
+
var readUEG = this.readUEG.bind(this);
|
14498
|
+
var readBoolean = this.readBoolean.bind(this);
|
14499
|
+
var skipBits = this.skipBits.bind(this);
|
14500
|
+
var skipEG = this.skipEG.bind(this);
|
14501
|
+
var skipUEG = this.skipUEG.bind(this);
|
15032
14502
|
var skipScalingList = this.skipScalingList.bind(this);
|
15033
14503
|
readUByte();
|
15034
14504
|
var profileIdc = readUByte(); // profile_idc
|
@@ -15053,9 +14523,9 @@
|
|
15053
14523
|
if (readBoolean()) {
|
15054
14524
|
// seq_scaling_list_present_flag[ i ]
|
15055
14525
|
if (i < 6) {
|
15056
|
-
skipScalingList(16
|
14526
|
+
skipScalingList(16);
|
15057
14527
|
} else {
|
15058
|
-
skipScalingList(64
|
14528
|
+
skipScalingList(64);
|
15059
14529
|
}
|
15060
14530
|
}
|
15061
14531
|
}
|
@@ -15154,11 +14624,270 @@
|
|
15154
14624
|
}
|
15155
14625
|
}
|
15156
14626
|
}
|
15157
|
-
return {
|
15158
|
-
width: Math.ceil((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2),
|
15159
|
-
height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset),
|
15160
|
-
pixelRatio: pixelRatio
|
15161
|
-
};
|
14627
|
+
return {
|
14628
|
+
width: Math.ceil((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2),
|
14629
|
+
height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset),
|
14630
|
+
pixelRatio: pixelRatio
|
14631
|
+
};
|
14632
|
+
};
|
14633
|
+
_proto.readSliceType = function readSliceType() {
|
14634
|
+
// skip NALu type
|
14635
|
+
this.readUByte();
|
14636
|
+
// discard first_mb_in_slice
|
14637
|
+
this.readUEG();
|
14638
|
+
// return slice_type
|
14639
|
+
return this.readUEG();
|
14640
|
+
};
|
14641
|
+
return ExpGolomb;
|
14642
|
+
}();
|
14643
|
+
|
14644
|
+
var AvcVideoParser = /*#__PURE__*/function (_BaseVideoParser) {
|
14645
|
+
_inheritsLoose(AvcVideoParser, _BaseVideoParser);
|
14646
|
+
function AvcVideoParser() {
|
14647
|
+
return _BaseVideoParser.apply(this, arguments) || this;
|
14648
|
+
}
|
14649
|
+
var _proto = AvcVideoParser.prototype;
|
14650
|
+
_proto.parseAVCPES = function parseAVCPES(track, textTrack, pes, last, duration) {
|
14651
|
+
var _this = this;
|
14652
|
+
var units = this.parseAVCNALu(track, pes.data);
|
14653
|
+
var VideoSample = this.VideoSample;
|
14654
|
+
var push;
|
14655
|
+
var spsfound = false;
|
14656
|
+
// free pes.data to save up some memory
|
14657
|
+
pes.data = null;
|
14658
|
+
|
14659
|
+
// if new NAL units found and last sample still there, let's push ...
|
14660
|
+
// this helps parsing streams with missing AUD (only do this if AUD never found)
|
14661
|
+
if (VideoSample && units.length && !track.audFound) {
|
14662
|
+
this.pushAccessUnit(VideoSample, track);
|
14663
|
+
VideoSample = this.VideoSample = this.createVideoSample(false, pes.pts, pes.dts, '');
|
14664
|
+
}
|
14665
|
+
units.forEach(function (unit) {
|
14666
|
+
var _VideoSample2;
|
14667
|
+
switch (unit.type) {
|
14668
|
+
// NDR
|
14669
|
+
case 1:
|
14670
|
+
{
|
14671
|
+
var iskey = false;
|
14672
|
+
push = true;
|
14673
|
+
var data = unit.data;
|
14674
|
+
// only check slice type to detect KF in case SPS found in same packet (any keyframe is preceded by SPS ...)
|
14675
|
+
if (spsfound && data.length > 4) {
|
14676
|
+
// retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR
|
14677
|
+
var sliceType = new ExpGolomb(data).readSliceType();
|
14678
|
+
// 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice
|
14679
|
+
// SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples.
|
14680
|
+
// An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice.
|
14681
|
+
// I slice: A slice that is not an SI slice that is decoded using intra prediction only.
|
14682
|
+
// if (sliceType === 2 || sliceType === 7) {
|
14683
|
+
if (sliceType === 2 || sliceType === 4 || sliceType === 7 || sliceType === 9) {
|
14684
|
+
iskey = true;
|
14685
|
+
}
|
14686
|
+
}
|
14687
|
+
if (iskey) {
|
14688
|
+
var _VideoSample;
|
14689
|
+
// if we have non-keyframe data already, that cannot belong to the same frame as a keyframe, so force a push
|
14690
|
+
if ((_VideoSample = VideoSample) != null && _VideoSample.frame && !VideoSample.key) {
|
14691
|
+
_this.pushAccessUnit(VideoSample, track);
|
14692
|
+
VideoSample = _this.VideoSample = null;
|
14693
|
+
}
|
14694
|
+
}
|
14695
|
+
if (!VideoSample) {
|
14696
|
+
VideoSample = _this.VideoSample = _this.createVideoSample(true, pes.pts, pes.dts, '');
|
14697
|
+
}
|
14698
|
+
VideoSample.frame = true;
|
14699
|
+
VideoSample.key = iskey;
|
14700
|
+
break;
|
14701
|
+
// IDR
|
14702
|
+
}
|
14703
|
+
case 5:
|
14704
|
+
push = true;
|
14705
|
+
// handle PES not starting with AUD
|
14706
|
+
// if we have frame data already, that cannot belong to the same frame, so force a push
|
14707
|
+
if ((_VideoSample2 = VideoSample) != null && _VideoSample2.frame && !VideoSample.key) {
|
14708
|
+
_this.pushAccessUnit(VideoSample, track);
|
14709
|
+
VideoSample = _this.VideoSample = null;
|
14710
|
+
}
|
14711
|
+
if (!VideoSample) {
|
14712
|
+
VideoSample = _this.VideoSample = _this.createVideoSample(true, pes.pts, pes.dts, '');
|
14713
|
+
}
|
14714
|
+
VideoSample.key = true;
|
14715
|
+
VideoSample.frame = true;
|
14716
|
+
break;
|
14717
|
+
// SEI
|
14718
|
+
case 6:
|
14719
|
+
{
|
14720
|
+
push = true;
|
14721
|
+
parseSEIMessageFromNALu(unit.data, 1, pes.pts, textTrack.samples);
|
14722
|
+
break;
|
14723
|
+
// SPS
|
14724
|
+
}
|
14725
|
+
case 7:
|
14726
|
+
{
|
14727
|
+
var _track$pixelRatio, _track$pixelRatio2;
|
14728
|
+
push = true;
|
14729
|
+
spsfound = true;
|
14730
|
+
var sps = unit.data;
|
14731
|
+
var expGolombDecoder = new ExpGolomb(sps);
|
14732
|
+
var config = expGolombDecoder.readSPS();
|
14733
|
+
if (!track.sps || track.width !== config.width || track.height !== config.height || ((_track$pixelRatio = track.pixelRatio) == null ? void 0 : _track$pixelRatio[0]) !== config.pixelRatio[0] || ((_track$pixelRatio2 = track.pixelRatio) == null ? void 0 : _track$pixelRatio2[1]) !== config.pixelRatio[1]) {
|
14734
|
+
track.width = config.width;
|
14735
|
+
track.height = config.height;
|
14736
|
+
track.pixelRatio = config.pixelRatio;
|
14737
|
+
track.sps = [sps];
|
14738
|
+
track.duration = duration;
|
14739
|
+
var codecarray = sps.subarray(1, 4);
|
14740
|
+
var codecstring = 'avc1.';
|
14741
|
+
for (var i = 0; i < 3; i++) {
|
14742
|
+
var h = codecarray[i].toString(16);
|
14743
|
+
if (h.length < 2) {
|
14744
|
+
h = '0' + h;
|
14745
|
+
}
|
14746
|
+
codecstring += h;
|
14747
|
+
}
|
14748
|
+
track.codec = codecstring;
|
14749
|
+
}
|
14750
|
+
break;
|
14751
|
+
}
|
14752
|
+
// PPS
|
14753
|
+
case 8:
|
14754
|
+
push = true;
|
14755
|
+
track.pps = [unit.data];
|
14756
|
+
break;
|
14757
|
+
// AUD
|
14758
|
+
case 9:
|
14759
|
+
push = true;
|
14760
|
+
track.audFound = true;
|
14761
|
+
if (VideoSample) {
|
14762
|
+
_this.pushAccessUnit(VideoSample, track);
|
14763
|
+
}
|
14764
|
+
VideoSample = _this.VideoSample = _this.createVideoSample(false, pes.pts, pes.dts, '');
|
14765
|
+
break;
|
14766
|
+
// Filler Data
|
14767
|
+
case 12:
|
14768
|
+
push = true;
|
14769
|
+
break;
|
14770
|
+
default:
|
14771
|
+
push = false;
|
14772
|
+
if (VideoSample) {
|
14773
|
+
VideoSample.debug += 'unknown NAL ' + unit.type + ' ';
|
14774
|
+
}
|
14775
|
+
break;
|
14776
|
+
}
|
14777
|
+
if (VideoSample && push) {
|
14778
|
+
var _units = VideoSample.units;
|
14779
|
+
_units.push(unit);
|
14780
|
+
}
|
14781
|
+
});
|
14782
|
+
// if last PES packet, push samples
|
14783
|
+
if (last && VideoSample) {
|
14784
|
+
this.pushAccessUnit(VideoSample, track);
|
14785
|
+
this.VideoSample = null;
|
14786
|
+
}
|
14787
|
+
};
|
14788
|
+
_proto.parseAVCNALu = function parseAVCNALu(track, array) {
|
14789
|
+
var len = array.byteLength;
|
14790
|
+
var state = track.naluState || 0;
|
14791
|
+
var lastState = state;
|
14792
|
+
var units = [];
|
14793
|
+
var i = 0;
|
14794
|
+
var value;
|
14795
|
+
var overflow;
|
14796
|
+
var unitType;
|
14797
|
+
var lastUnitStart = -1;
|
14798
|
+
var lastUnitType = 0;
|
14799
|
+
// logger.log('PES:' + Hex.hexDump(array));
|
14800
|
+
|
14801
|
+
if (state === -1) {
|
14802
|
+
// special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet
|
14803
|
+
lastUnitStart = 0;
|
14804
|
+
// NALu type is value read from offset 0
|
14805
|
+
lastUnitType = array[0] & 0x1f;
|
14806
|
+
state = 0;
|
14807
|
+
i = 1;
|
14808
|
+
}
|
14809
|
+
while (i < len) {
|
14810
|
+
value = array[i++];
|
14811
|
+
// optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case
|
14812
|
+
if (!state) {
|
14813
|
+
state = value ? 0 : 1;
|
14814
|
+
continue;
|
14815
|
+
}
|
14816
|
+
if (state === 1) {
|
14817
|
+
state = value ? 0 : 2;
|
14818
|
+
continue;
|
14819
|
+
}
|
14820
|
+
// here we have state either equal to 2 or 3
|
14821
|
+
if (!value) {
|
14822
|
+
state = 3;
|
14823
|
+
} else if (value === 1) {
|
14824
|
+
overflow = i - state - 1;
|
14825
|
+
if (lastUnitStart >= 0) {
|
14826
|
+
var unit = {
|
14827
|
+
data: array.subarray(lastUnitStart, overflow),
|
14828
|
+
type: lastUnitType
|
14829
|
+
};
|
14830
|
+
// logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength);
|
14831
|
+
units.push(unit);
|
14832
|
+
} else {
|
14833
|
+
// lastUnitStart is undefined => this is the first start code found in this PES packet
|
14834
|
+
// first check if start code delimiter is overlapping between 2 PES packets,
|
14835
|
+
// ie it started in last packet (lastState not zero)
|
14836
|
+
// and ended at the beginning of this PES packet (i <= 4 - lastState)
|
14837
|
+
var lastUnit = this.getLastNalUnit(track.samples);
|
14838
|
+
if (lastUnit) {
|
14839
|
+
if (lastState && i <= 4 - lastState) {
|
14840
|
+
// start delimiter overlapping between PES packets
|
14841
|
+
// strip start delimiter bytes from the end of last NAL unit
|
14842
|
+
// check if lastUnit had a state different from zero
|
14843
|
+
if (lastUnit.state) {
|
14844
|
+
// strip last bytes
|
14845
|
+
lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState);
|
14846
|
+
}
|
14847
|
+
}
|
14848
|
+
// If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit.
|
14849
|
+
|
14850
|
+
if (overflow > 0) {
|
14851
|
+
// logger.log('first NALU found with overflow:' + overflow);
|
14852
|
+
lastUnit.data = appendUint8Array(lastUnit.data, array.subarray(0, overflow));
|
14853
|
+
lastUnit.state = 0;
|
14854
|
+
}
|
14855
|
+
}
|
14856
|
+
}
|
14857
|
+
// check if we can read unit type
|
14858
|
+
if (i < len) {
|
14859
|
+
unitType = array[i] & 0x1f;
|
14860
|
+
// logger.log('find NALU @ offset:' + i + ',type:' + unitType);
|
14861
|
+
lastUnitStart = i;
|
14862
|
+
lastUnitType = unitType;
|
14863
|
+
state = 0;
|
14864
|
+
} else {
|
14865
|
+
// not enough byte to read unit type. let's read it on next PES parsing
|
14866
|
+
state = -1;
|
14867
|
+
}
|
14868
|
+
} else {
|
14869
|
+
state = 0;
|
14870
|
+
}
|
14871
|
+
}
|
14872
|
+
if (lastUnitStart >= 0 && state >= 0) {
|
14873
|
+
var _unit = {
|
14874
|
+
data: array.subarray(lastUnitStart, len),
|
14875
|
+
type: lastUnitType,
|
14876
|
+
state: state
|
14877
|
+
};
|
14878
|
+
units.push(_unit);
|
14879
|
+
// logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state);
|
14880
|
+
}
|
14881
|
+
// no NALu found
|
14882
|
+
if (units.length === 0) {
|
14883
|
+
// append pes.data to previous NAL unit
|
14884
|
+
var _lastUnit = this.getLastNalUnit(track.samples);
|
14885
|
+
if (_lastUnit) {
|
14886
|
+
_lastUnit.data = appendUint8Array(_lastUnit.data, array);
|
14887
|
+
}
|
14888
|
+
}
|
14889
|
+
track.naluState = state;
|
14890
|
+
return units;
|
15162
14891
|
};
|
15163
14892
|
return AvcVideoParser;
|
15164
14893
|
}(BaseVideoParser);
|
@@ -15178,7 +14907,7 @@
|
|
15178
14907
|
}
|
15179
14908
|
var _proto = SampleAesDecrypter.prototype;
|
15180
14909
|
_proto.decryptBuffer = function decryptBuffer(encryptedData) {
|
15181
|
-
return this.decrypter.decrypt(encryptedData, this.keyData.key.buffer, this.keyData.iv.buffer
|
14910
|
+
return this.decrypter.decrypt(encryptedData, this.keyData.key.buffer, this.keyData.iv.buffer);
|
15182
14911
|
}
|
15183
14912
|
|
15184
14913
|
// AAC - encrypt all full 16 bytes blocks starting from offset 16
|
@@ -15297,7 +15026,7 @@
|
|
15297
15026
|
this.observer = observer;
|
15298
15027
|
this.config = config;
|
15299
15028
|
this.typeSupported = typeSupported;
|
15300
|
-
this.videoParser =
|
15029
|
+
this.videoParser = new AvcVideoParser();
|
15301
15030
|
}
|
15302
15031
|
TSDemuxer.probe = function probe(data) {
|
15303
15032
|
var syncOffset = TSDemuxer.syncOffset(data);
|
@@ -15467,16 +15196,7 @@
|
|
15467
15196
|
case videoPid:
|
15468
15197
|
if (stt) {
|
15469
15198
|
if (videoData && (pes = parsePES(videoData))) {
|
15470
|
-
|
15471
|
-
switch (videoTrack.segmentCodec) {
|
15472
|
-
case 'avc':
|
15473
|
-
this.videoParser = new AvcVideoParser();
|
15474
|
-
break;
|
15475
|
-
}
|
15476
|
-
}
|
15477
|
-
if (this.videoParser !== null) {
|
15478
|
-
this.videoParser.parsePES(videoTrack, textTrack, pes, false, this._duration);
|
15479
|
-
}
|
15199
|
+
this.videoParser.parseAVCPES(videoTrack, textTrack, pes, false, this._duration);
|
15480
15200
|
}
|
15481
15201
|
videoData = {
|
15482
15202
|
data: [],
|
@@ -15634,17 +15354,8 @@
|
|
15634
15354
|
// try to parse last PES packets
|
15635
15355
|
var pes;
|
15636
15356
|
if (videoData && (pes = parsePES(videoData))) {
|
15637
|
-
|
15638
|
-
|
15639
|
-
case 'avc':
|
15640
|
-
this.videoParser = new AvcVideoParser();
|
15641
|
-
break;
|
15642
|
-
}
|
15643
|
-
}
|
15644
|
-
if (this.videoParser !== null) {
|
15645
|
-
this.videoParser.parsePES(videoTrack, textTrack, pes, true, this._duration);
|
15646
|
-
videoTrack.pesData = null;
|
15647
|
-
}
|
15357
|
+
this.videoParser.parseAVCPES(videoTrack, textTrack, pes, true, this._duration);
|
15358
|
+
videoTrack.pesData = null;
|
15648
15359
|
} else {
|
15649
15360
|
// either avcData null or PES truncated, keep it for next frag parsing
|
15650
15361
|
videoTrack.pesData = videoData;
|
@@ -15946,10 +15657,7 @@
|
|
15946
15657
|
logger.warn('Unsupported EC-3 in M2TS found');
|
15947
15658
|
break;
|
15948
15659
|
case 0x24:
|
15949
|
-
|
15950
|
-
{
|
15951
|
-
logger.warn('Unsupported HEVC in M2TS found');
|
15952
|
-
}
|
15660
|
+
logger.warn('Unsupported HEVC in M2TS found');
|
15953
15661
|
break;
|
15954
15662
|
}
|
15955
15663
|
// move to the next table entry
|
@@ -16177,8 +15885,6 @@
|
|
16177
15885
|
avc1: [],
|
16178
15886
|
// codingname
|
16179
15887
|
avcC: [],
|
16180
|
-
hvc1: [],
|
16181
|
-
hvcC: [],
|
16182
15888
|
btrt: [],
|
16183
15889
|
dinf: [],
|
16184
15890
|
dref: [],
|
@@ -16606,10 +16312,8 @@
|
|
16606
16312
|
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.ac3(track));
|
16607
16313
|
}
|
16608
16314
|
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track));
|
16609
|
-
} else if (track.segmentCodec === 'avc') {
|
16610
|
-
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track));
|
16611
16315
|
} else {
|
16612
|
-
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.
|
16316
|
+
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track));
|
16613
16317
|
}
|
16614
16318
|
};
|
16615
16319
|
MP4.tkhd = function tkhd(track) {
|
@@ -16747,84 +16451,6 @@
|
|
16747
16451
|
var result = appendUint8Array(MP4.FTYP, movie);
|
16748
16452
|
return result;
|
16749
16453
|
};
|
16750
|
-
MP4.hvc1 = function hvc1(track) {
|
16751
|
-
var ps = track.params;
|
16752
|
-
var units = [track.vps, track.sps, track.pps];
|
16753
|
-
var NALuLengthSize = 4;
|
16754
|
-
var config = new Uint8Array([0x01, ps.general_profile_space << 6 | (ps.general_tier_flag ? 32 : 0) | ps.general_profile_idc, ps.general_profile_compatibility_flags[0], ps.general_profile_compatibility_flags[1], ps.general_profile_compatibility_flags[2], ps.general_profile_compatibility_flags[3], ps.general_constraint_indicator_flags[0], ps.general_constraint_indicator_flags[1], ps.general_constraint_indicator_flags[2], ps.general_constraint_indicator_flags[3], ps.general_constraint_indicator_flags[4], ps.general_constraint_indicator_flags[5], ps.general_level_idc, 240 | ps.min_spatial_segmentation_idc >> 8, 255 & ps.min_spatial_segmentation_idc, 252 | ps.parallelismType, 252 | ps.chroma_format_idc, 248 | ps.bit_depth_luma_minus8, 248 | ps.bit_depth_chroma_minus8, 0x00, parseInt(ps.frame_rate.fps), NALuLengthSize - 1 | ps.temporal_id_nested << 2 | ps.num_temporal_layers << 3 | (ps.frame_rate.fixed ? 64 : 0), units.length]);
|
16755
|
-
|
16756
|
-
// compute hvcC size in bytes
|
16757
|
-
var length = config.length;
|
16758
|
-
for (var i = 0; i < units.length; i += 1) {
|
16759
|
-
length += 3;
|
16760
|
-
for (var j = 0; j < units[i].length; j += 1) {
|
16761
|
-
length += 2 + units[i][j].length;
|
16762
|
-
}
|
16763
|
-
}
|
16764
|
-
var hvcC = new Uint8Array(length);
|
16765
|
-
hvcC.set(config, 0);
|
16766
|
-
length = config.length;
|
16767
|
-
// append parameter set units: one vps, one or more sps and pps
|
16768
|
-
var iMax = units.length - 1;
|
16769
|
-
for (var _i = 0; _i < units.length; _i += 1) {
|
16770
|
-
hvcC.set(new Uint8Array([32 + _i | (_i === iMax ? 128 : 0), 0x00, units[_i].length]), length);
|
16771
|
-
length += 3;
|
16772
|
-
for (var _j = 0; _j < units[_i].length; _j += 1) {
|
16773
|
-
hvcC.set(new Uint8Array([units[_i][_j].length >> 8, units[_i][_j].length & 255]), length);
|
16774
|
-
length += 2;
|
16775
|
-
hvcC.set(units[_i][_j], length);
|
16776
|
-
length += units[_i][_j].length;
|
16777
|
-
}
|
16778
|
-
}
|
16779
|
-
var hvcc = MP4.box(MP4.types.hvcC, hvcC);
|
16780
|
-
var width = track.width;
|
16781
|
-
var height = track.height;
|
16782
|
-
var hSpacing = track.pixelRatio[0];
|
16783
|
-
var vSpacing = track.pixelRatio[1];
|
16784
|
-
return MP4.box(MP4.types.hvc1, new Uint8Array([0x00, 0x00, 0x00,
|
16785
|
-
// reserved
|
16786
|
-
0x00, 0x00, 0x00,
|
16787
|
-
// reserved
|
16788
|
-
0x00, 0x01,
|
16789
|
-
// data_reference_index
|
16790
|
-
0x00, 0x00,
|
16791
|
-
// pre_defined
|
16792
|
-
0x00, 0x00,
|
16793
|
-
// reserved
|
16794
|
-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
16795
|
-
// pre_defined
|
16796
|
-
width >> 8 & 0xff, width & 0xff,
|
16797
|
-
// width
|
16798
|
-
height >> 8 & 0xff, height & 0xff,
|
16799
|
-
// height
|
16800
|
-
0x00, 0x48, 0x00, 0x00,
|
16801
|
-
// horizresolution
|
16802
|
-
0x00, 0x48, 0x00, 0x00,
|
16803
|
-
// vertresolution
|
16804
|
-
0x00, 0x00, 0x00, 0x00,
|
16805
|
-
// reserved
|
16806
|
-
0x00, 0x01,
|
16807
|
-
// frame_count
|
16808
|
-
0x12, 0x64, 0x61, 0x69, 0x6c,
|
16809
|
-
// dailymotion/hls.js
|
16810
|
-
0x79, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x68, 0x6c, 0x73, 0x2e, 0x6a, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
16811
|
-
// compressorname
|
16812
|
-
0x00, 0x18,
|
16813
|
-
// depth = 24
|
16814
|
-
0x11, 0x11]),
|
16815
|
-
// pre_defined = -1
|
16816
|
-
hvcc, MP4.box(MP4.types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80,
|
16817
|
-
// bufferSizeDB
|
16818
|
-
0x00, 0x2d, 0xc6, 0xc0,
|
16819
|
-
// maxBitrate
|
16820
|
-
0x00, 0x2d, 0xc6, 0xc0])),
|
16821
|
-
// avgBitrate
|
16822
|
-
MP4.box(MP4.types.pasp, new Uint8Array([hSpacing >> 24,
|
16823
|
-
// hSpacing
|
16824
|
-
hSpacing >> 16 & 0xff, hSpacing >> 8 & 0xff, hSpacing & 0xff, vSpacing >> 24,
|
16825
|
-
// vSpacing
|
16826
|
-
vSpacing >> 16 & 0xff, vSpacing >> 8 & 0xff, vSpacing & 0xff])));
|
16827
|
-
};
|
16828
16454
|
return MP4;
|
16829
16455
|
}();
|
16830
16456
|
MP4.types = void 0;
|
@@ -17211,9 +16837,9 @@
|
|
17211
16837
|
var foundOverlap = delta < -1;
|
17212
16838
|
if (foundHole || foundOverlap) {
|
17213
16839
|
if (foundHole) {
|
17214
|
-
logger.warn(
|
16840
|
+
logger.warn("AVC: " + toMsFromMpegTsClock(delta, true) + " ms (" + delta + "dts) hole between fragments detected at " + timeOffset.toFixed(3));
|
17215
16841
|
} else {
|
17216
|
-
logger.warn(
|
16842
|
+
logger.warn("AVC: " + toMsFromMpegTsClock(-delta, true) + " ms (" + delta + "dts) overlapping between fragments detected at " + timeOffset.toFixed(3));
|
17217
16843
|
}
|
17218
16844
|
if (!foundOverlap || nextAvcDts >= inputSamples[0].pts || chromeVersion) {
|
17219
16845
|
firstDTS = nextAvcDts;
|
@@ -17222,24 +16848,12 @@
|
|
17222
16848
|
inputSamples[0].dts = firstDTS;
|
17223
16849
|
inputSamples[0].pts = firstPTS;
|
17224
16850
|
} else {
|
17225
|
-
var isPTSOrderRetained = true;
|
17226
16851
|
for (var _i = 0; _i < inputSamples.length; _i++) {
|
17227
|
-
if (inputSamples[_i].dts > firstPTS
|
16852
|
+
if (inputSamples[_i].dts > firstPTS) {
|
17228
16853
|
break;
|
17229
16854
|
}
|
17230
|
-
var prevPTS = inputSamples[_i].pts;
|
17231
16855
|
inputSamples[_i].dts -= delta;
|
17232
16856
|
inputSamples[_i].pts -= delta;
|
17233
|
-
|
17234
|
-
// check to see if this sample's PTS order has changed
|
17235
|
-
// relative to the next one
|
17236
|
-
if (_i < inputSamples.length - 1) {
|
17237
|
-
var nextSamplePTS = inputSamples[_i + 1].pts;
|
17238
|
-
var currentSamplePTS = inputSamples[_i].pts;
|
17239
|
-
var currentOrder = nextSamplePTS <= currentSamplePTS;
|
17240
|
-
var prevOrder = nextSamplePTS <= prevPTS;
|
17241
|
-
isPTSOrderRetained = currentOrder == prevOrder;
|
17242
|
-
}
|
17243
16857
|
}
|
17244
16858
|
}
|
17245
16859
|
logger.log("Video: Initial PTS/DTS adjusted: " + toMsFromMpegTsClock(firstPTS, true) + "/" + toMsFromMpegTsClock(firstDTS, true) + ", delta: " + toMsFromMpegTsClock(delta, true) + " ms");
|
@@ -17387,7 +17001,7 @@
|
|
17387
17001
|
}
|
17388
17002
|
}
|
17389
17003
|
}
|
17390
|
-
// next AVC
|
17004
|
+
// next AVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)
|
17391
17005
|
mp4SampleDuration = stretchedLastFrame || !mp4SampleDuration ? averageSampleDuration : mp4SampleDuration;
|
17392
17006
|
this.nextAvcDts = nextAvcDts = lastDTS + mp4SampleDuration;
|
17393
17007
|
this.videoSampleDuration = mp4SampleDuration;
|
@@ -17522,7 +17136,7 @@
|
|
17522
17136
|
logger.warn("[mp4-remuxer]: Injecting " + missing + " audio frame @ " + (nextPts / inputTimeScale).toFixed(3) + "s due to " + Math.round(1000 * delta / inputTimeScale) + " ms gap.");
|
17523
17137
|
for (var j = 0; j < missing; j++) {
|
17524
17138
|
var newStamp = Math.max(nextPts, 0);
|
17525
|
-
var fillFrame = AAC.getSilentFrame(track.
|
17139
|
+
var fillFrame = AAC.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);
|
17526
17140
|
if (!fillFrame) {
|
17527
17141
|
logger.log('[mp4-remuxer]: Unable to get silent frame for given audio codec; duplicating last frame instead.');
|
17528
17142
|
fillFrame = sample.unit.subarray();
|
@@ -17650,7 +17264,7 @@
|
|
17650
17264
|
// samples count of this segment's duration
|
17651
17265
|
var nbSamples = Math.ceil((endDTS - startDTS) / frameDuration);
|
17652
17266
|
// silent frame
|
17653
|
-
var silentFrame = AAC.getSilentFrame(track.
|
17267
|
+
var silentFrame = AAC.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);
|
17654
17268
|
logger.warn('[mp4-remuxer]: remux empty Audio');
|
17655
17269
|
// Can't remux if we can't generate a silent frame...
|
17656
17270
|
if (!silentFrame) {
|
@@ -18037,15 +17651,13 @@
|
|
18037
17651
|
duration = transmuxConfig.duration,
|
18038
17652
|
initSegmentData = transmuxConfig.initSegmentData;
|
18039
17653
|
var keyData = getEncryptionType(uintData, decryptdata);
|
18040
|
-
if (keyData &&
|
17654
|
+
if (keyData && keyData.method === 'AES-128') {
|
18041
17655
|
var decrypter = this.getDecrypter();
|
18042
|
-
var aesMode = getAesModeFromFullSegmentMethod(keyData.method);
|
18043
|
-
|
18044
17656
|
// Software decryption is synchronous; webCrypto is not
|
18045
17657
|
if (decrypter.isSync()) {
|
18046
17658
|
// Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached
|
18047
17659
|
// data is handled in the flush() call
|
18048
|
-
var decryptedData = decrypter.softwareDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer
|
17660
|
+
var decryptedData = decrypter.softwareDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer);
|
18049
17661
|
// For Low-Latency HLS Parts, decrypt in place, since part parsing is expected on push progress
|
18050
17662
|
var loadingParts = chunkMeta.part > -1;
|
18051
17663
|
if (loadingParts) {
|
@@ -18057,7 +17669,7 @@
|
|
18057
17669
|
}
|
18058
17670
|
uintData = new Uint8Array(decryptedData);
|
18059
17671
|
} else {
|
18060
|
-
this.decryptionPromise = decrypter.webCryptoDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer
|
17672
|
+
this.decryptionPromise = decrypter.webCryptoDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer).then(function (decryptedData) {
|
18061
17673
|
// Calling push here is important; if flush() is called while this is still resolving, this ensures that
|
18062
17674
|
// the decrypted data has been transmuxed
|
18063
17675
|
var result = _this.push(decryptedData, null, chunkMeta);
|
@@ -18678,7 +18290,7 @@
|
|
18678
18290
|
observer.on(Events.ERROR, forwardMessage);
|
18679
18291
|
|
18680
18292
|
// forward logger events to main thread
|
18681
|
-
var forwardWorkerLogs = function forwardWorkerLogs(
|
18293
|
+
var forwardWorkerLogs = function forwardWorkerLogs() {
|
18682
18294
|
var _loop = function _loop(logFn) {
|
18683
18295
|
var func = function func(message) {
|
18684
18296
|
forwardMessage('workerLog', {
|
@@ -18699,8 +18311,8 @@
|
|
18699
18311
|
{
|
18700
18312
|
var config = JSON.parse(data.config);
|
18701
18313
|
self.transmuxer = new Transmuxer(observer, data.typeSupported, config, data.vendor, data.id);
|
18702
|
-
|
18703
|
-
forwardWorkerLogs(
|
18314
|
+
enableLogs(config.debug, data.id);
|
18315
|
+
forwardWorkerLogs();
|
18704
18316
|
forwardMessage('init', null);
|
18705
18317
|
break;
|
18706
18318
|
}
|
@@ -18874,7 +18486,16 @@
|
|
18874
18486
|
this.observer = new EventEmitter();
|
18875
18487
|
this.observer.on(Events.FRAG_DECRYPTED, forwardMessage);
|
18876
18488
|
this.observer.on(Events.ERROR, forwardMessage);
|
18877
|
-
var
|
18489
|
+
var MediaSource = getMediaSource(config.preferManagedMediaSource) || {
|
18490
|
+
isTypeSupported: function isTypeSupported() {
|
18491
|
+
return false;
|
18492
|
+
}
|
18493
|
+
};
|
18494
|
+
var m2tsTypeSupported = {
|
18495
|
+
mpeg: MediaSource.isTypeSupported('audio/mpeg'),
|
18496
|
+
mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'),
|
18497
|
+
ac3: false
|
18498
|
+
};
|
18878
18499
|
|
18879
18500
|
// navigator.vendor is not always available in Web Worker
|
18880
18501
|
// refer to https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/navigator
|
@@ -19131,26 +18752,21 @@
|
|
19131
18752
|
var MAX_START_GAP_JUMP = 2.0;
|
19132
18753
|
var SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1;
|
19133
18754
|
var SKIP_BUFFER_RANGE_START = 0.05;
|
19134
|
-
var GapController = /*#__PURE__*/function (
|
19135
|
-
_inheritsLoose(GapController, _Logger);
|
18755
|
+
var GapController = /*#__PURE__*/function () {
|
19136
18756
|
function GapController(config, media, fragmentTracker, hls) {
|
19137
|
-
|
19138
|
-
|
19139
|
-
|
19140
|
-
|
19141
|
-
|
19142
|
-
|
19143
|
-
|
19144
|
-
|
19145
|
-
|
19146
|
-
|
19147
|
-
|
19148
|
-
|
19149
|
-
|
19150
|
-
_this.media = media;
|
19151
|
-
_this.fragmentTracker = fragmentTracker;
|
19152
|
-
_this.hls = hls;
|
19153
|
-
return _this;
|
18757
|
+
this.config = void 0;
|
18758
|
+
this.media = null;
|
18759
|
+
this.fragmentTracker = void 0;
|
18760
|
+
this.hls = void 0;
|
18761
|
+
this.nudgeRetry = 0;
|
18762
|
+
this.stallReported = false;
|
18763
|
+
this.stalled = null;
|
18764
|
+
this.moved = false;
|
18765
|
+
this.seeking = false;
|
18766
|
+
this.config = config;
|
18767
|
+
this.media = media;
|
18768
|
+
this.fragmentTracker = fragmentTracker;
|
18769
|
+
this.hls = hls;
|
19154
18770
|
}
|
19155
18771
|
var _proto = GapController.prototype;
|
19156
18772
|
_proto.destroy = function destroy() {
|
@@ -19165,7 +18781,7 @@
|
|
19165
18781
|
*
|
19166
18782
|
* @param lastCurrentTime - Previously read playhead position
|
19167
18783
|
*/;
|
19168
|
-
_proto.poll = function poll(lastCurrentTime, activeFrag
|
18784
|
+
_proto.poll = function poll(lastCurrentTime, activeFrag) {
|
19169
18785
|
var config = this.config,
|
19170
18786
|
media = this.media,
|
19171
18787
|
stalled = this.stalled;
|
@@ -19180,7 +18796,6 @@
|
|
19180
18796
|
|
19181
18797
|
// The playhead is moving, no-op
|
19182
18798
|
if (currentTime !== lastCurrentTime) {
|
19183
|
-
this.ended = 0;
|
19184
18799
|
this.moved = true;
|
19185
18800
|
if (!seeking) {
|
19186
18801
|
this.nudgeRetry = 0;
|
@@ -19189,7 +18804,7 @@
|
|
19189
18804
|
// The playhead is now moving, but was previously stalled
|
19190
18805
|
if (this.stallReported) {
|
19191
18806
|
var _stalledDuration = self.performance.now() - stalled;
|
19192
|
-
|
18807
|
+
logger.warn("playback not stuck anymore @" + currentTime + ", after " + Math.round(_stalledDuration) + "ms");
|
19193
18808
|
this.stallReported = false;
|
19194
18809
|
}
|
19195
18810
|
this.stalled = null;
|
@@ -19225,6 +18840,7 @@
|
|
19225
18840
|
// Skip start gaps if we haven't played, but the last poll detected the start of a stall
|
19226
18841
|
// The addition poll gives the browser a chance to jump the gap for us
|
19227
18842
|
if (!this.moved && this.stalled !== null) {
|
18843
|
+
var _level$details;
|
19228
18844
|
// There is no playable buffer (seeked, waiting for buffer)
|
19229
18845
|
var isBuffered = bufferInfo.len > 0;
|
19230
18846
|
if (!isBuffered && !nextStart) {
|
@@ -19236,8 +18852,9 @@
|
|
19236
18852
|
// When joining a live stream with audio tracks, account for live playlist window sliding by allowing
|
19237
18853
|
// a larger jump over start gaps caused by the audio-stream-controller buffering a start fragment
|
19238
18854
|
// that begins over 1 target duration after the video start position.
|
19239
|
-
var
|
19240
|
-
var
|
18855
|
+
var level = this.hls.levels ? this.hls.levels[this.hls.currentLevel] : null;
|
18856
|
+
var isLive = level == null ? void 0 : (_level$details = level.details) == null ? void 0 : _level$details.live;
|
18857
|
+
var maxStartGapJump = isLive ? level.details.targetduration * 2 : MAX_START_GAP_JUMP;
|
19241
18858
|
var partialOrGap = this.fragmentTracker.getPartialFragment(currentTime);
|
19242
18859
|
if (startJump > 0 && (startJump <= maxStartGapJump || partialOrGap)) {
|
19243
18860
|
if (!media.paused) {
|
@@ -19255,17 +18872,6 @@
|
|
19255
18872
|
}
|
19256
18873
|
var stalledDuration = tnow - stalled;
|
19257
18874
|
if (!seeking && stalledDuration >= STALL_MINIMUM_DURATION_MS) {
|
19258
|
-
// Dispatch MEDIA_ENDED when media.ended/ended event is not signalled at end of stream
|
19259
|
-
if (state === State.ENDED && !(levelDetails && levelDetails.live) && Math.abs(currentTime - ((levelDetails == null ? void 0 : levelDetails.edge) || 0)) < 1) {
|
19260
|
-
if (stalledDuration < 1000 || this.ended) {
|
19261
|
-
return;
|
19262
|
-
}
|
19263
|
-
this.ended = currentTime;
|
19264
|
-
this.hls.trigger(Events.MEDIA_ENDED, {
|
19265
|
-
stalled: true
|
19266
|
-
});
|
19267
|
-
return;
|
19268
|
-
}
|
19269
18875
|
// Report stalling after trying to fix
|
19270
18876
|
this._reportStall(bufferInfo);
|
19271
18877
|
if (!this.media) {
|
@@ -19307,7 +18913,7 @@
|
|
19307
18913
|
// needs to cross some sort of threshold covering all source-buffers content
|
19308
18914
|
// to start playing properly.
|
19309
18915
|
if ((bufferInfo.len > config.maxBufferHole || bufferInfo.nextStart && bufferInfo.nextStart - currentTime < config.maxBufferHole) && stalledDurationMs > config.highBufferWatchdogPeriod * 1000) {
|
19310
|
-
|
18916
|
+
logger.warn('Trying to nudge playhead over buffer-hole');
|
19311
18917
|
// Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
|
19312
18918
|
// We only try to jump the hole if it's under the configured size
|
19313
18919
|
// Reset stalled so to rearm watchdog timer
|
@@ -19329,7 +18935,7 @@
|
|
19329
18935
|
// Report stalled error once
|
19330
18936
|
this.stallReported = true;
|
19331
18937
|
var error = new Error("Playback stalling at @" + media.currentTime + " due to low buffer (" + JSON.stringify(bufferInfo) + ")");
|
19332
|
-
|
18938
|
+
logger.warn(error.message);
|
19333
18939
|
hls.trigger(Events.ERROR, {
|
19334
18940
|
type: ErrorTypes.MEDIA_ERROR,
|
19335
18941
|
details: ErrorDetails.BUFFER_STALLED_ERROR,
|
@@ -19393,7 +18999,7 @@
|
|
19393
18999
|
}
|
19394
19000
|
}
|
19395
19001
|
var targetTime = Math.max(startTime + SKIP_BUFFER_RANGE_START, currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS);
|
19396
|
-
|
19002
|
+
logger.warn("skipping hole, adjusting currentTime from " + currentTime + " to " + targetTime);
|
19397
19003
|
this.moved = true;
|
19398
19004
|
this.stalled = null;
|
19399
19005
|
media.currentTime = targetTime;
|
@@ -19432,7 +19038,7 @@
|
|
19432
19038
|
var targetTime = currentTime + (nudgeRetry + 1) * config.nudgeOffset;
|
19433
19039
|
// playback stalled in buffered area ... let's nudge currentTime to try to overcome this
|
19434
19040
|
var error = new Error("Nudging 'currentTime' from " + currentTime + " to " + targetTime);
|
19435
|
-
|
19041
|
+
logger.warn(error.message);
|
19436
19042
|
media.currentTime = targetTime;
|
19437
19043
|
hls.trigger(Events.ERROR, {
|
19438
19044
|
type: ErrorTypes.MEDIA_ERROR,
|
@@ -19442,7 +19048,7 @@
|
|
19442
19048
|
});
|
19443
19049
|
} else {
|
19444
19050
|
var _error = new Error("Playhead still not moving while enough data buffered @" + currentTime + " after " + config.nudgeMaxRetry + " nudges");
|
19445
|
-
|
19051
|
+
logger.error(_error.message);
|
19446
19052
|
hls.trigger(Events.ERROR, {
|
19447
19053
|
type: ErrorTypes.MEDIA_ERROR,
|
19448
19054
|
details: ErrorDetails.BUFFER_STALLED_ERROR,
|
@@ -19452,14 +19058,14 @@
|
|
19452
19058
|
}
|
19453
19059
|
};
|
19454
19060
|
return GapController;
|
19455
|
-
}(
|
19061
|
+
}();
|
19456
19062
|
|
19457
19063
|
var TICK_INTERVAL = 100; // how often to tick in ms
|
19458
19064
|
var StreamController = /*#__PURE__*/function (_BaseStreamController) {
|
19459
19065
|
_inheritsLoose(StreamController, _BaseStreamController);
|
19460
19066
|
function StreamController(hls, fragmentTracker, keyLoader) {
|
19461
19067
|
var _this;
|
19462
|
-
_this = _BaseStreamController.call(this, hls, fragmentTracker, keyLoader, 'stream-controller', PlaylistLevelType.MAIN) || this;
|
19068
|
+
_this = _BaseStreamController.call(this, hls, fragmentTracker, keyLoader, '[stream-controller]', PlaylistLevelType.MAIN) || this;
|
19463
19069
|
_this.audioCodecSwap = false;
|
19464
19070
|
_this.gapController = null;
|
19465
19071
|
_this.level = -1;
|
@@ -19467,43 +19073,27 @@
|
|
19467
19073
|
_this.altAudio = false;
|
19468
19074
|
_this.audioOnly = false;
|
19469
19075
|
_this.fragPlaying = null;
|
19076
|
+
_this.onvplaying = null;
|
19077
|
+
_this.onvseeked = null;
|
19470
19078
|
_this.fragLastKbps = 0;
|
19471
19079
|
_this.couldBacktrack = false;
|
19472
19080
|
_this.backtrackFragment = null;
|
19473
19081
|
_this.audioCodecSwitch = false;
|
19474
19082
|
_this.videoBuffer = null;
|
19475
|
-
_this.
|
19476
|
-
// tick to speed up FRAG_CHANGED triggering
|
19477
|
-
_this.tick();
|
19478
|
-
};
|
19479
|
-
_this.onMediaSeeked = function () {
|
19480
|
-
var media = _this.media;
|
19481
|
-
var currentTime = media ? media.currentTime : null;
|
19482
|
-
if (isFiniteNumber(currentTime)) {
|
19483
|
-
_this.log("Media seeked to " + currentTime.toFixed(3));
|
19484
|
-
}
|
19485
|
-
|
19486
|
-
// If seeked was issued before buffer was appended do not tick immediately
|
19487
|
-
var bufferInfo = _this.getMainFwdBufferInfo();
|
19488
|
-
if (bufferInfo === null || bufferInfo.len === 0) {
|
19489
|
-
_this.warn("Main forward buffer length on \"seeked\" event " + (bufferInfo ? bufferInfo.len : 'empty') + ")");
|
19490
|
-
return;
|
19491
|
-
}
|
19492
|
-
|
19493
|
-
// tick to speed up FRAG_CHANGED triggering
|
19494
|
-
_this.tick();
|
19495
|
-
};
|
19496
|
-
_this.registerListeners();
|
19083
|
+
_this._registerListeners();
|
19497
19084
|
return _this;
|
19498
19085
|
}
|
19499
19086
|
var _proto = StreamController.prototype;
|
19500
|
-
_proto.
|
19501
|
-
_BaseStreamController.prototype.registerListeners.call(this);
|
19087
|
+
_proto._registerListeners = function _registerListeners() {
|
19502
19088
|
var hls = this.hls;
|
19089
|
+
hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
|
19090
|
+
hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
|
19091
|
+
hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
|
19503
19092
|
hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this);
|
19504
19093
|
hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this);
|
19505
19094
|
hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);
|
19506
19095
|
hls.on(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this);
|
19096
|
+
hls.on(Events.ERROR, this.onError, this);
|
19507
19097
|
hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
|
19508
19098
|
hls.on(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
|
19509
19099
|
hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this);
|
@@ -19511,12 +19101,15 @@
|
|
19511
19101
|
hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this);
|
19512
19102
|
hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);
|
19513
19103
|
};
|
19514
|
-
_proto.
|
19515
|
-
_BaseStreamController.prototype.unregisterListeners.call(this);
|
19104
|
+
_proto._unregisterListeners = function _unregisterListeners() {
|
19516
19105
|
var hls = this.hls;
|
19106
|
+
hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
|
19107
|
+
hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
|
19108
|
+
hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
|
19517
19109
|
hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this);
|
19518
19110
|
hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);
|
19519
19111
|
hls.off(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this);
|
19112
|
+
hls.off(Events.ERROR, this.onError, this);
|
19520
19113
|
hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
|
19521
19114
|
hls.off(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
|
19522
19115
|
hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this);
|
@@ -19525,9 +19118,7 @@
|
|
19525
19118
|
hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this);
|
19526
19119
|
};
|
19527
19120
|
_proto.onHandlerDestroying = function onHandlerDestroying() {
|
19528
|
-
|
19529
|
-
this.onMediaPlaying = this.onMediaSeeked = null;
|
19530
|
-
this.unregisterListeners();
|
19121
|
+
this._unregisterListeners();
|
19531
19122
|
_BaseStreamController.prototype.onHandlerDestroying.call(this);
|
19532
19123
|
};
|
19533
19124
|
_proto.startLoad = function startLoad(startPosition) {
|
@@ -19619,9 +19210,6 @@
|
|
19619
19210
|
this.checkFragmentChanged();
|
19620
19211
|
};
|
19621
19212
|
_proto.doTickIdle = function doTickIdle() {
|
19622
|
-
if (!this.buffering) {
|
19623
|
-
return;
|
19624
|
-
}
|
19625
19213
|
var hls = this.hls,
|
19626
19214
|
levelLastLoaded = this.levelLastLoaded,
|
19627
19215
|
levels = this.levels,
|
@@ -19845,15 +19433,18 @@
|
|
19845
19433
|
_proto.onMediaAttached = function onMediaAttached(event, data) {
|
19846
19434
|
_BaseStreamController.prototype.onMediaAttached.call(this, event, data);
|
19847
19435
|
var media = data.media;
|
19848
|
-
|
19849
|
-
|
19436
|
+
this.onvplaying = this.onMediaPlaying.bind(this);
|
19437
|
+
this.onvseeked = this.onMediaSeeked.bind(this);
|
19438
|
+
media.addEventListener('playing', this.onvplaying);
|
19439
|
+
media.addEventListener('seeked', this.onvseeked);
|
19850
19440
|
this.gapController = new GapController(this.config, media, this.fragmentTracker, this.hls);
|
19851
19441
|
};
|
19852
19442
|
_proto.onMediaDetaching = function onMediaDetaching() {
|
19853
19443
|
var media = this.media;
|
19854
|
-
if (media) {
|
19855
|
-
media.removeEventListener('playing', this.
|
19856
|
-
media.removeEventListener('seeked', this.
|
19444
|
+
if (media && this.onvplaying && this.onvseeked) {
|
19445
|
+
media.removeEventListener('playing', this.onvplaying);
|
19446
|
+
media.removeEventListener('seeked', this.onvseeked);
|
19447
|
+
this.onvplaying = this.onvseeked = null;
|
19857
19448
|
this.videoBuffer = null;
|
19858
19449
|
}
|
19859
19450
|
this.fragPlaying = null;
|
@@ -19863,6 +19454,27 @@
|
|
19863
19454
|
}
|
19864
19455
|
_BaseStreamController.prototype.onMediaDetaching.call(this);
|
19865
19456
|
};
|
19457
|
+
_proto.onMediaPlaying = function onMediaPlaying() {
|
19458
|
+
// tick to speed up FRAG_CHANGED triggering
|
19459
|
+
this.tick();
|
19460
|
+
};
|
19461
|
+
_proto.onMediaSeeked = function onMediaSeeked() {
|
19462
|
+
var media = this.media;
|
19463
|
+
var currentTime = media ? media.currentTime : null;
|
19464
|
+
if (isFiniteNumber(currentTime)) {
|
19465
|
+
this.log("Media seeked to " + currentTime.toFixed(3));
|
19466
|
+
}
|
19467
|
+
|
19468
|
+
// If seeked was issued before buffer was appended do not tick immediately
|
19469
|
+
var bufferInfo = this.getMainFwdBufferInfo();
|
19470
|
+
if (bufferInfo === null || bufferInfo.len === 0) {
|
19471
|
+
this.warn("Main forward buffer length on \"seeked\" event " + (bufferInfo ? bufferInfo.len : 'empty') + ")");
|
19472
|
+
return;
|
19473
|
+
}
|
19474
|
+
|
19475
|
+
// tick to speed up FRAG_CHANGED triggering
|
19476
|
+
this.tick();
|
19477
|
+
};
|
19866
19478
|
_proto.onManifestLoading = function onManifestLoading() {
|
19867
19479
|
// reset buffer on manifest loading
|
19868
19480
|
this.log('Trigger BUFFER_RESET');
|
@@ -20143,10 +19755,8 @@
|
|
20143
19755
|
}
|
20144
19756
|
if (this.loadedmetadata || !BufferHelper.getBuffered(media).length) {
|
20145
19757
|
// Resolve gaps using the main buffer, whose ranges are the intersections of the A/V sourcebuffers
|
20146
|
-
var
|
20147
|
-
|
20148
|
-
var levelDetails = this.getLevelDetails();
|
20149
|
-
gapController.poll(this.lastCurrentTime, activeFrag, levelDetails, state);
|
19758
|
+
var activeFrag = this.state !== State.IDLE ? this.fragCurrent : null;
|
19759
|
+
gapController.poll(this.lastCurrentTime, activeFrag);
|
20150
19760
|
}
|
20151
19761
|
this.lastCurrentTime = media.currentTime;
|
20152
19762
|
};
|
@@ -20512,17 +20122,6 @@
|
|
20512
20122
|
}
|
20513
20123
|
};
|
20514
20124
|
_createClass(StreamController, [{
|
20515
|
-
key: "maxBufferLength",
|
20516
|
-
get: function get() {
|
20517
|
-
var levels = this.levels,
|
20518
|
-
level = this.level;
|
20519
|
-
var levelInfo = levels == null ? void 0 : levels[level];
|
20520
|
-
if (!levelInfo) {
|
20521
|
-
return this.config.maxBufferLength;
|
20522
|
-
}
|
20523
|
-
return this.getMaxBufferLength(levelInfo.maxBitrate);
|
20524
|
-
}
|
20525
|
-
}, {
|
20526
20125
|
key: "nextLevel",
|
20527
20126
|
get: function get() {
|
20528
20127
|
var frag = this.nextBufferedFrag;
|
@@ -20622,12 +20221,9 @@
|
|
20622
20221
|
* The configuration object provided on player instantiation.
|
20623
20222
|
*/
|
20624
20223
|
this.userConfig = void 0;
|
20625
|
-
/**
|
20626
|
-
* The logger functions used by this player instance, configured on player instantiation.
|
20627
|
-
*/
|
20628
|
-
this.logger = void 0;
|
20629
20224
|
this.coreComponents = void 0;
|
20630
20225
|
this.networkControllers = void 0;
|
20226
|
+
this.started = false;
|
20631
20227
|
this._emitter = new EventEmitter();
|
20632
20228
|
this._autoLevelCapping = -1;
|
20633
20229
|
this._maxHdcpLevel = null;
|
@@ -20644,11 +20240,11 @@
|
|
20644
20240
|
this._media = null;
|
20645
20241
|
this.url = null;
|
20646
20242
|
this.triggeringException = void 0;
|
20647
|
-
|
20648
|
-
var config = this.config = mergeConfig(Hls.DefaultConfig, userConfig
|
20243
|
+
enableLogs(userConfig.debug || false, 'Hls instance');
|
20244
|
+
var config = this.config = mergeConfig(Hls.DefaultConfig, userConfig);
|
20649
20245
|
this.userConfig = userConfig;
|
20650
20246
|
if (config.progressive) {
|
20651
|
-
enableStreamingMode(config
|
20247
|
+
enableStreamingMode(config);
|
20652
20248
|
}
|
20653
20249
|
|
20654
20250
|
// core controllers and network loaders
|
@@ -20659,9 +20255,7 @@
|
|
20659
20255
|
ConfigFpsController = config.fpsController;
|
20660
20256
|
var errorController = new ConfigErrorController(this);
|
20661
20257
|
var abrController = this.abrController = new ConfigAbrController(this);
|
20662
|
-
|
20663
|
-
var fragmentTracker = new FragmentTracker(this);
|
20664
|
-
var bufferController = this.bufferController = new ConfigBufferController(this, fragmentTracker);
|
20258
|
+
var bufferController = this.bufferController = new ConfigBufferController(this);
|
20665
20259
|
var capLevelController = this.capLevelController = new ConfigCapLevelController(this);
|
20666
20260
|
var fpsController = new ConfigFpsController(this);
|
20667
20261
|
var playListLoader = new PlaylistLoader(this);
|
@@ -20670,6 +20264,8 @@
|
|
20670
20264
|
// ConentSteeringController is defined before LevelController to receive Multivariant Playlist events first
|
20671
20265
|
var contentSteering = ConfigContentSteeringController ? new ConfigContentSteeringController(this) : null;
|
20672
20266
|
var levelController = this.levelController = new LevelController(this, contentSteering);
|
20267
|
+
// FragmentTracker must be defined before StreamController because the order of event handling is important
|
20268
|
+
var fragmentTracker = new FragmentTracker(this);
|
20673
20269
|
var keyLoader = new KeyLoader(this.config);
|
20674
20270
|
var streamController = this.streamController = new StreamController(this, fragmentTracker, keyLoader);
|
20675
20271
|
|
@@ -20756,7 +20352,7 @@
|
|
20756
20352
|
try {
|
20757
20353
|
return this.emit(event, event, eventObject);
|
20758
20354
|
} catch (error) {
|
20759
|
-
|
20355
|
+
logger.error('An internal error happened while handling event ' + event + '. Error message: "' + error.message + '". Here is a stacktrace:', error);
|
20760
20356
|
// Prevent recursion in error event handlers that throw #5497
|
20761
20357
|
if (!this.triggeringException) {
|
20762
20358
|
this.triggeringException = true;
|
@@ -20782,7 +20378,7 @@
|
|
20782
20378
|
* Dispose of the instance
|
20783
20379
|
*/;
|
20784
20380
|
_proto.destroy = function destroy() {
|
20785
|
-
|
20381
|
+
logger.log('destroy');
|
20786
20382
|
this.trigger(Events.DESTROYING, undefined);
|
20787
20383
|
this.detachMedia();
|
20788
20384
|
this.removeAllListeners();
|
@@ -20807,7 +20403,7 @@
|
|
20807
20403
|
* Attaches Hls.js to a media element
|
20808
20404
|
*/;
|
20809
20405
|
_proto.attachMedia = function attachMedia(media) {
|
20810
|
-
|
20406
|
+
logger.log('attachMedia');
|
20811
20407
|
this._media = media;
|
20812
20408
|
this.trigger(Events.MEDIA_ATTACHING, {
|
20813
20409
|
media: media
|
@@ -20818,7 +20414,7 @@
|
|
20818
20414
|
* Detach Hls.js from the media
|
20819
20415
|
*/;
|
20820
20416
|
_proto.detachMedia = function detachMedia() {
|
20821
|
-
|
20417
|
+
logger.log('detachMedia');
|
20822
20418
|
this.trigger(Events.MEDIA_DETACHING, undefined);
|
20823
20419
|
this._media = null;
|
20824
20420
|
}
|
@@ -20835,7 +20431,7 @@
|
|
20835
20431
|
});
|
20836
20432
|
this._autoLevelCapping = -1;
|
20837
20433
|
this._maxHdcpLevel = null;
|
20838
|
-
|
20434
|
+
logger.log("loadSource:" + loadingSource);
|
20839
20435
|
if (media && loadedSource && (loadedSource !== loadingSource || this.bufferController.hasSourceTypes())) {
|
20840
20436
|
this.detachMedia();
|
20841
20437
|
this.attachMedia(media);
|
@@ -20857,7 +20453,8 @@
|
|
20857
20453
|
if (startPosition === void 0) {
|
20858
20454
|
startPosition = -1;
|
20859
20455
|
}
|
20860
|
-
|
20456
|
+
logger.log("startLoad(" + startPosition + ")");
|
20457
|
+
this.started = true;
|
20861
20458
|
this.networkControllers.forEach(function (controller) {
|
20862
20459
|
controller.startLoad(startPosition);
|
20863
20460
|
});
|
@@ -20867,31 +20464,34 @@
|
|
20867
20464
|
* Stop loading of any stream data.
|
20868
20465
|
*/;
|
20869
20466
|
_proto.stopLoad = function stopLoad() {
|
20870
|
-
|
20467
|
+
logger.log('stopLoad');
|
20468
|
+
this.started = false;
|
20871
20469
|
this.networkControllers.forEach(function (controller) {
|
20872
20470
|
controller.stopLoad();
|
20873
20471
|
});
|
20874
20472
|
}
|
20875
20473
|
|
20876
20474
|
/**
|
20877
|
-
* Resumes stream controller segment loading
|
20475
|
+
* Resumes stream controller segment loading if previously started.
|
20878
20476
|
*/;
|
20879
20477
|
_proto.resumeBuffering = function resumeBuffering() {
|
20880
|
-
this.
|
20881
|
-
|
20882
|
-
controller
|
20883
|
-
|
20884
|
-
|
20478
|
+
if (this.started) {
|
20479
|
+
this.networkControllers.forEach(function (controller) {
|
20480
|
+
if ('fragmentLoader' in controller) {
|
20481
|
+
controller.startLoad(-1);
|
20482
|
+
}
|
20483
|
+
});
|
20484
|
+
}
|
20885
20485
|
}
|
20886
20486
|
|
20887
20487
|
/**
|
20888
|
-
*
|
20488
|
+
* Stops stream controller segment loading without changing 'started' state like stopLoad().
|
20889
20489
|
* This allows for media buffering to be paused without interupting playlist loading.
|
20890
20490
|
*/;
|
20891
20491
|
_proto.pauseBuffering = function pauseBuffering() {
|
20892
20492
|
this.networkControllers.forEach(function (controller) {
|
20893
|
-
if (controller
|
20894
|
-
controller.
|
20493
|
+
if ('fragmentLoader' in controller) {
|
20494
|
+
controller.stopLoad();
|
20895
20495
|
}
|
20896
20496
|
});
|
20897
20497
|
}
|
@@ -20900,7 +20500,7 @@
|
|
20900
20500
|
* Swap through possible audio codecs in the stream (for example to switch from stereo to 5.1)
|
20901
20501
|
*/;
|
20902
20502
|
_proto.swapAudioCodec = function swapAudioCodec() {
|
20903
|
-
|
20503
|
+
logger.log('swapAudioCodec');
|
20904
20504
|
this.streamController.swapAudioCodec();
|
20905
20505
|
}
|
20906
20506
|
|
@@ -20911,7 +20511,7 @@
|
|
20911
20511
|
* Automatic recovery of media-errors by this process is configurable.
|
20912
20512
|
*/;
|
20913
20513
|
_proto.recoverMediaError = function recoverMediaError() {
|
20914
|
-
|
20514
|
+
logger.log('recoverMediaError');
|
20915
20515
|
var media = this._media;
|
20916
20516
|
this.detachMedia();
|
20917
20517
|
if (media) {
|
@@ -20966,7 +20566,7 @@
|
|
20966
20566
|
* Set quality level index immediately. This will flush the current buffer to replace the quality asap. That means playback will interrupt at least shortly to re-buffer and re-sync eventually. Set to -1 for automatic level selection.
|
20967
20567
|
*/,
|
20968
20568
|
set: function set(newLevel) {
|
20969
|
-
|
20569
|
+
logger.log("set currentLevel:" + newLevel);
|
20970
20570
|
this.levelController.manualLevel = newLevel;
|
20971
20571
|
this.streamController.immediateLevelSwitch();
|
20972
20572
|
}
|
@@ -20987,7 +20587,7 @@
|
|
20987
20587
|
* @param newLevel - Pass -1 for automatic level selection
|
20988
20588
|
*/,
|
20989
20589
|
set: function set(newLevel) {
|
20990
|
-
|
20590
|
+
logger.log("set nextLevel:" + newLevel);
|
20991
20591
|
this.levelController.manualLevel = newLevel;
|
20992
20592
|
this.streamController.nextLevelSwitch();
|
20993
20593
|
}
|
@@ -21008,7 +20608,7 @@
|
|
21008
20608
|
* @param newLevel - Pass -1 for automatic level selection
|
21009
20609
|
*/,
|
21010
20610
|
set: function set(newLevel) {
|
21011
|
-
|
20611
|
+
logger.log("set loadLevel:" + newLevel);
|
21012
20612
|
this.levelController.manualLevel = newLevel;
|
21013
20613
|
}
|
21014
20614
|
|
@@ -21043,7 +20643,7 @@
|
|
21043
20643
|
* Sets "first-level", see getter.
|
21044
20644
|
*/,
|
21045
20645
|
set: function set(newLevel) {
|
21046
|
-
|
20646
|
+
logger.log("set firstLevel:" + newLevel);
|
21047
20647
|
this.levelController.firstLevel = newLevel;
|
21048
20648
|
}
|
21049
20649
|
|
@@ -21070,7 +20670,7 @@
|
|
21070
20670
|
* (determined from download of first segment)
|
21071
20671
|
*/,
|
21072
20672
|
set: function set(newLevel) {
|
21073
|
-
|
20673
|
+
logger.log("set startLevel:" + newLevel);
|
21074
20674
|
// if not in automatic start level detection, ensure startLevel is greater than minAutoLevel
|
21075
20675
|
if (newLevel !== -1) {
|
21076
20676
|
newLevel = Math.max(newLevel, this.minAutoLevel);
|
@@ -21123,7 +20723,7 @@
|
|
21123
20723
|
*/
|
21124
20724
|
function set(newLevel) {
|
21125
20725
|
if (this._autoLevelCapping !== newLevel) {
|
21126
|
-
|
20726
|
+
logger.log("set autoLevelCapping:" + newLevel);
|
21127
20727
|
this._autoLevelCapping = newLevel;
|
21128
20728
|
this.levelController.checkMaxAutoUpdated();
|
21129
20729
|
}
|
@@ -21266,11 +20866,6 @@
|
|
21266
20866
|
get: function get() {
|
21267
20867
|
return this.streamController.getMainFwdBufferInfo();
|
21268
20868
|
}
|
21269
|
-
}, {
|
21270
|
-
key: "maxBufferLength",
|
21271
|
-
get: function get() {
|
21272
|
-
return this.streamController.maxBufferLength;
|
21273
|
-
}
|
21274
20869
|
}, {
|
21275
20870
|
key: "allAudioTracks",
|
21276
20871
|
get: function get() {
|
@@ -21453,7 +21048,7 @@
|
|
21453
21048
|
* Get the video-dev/hls.js package version.
|
21454
21049
|
*/
|
21455
21050
|
function get() {
|
21456
|
-
return "1.5.7
|
21051
|
+
return "1.5.7";
|
21457
21052
|
}
|
21458
21053
|
}, {
|
21459
21054
|
key: "Events",
|