hls.js 1.5.7 → 1.5.8-0.canary.10044
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 +2 -1
- package/dist/hls-demo.js +10 -0
- package/dist/hls-demo.js.map +1 -1
- package/dist/hls.js +2314 -1298
- package/dist/hls.js.d.ts +97 -84
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +1486 -1075
- 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 +1195 -789
- 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 +1979 -982
- 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 +3 -2
- package/src/controller/abr-controller.ts +24 -20
- package/src/controller/audio-stream-controller.ts +68 -74
- package/src/controller/audio-track-controller.ts +1 -1
- package/src/controller/base-playlist-controller.ts +20 -8
- package/src/controller/base-stream-controller.ts +157 -36
- package/src/controller/buffer-controller.ts +203 -67
- package/src/controller/buffer-operation-queue.ts +16 -19
- package/src/controller/cap-level-controller.ts +2 -2
- package/src/controller/cmcd-controller.ts +27 -6
- package/src/controller/content-steering-controller.ts +8 -6
- package/src/controller/eme-controller.ts +9 -22
- package/src/controller/error-controller.ts +6 -8
- package/src/controller/fps-controller.ts +2 -3
- package/src/controller/fragment-tracker.ts +15 -11
- package/src/controller/gap-controller.ts +43 -16
- package/src/controller/latency-controller.ts +9 -11
- package/src/controller/level-controller.ts +12 -18
- package/src/controller/stream-controller.ts +36 -31
- package/src/controller/subtitle-stream-controller.ts +28 -40
- package/src/controller/subtitle-track-controller.ts +5 -3
- package/src/controller/timeline-controller.ts +23 -30
- package/src/crypt/aes-crypto.ts +21 -2
- package/src/crypt/decrypter-aes-mode.ts +4 -0
- package/src/crypt/decrypter.ts +32 -18
- package/src/crypt/fast-aes-key.ts +24 -5
- package/src/demux/audio/adts.ts +9 -4
- package/src/demux/sample-aes.ts +2 -0
- package/src/demux/transmuxer-interface.ts +4 -12
- package/src/demux/transmuxer-worker.ts +4 -4
- package/src/demux/transmuxer.ts +16 -3
- package/src/demux/tsdemuxer.ts +71 -37
- package/src/demux/video/avc-video-parser.ts +208 -119
- package/src/demux/video/base-video-parser.ts +134 -2
- package/src/demux/video/exp-golomb.ts +0 -208
- package/src/demux/video/hevc-video-parser.ts +746 -0
- package/src/events.ts +7 -0
- package/src/hls.ts +49 -37
- package/src/loader/fragment-loader.ts +9 -2
- package/src/loader/key-loader.ts +2 -0
- package/src/loader/level-key.ts +10 -9
- package/src/loader/playlist-loader.ts +4 -5
- package/src/remux/mp4-generator.ts +196 -1
- package/src/remux/mp4-remuxer.ts +23 -7
- package/src/task-loop.ts +5 -2
- package/src/types/component-api.ts +2 -0
- package/src/types/demuxer.ts +3 -0
- package/src/types/events.ts +4 -0
- package/src/utils/buffer-helper.ts +12 -31
- package/src/utils/codecs.ts +34 -5
- package/src/utils/encryption-methods-util.ts +21 -0
- package/src/utils/logger.ts +54 -24
- package/src/utils/mp4-tools.ts +4 -2
package/dist/hls.light.js
CHANGED
@@ -5,6 +5,21 @@
|
|
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
|
+
}
|
8
23
|
function ownKeys(e, r) {
|
9
24
|
var t = Object.keys(e);
|
10
25
|
if (Object.getOwnPropertySymbols) {
|
@@ -103,32 +118,6 @@
|
|
103
118
|
};
|
104
119
|
return _setPrototypeOf(o, p);
|
105
120
|
}
|
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
|
-
}
|
132
121
|
function _isNativeFunction(fn) {
|
133
122
|
try {
|
134
123
|
return Function.toString.call(fn).indexOf("[native code]") !== -1;
|
@@ -363,6 +352,7 @@
|
|
363
352
|
Events["MEDIA_ATTACHED"] = "hlsMediaAttached";
|
364
353
|
Events["MEDIA_DETACHING"] = "hlsMediaDetaching";
|
365
354
|
Events["MEDIA_DETACHED"] = "hlsMediaDetached";
|
355
|
+
Events["MEDIA_ENDED"] = "hlsMediaEnded";
|
366
356
|
Events["BUFFER_RESET"] = "hlsBufferReset";
|
367
357
|
Events["BUFFER_CODECS"] = "hlsBufferCodecs";
|
368
358
|
Events["BUFFER_CREATED"] = "hlsBufferCreated";
|
@@ -476,61 +466,6 @@
|
|
476
466
|
return ErrorDetails;
|
477
467
|
}({});
|
478
468
|
|
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
|
-
|
534
469
|
var DECIMAL_RESOLUTION_REGEX = /^(\d+)x(\d+)$/;
|
535
470
|
var ATTR_LIST_REGEX = /(.+?)=(".*?"|.*?)(?:,|$)/g;
|
536
471
|
|
@@ -619,6 +554,77 @@
|
|
619
554
|
return AttrList;
|
620
555
|
}();
|
621
556
|
|
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.8-0.canary.10044");
|
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
|
+
|
622
628
|
// Avoid exporting const enum so that these values can be inlined
|
623
629
|
|
624
630
|
function isDateRangeCueAttribute(attrName) {
|
@@ -1173,10 +1179,30 @@
|
|
1173
1179
|
return LevelDetails;
|
1174
1180
|
}();
|
1175
1181
|
|
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
|
+
|
1176
1202
|
// This file is inserted as a shim for modules which we do not want to include into the distro.
|
1177
1203
|
// This replacement is done in the "alias" plugin of the rollup config.
|
1178
1204
|
var empty = undefined;
|
1179
|
-
var
|
1205
|
+
var HevcVideoParser = /*@__PURE__*/getDefaultExportFromCjs(empty);
|
1180
1206
|
|
1181
1207
|
function sliceUint8(array, start, end) {
|
1182
1208
|
// @ts-expect-error This polyfills IE11 usage of Uint8Array slice.
|
@@ -1811,7 +1837,7 @@
|
|
1811
1837
|
{
|
1812
1838
|
var codecBox = findBox(sampleEntries, [fourCC])[0];
|
1813
1839
|
var esdsBox = findBox(codecBox.subarray(28), ['esds'])[0];
|
1814
|
-
if (esdsBox && esdsBox.length >
|
1840
|
+
if (esdsBox && esdsBox.length > 7) {
|
1815
1841
|
var i = 4;
|
1816
1842
|
// ES Descriptor tag
|
1817
1843
|
if (esdsBox[i++] !== 0x03) {
|
@@ -1926,7 +1952,9 @@
|
|
1926
1952
|
}
|
1927
1953
|
function skipBERInteger(bytes, i) {
|
1928
1954
|
var limit = i + 5;
|
1929
|
-
while (bytes[i++] & 0x80 && i < limit) {
|
1955
|
+
while (bytes[i++] & 0x80 && i < limit) {
|
1956
|
+
/* do nothing */
|
1957
|
+
}
|
1930
1958
|
return i;
|
1931
1959
|
}
|
1932
1960
|
function toHex(x) {
|
@@ -2628,13 +2656,13 @@
|
|
2628
2656
|
this.keyFormatVersions = formatversions;
|
2629
2657
|
this.iv = iv;
|
2630
2658
|
this.encrypted = method ? method !== 'NONE' : false;
|
2631
|
-
this.isCommonEncryption = this.encrypted && method
|
2659
|
+
this.isCommonEncryption = this.encrypted && !isFullSegmentEncryption(method);
|
2632
2660
|
}
|
2633
2661
|
var _proto = LevelKey.prototype;
|
2634
2662
|
_proto.isSupported = function isSupported() {
|
2635
2663
|
// If it's Segment encryption or No encryption, just select that key system
|
2636
2664
|
if (this.method) {
|
2637
|
-
if (this.method
|
2665
|
+
if (isFullSegmentEncryption(this.method) || this.method === 'NONE') {
|
2638
2666
|
return true;
|
2639
2667
|
}
|
2640
2668
|
if (this.keyFormat === 'identity') {
|
@@ -2648,14 +2676,13 @@
|
|
2648
2676
|
if (!this.encrypted || !this.uri) {
|
2649
2677
|
return null;
|
2650
2678
|
}
|
2651
|
-
if (this.method
|
2679
|
+
if (isFullSegmentEncryption(this.method) && this.uri && !this.iv) {
|
2652
2680
|
if (typeof sn !== 'number') {
|
2653
2681
|
// We are fetching decryption data for a initialization segment
|
2654
|
-
// If the segment was encrypted with AES-128
|
2682
|
+
// If the segment was encrypted with AES-128/256
|
2655
2683
|
// It must have an IV defined. We cannot substitute the Segment Number in.
|
2656
|
-
|
2657
|
-
|
2658
|
-
}
|
2684
|
+
logger.warn("missing IV for initialization segment with method=\"" + this.method + "\" - compliance issue");
|
2685
|
+
|
2659
2686
|
// Explicitly set sn to resulting value from implicit conversions 'initSegment' values for IV generation.
|
2660
2687
|
sn = 0;
|
2661
2688
|
}
|
@@ -2817,23 +2844,28 @@
|
|
2817
2844
|
if (CODEC_COMPATIBLE_NAMES[lowerCaseCodec]) {
|
2818
2845
|
return CODEC_COMPATIBLE_NAMES[lowerCaseCodec];
|
2819
2846
|
}
|
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
|
2824
2847
|
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
|
2825
2851
|
flac: ['flac', 'fLaC', 'FLAC'],
|
2826
|
-
opus: ['opus', 'Opus']
|
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']
|
2827
2856
|
}[lowerCaseCodec];
|
2828
2857
|
for (var i = 0; i < codecsToCheck.length; i++) {
|
2858
|
+
var _getMediaSource;
|
2829
2859
|
if (isCodecMediaSourceSupported(codecsToCheck[i], 'audio', preferManagedMediaSource)) {
|
2830
2860
|
CODEC_COMPATIBLE_NAMES[lowerCaseCodec] = codecsToCheck[i];
|
2831
2861
|
return codecsToCheck[i];
|
2862
|
+
} else if (codecsToCheck[i] === 'mp3' && (_getMediaSource = getMediaSource(preferManagedMediaSource)) != null && _getMediaSource.isTypeSupported('audio/mpeg')) {
|
2863
|
+
return '';
|
2832
2864
|
}
|
2833
2865
|
}
|
2834
2866
|
return lowerCaseCodec;
|
2835
2867
|
}
|
2836
|
-
var AUDIO_CODEC_REGEXP = /flac|opus/i;
|
2868
|
+
var AUDIO_CODEC_REGEXP = /flac|opus|mp4a\.40\.34/i;
|
2837
2869
|
function getCodecCompatibleName(codec, preferManagedMediaSource) {
|
2838
2870
|
if (preferManagedMediaSource === void 0) {
|
2839
2871
|
preferManagedMediaSource = true;
|
@@ -2861,6 +2893,18 @@
|
|
2861
2893
|
}
|
2862
2894
|
return codec;
|
2863
2895
|
}
|
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
|
+
}
|
2864
2908
|
|
2865
2909
|
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;
|
2866
2910
|
var MASTER_PLAYLIST_MEDIA_REGEX = /#EXT-X-MEDIA:(.*)/g;
|
@@ -3661,10 +3705,10 @@
|
|
3661
3705
|
var loaderContext = loader.context;
|
3662
3706
|
if (loaderContext && loaderContext.url === context.url && loaderContext.level === context.level) {
|
3663
3707
|
// same URL can't overlap
|
3664
|
-
logger.trace('[playlist-loader]: playlist request ongoing');
|
3708
|
+
this.hls.logger.trace('[playlist-loader]: playlist request ongoing');
|
3665
3709
|
return;
|
3666
3710
|
}
|
3667
|
-
logger.log("[playlist-loader]: aborting previous loader for type: " + context.type);
|
3711
|
+
this.hls.logger.log("[playlist-loader]: aborting previous loader for type: " + context.type);
|
3668
3712
|
loader.abort();
|
3669
3713
|
}
|
3670
3714
|
|
@@ -3774,7 +3818,7 @@
|
|
3774
3818
|
// alt audio rendition in which quality levels (main)
|
3775
3819
|
// contains both audio+video. but with mixed audio track not signaled
|
3776
3820
|
if (!embeddedAudioFound && levels[0].audioCodec && !levels[0].attrs.AUDIO) {
|
3777
|
-
logger.log('[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one');
|
3821
|
+
this.hls.logger.log('[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one');
|
3778
3822
|
audioTracks.unshift({
|
3779
3823
|
type: 'main',
|
3780
3824
|
name: 'main',
|
@@ -3874,7 +3918,7 @@
|
|
3874
3918
|
message += " id: " + context.id + " group-id: \"" + context.groupId + "\"";
|
3875
3919
|
}
|
3876
3920
|
var error = new Error(message);
|
3877
|
-
logger.warn("[playlist-loader]: " + message);
|
3921
|
+
this.hls.logger.warn("[playlist-loader]: " + message);
|
3878
3922
|
var details = ErrorDetails.UNKNOWN;
|
3879
3923
|
var fatal = false;
|
3880
3924
|
var loader = this.getInternalLoader(context);
|
@@ -4435,8 +4479,43 @@
|
|
4435
4479
|
this.currentTime = 0;
|
4436
4480
|
this.stallCount = 0;
|
4437
4481
|
this._latency = null;
|
4438
|
-
this.
|
4439
|
-
|
4482
|
+
this.onTimeupdate = function () {
|
4483
|
+
var media = _this.media,
|
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
|
+
}
|
4440
4519
|
};
|
4441
4520
|
this.hls = hls;
|
4442
4521
|
this.config = hls.config;
|
@@ -4448,7 +4527,7 @@
|
|
4448
4527
|
this.onMediaDetaching();
|
4449
4528
|
this.levelDetails = null;
|
4450
4529
|
// @ts-ignore
|
4451
|
-
this.hls =
|
4530
|
+
this.hls = null;
|
4452
4531
|
};
|
4453
4532
|
_proto.registerListeners = function registerListeners() {
|
4454
4533
|
this.hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
|
@@ -4466,11 +4545,11 @@
|
|
4466
4545
|
};
|
4467
4546
|
_proto.onMediaAttached = function onMediaAttached(event, data) {
|
4468
4547
|
this.media = data.media;
|
4469
|
-
this.media.addEventListener('timeupdate', this.
|
4548
|
+
this.media.addEventListener('timeupdate', this.onTimeupdate);
|
4470
4549
|
};
|
4471
4550
|
_proto.onMediaDetaching = function onMediaDetaching() {
|
4472
4551
|
if (this.media) {
|
4473
|
-
this.media.removeEventListener('timeupdate', this.
|
4552
|
+
this.media.removeEventListener('timeupdate', this.onTimeupdate);
|
4474
4553
|
this.media = null;
|
4475
4554
|
}
|
4476
4555
|
};
|
@@ -4483,10 +4562,10 @@
|
|
4483
4562
|
var details = _ref.details;
|
4484
4563
|
this.levelDetails = details;
|
4485
4564
|
if (details.advanced) {
|
4486
|
-
this.
|
4565
|
+
this.onTimeupdate();
|
4487
4566
|
}
|
4488
4567
|
if (!details.live && this.media) {
|
4489
|
-
this.media.removeEventListener('timeupdate', this.
|
4568
|
+
this.media.removeEventListener('timeupdate', this.onTimeupdate);
|
4490
4569
|
}
|
4491
4570
|
};
|
4492
4571
|
_proto.onError = function onError(event, data) {
|
@@ -4496,45 +4575,7 @@
|
|
4496
4575
|
}
|
4497
4576
|
this.stallCount++;
|
4498
4577
|
if ((_this$levelDetails = this.levelDetails) != null && _this$levelDetails.live) {
|
4499
|
-
logger.warn('[
|
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;
|
4578
|
+
this.hls.logger.warn('[latency-controller]: Stall detected, adjusting target latency');
|
4538
4579
|
}
|
4539
4580
|
};
|
4540
4581
|
_proto.estimateLiveEdge = function estimateLiveEdge() {
|
@@ -5442,19 +5483,17 @@
|
|
5442
5483
|
MoveAllAlternatesMatchingHDCP: 2,
|
5443
5484
|
SwitchToSDR: 4
|
5444
5485
|
}; // Reserved for future use
|
5445
|
-
var ErrorController = /*#__PURE__*/function () {
|
5486
|
+
var ErrorController = /*#__PURE__*/function (_Logger) {
|
5487
|
+
_inheritsLoose(ErrorController, _Logger);
|
5446
5488
|
function ErrorController(hls) {
|
5447
|
-
|
5448
|
-
this.
|
5449
|
-
|
5450
|
-
|
5451
|
-
|
5452
|
-
|
5453
|
-
|
5454
|
-
|
5455
|
-
this.warn = logger.warn.bind(logger, "[warning]:");
|
5456
|
-
this.error = logger.error.bind(logger, "[error]:");
|
5457
|
-
this.registerListeners();
|
5489
|
+
var _this;
|
5490
|
+
_this = _Logger.call(this, 'error-controller', hls.logger) || this;
|
5491
|
+
_this.hls = void 0;
|
5492
|
+
_this.playlistError = 0;
|
5493
|
+
_this.penalizedRenditions = {};
|
5494
|
+
_this.hls = hls;
|
5495
|
+
_this.registerListeners();
|
5496
|
+
return _this;
|
5458
5497
|
}
|
5459
5498
|
var _proto = ErrorController.prototype;
|
5460
5499
|
_proto.registerListeners = function registerListeners() {
|
@@ -5810,19 +5849,19 @@
|
|
5810
5849
|
}
|
5811
5850
|
};
|
5812
5851
|
return ErrorController;
|
5813
|
-
}();
|
5852
|
+
}(Logger);
|
5814
5853
|
|
5815
|
-
var BasePlaylistController = /*#__PURE__*/function () {
|
5854
|
+
var BasePlaylistController = /*#__PURE__*/function (_Logger) {
|
5855
|
+
_inheritsLoose(BasePlaylistController, _Logger);
|
5816
5856
|
function BasePlaylistController(hls, logPrefix) {
|
5817
|
-
|
5818
|
-
this.
|
5819
|
-
|
5820
|
-
|
5821
|
-
|
5822
|
-
|
5823
|
-
|
5824
|
-
|
5825
|
-
this.hls = hls;
|
5857
|
+
var _this;
|
5858
|
+
_this = _Logger.call(this, logPrefix, hls.logger) || this;
|
5859
|
+
_this.hls = void 0;
|
5860
|
+
_this.timer = -1;
|
5861
|
+
_this.requestScheduled = -1;
|
5862
|
+
_this.canLoad = false;
|
5863
|
+
_this.hls = hls;
|
5864
|
+
return _this;
|
5826
5865
|
}
|
5827
5866
|
var _proto = BasePlaylistController.prototype;
|
5828
5867
|
_proto.destroy = function destroy() {
|
@@ -5855,7 +5894,7 @@
|
|
5855
5894
|
try {
|
5856
5895
|
uri = new self.URL(attr.URI, previous.url).href;
|
5857
5896
|
} catch (error) {
|
5858
|
-
|
5897
|
+
this.warn("Could not construct new URL for Rendition Report: " + error);
|
5859
5898
|
uri = attr.URI || '';
|
5860
5899
|
}
|
5861
5900
|
// Use exact match. Otherwise, the last partial match, if any, will be used
|
@@ -5894,7 +5933,7 @@
|
|
5894
5933
|
return this.timer === -1 && this.requestScheduled === -1 && this.shouldLoadPlaylist(playlist);
|
5895
5934
|
};
|
5896
5935
|
_proto.playlistLoaded = function playlistLoaded(index, data, previousDetails) {
|
5897
|
-
var
|
5936
|
+
var _this2 = this;
|
5898
5937
|
var details = data.details,
|
5899
5938
|
stats = data.stats;
|
5900
5939
|
|
@@ -5941,7 +5980,12 @@
|
|
5941
5980
|
var cdnAge = lastAdvanced + details.ageHeader;
|
5942
5981
|
var currentGoal = Math.min(cdnAge - details.partTarget, details.targetduration * 1.5);
|
5943
5982
|
if (currentGoal > 0) {
|
5944
|
-
if (
|
5983
|
+
if (cdnAge > details.targetduration * 3) {
|
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) {
|
5945
5989
|
// If we attempted to get the next or latest playlist update, but currentGoal increased,
|
5946
5990
|
// then we either can't catchup, or the "age" header cannot be trusted.
|
5947
5991
|
this.warn("CDN Tune-in goal increased from: " + previousDetails.tuneInGoal + " to: " + currentGoal + " with playlist age: " + details.age);
|
@@ -5999,7 +6043,7 @@
|
|
5999
6043
|
// );
|
6000
6044
|
|
6001
6045
|
this.timer = self.setTimeout(function () {
|
6002
|
-
return
|
6046
|
+
return _this2.loadPlaylist(deliveryDirectives);
|
6003
6047
|
}, estimatedTimeUntilUpdate);
|
6004
6048
|
} else {
|
6005
6049
|
this.clearTimer();
|
@@ -6015,7 +6059,7 @@
|
|
6015
6059
|
return new HlsUrlParameters(msn, part, skip);
|
6016
6060
|
};
|
6017
6061
|
_proto.checkRetry = function checkRetry(errorEvent) {
|
6018
|
-
var
|
6062
|
+
var _this3 = this;
|
6019
6063
|
var errorDetails = errorEvent.details;
|
6020
6064
|
var isTimeout = isTimeoutError(errorEvent);
|
6021
6065
|
var errorAction = errorEvent.errorAction;
|
@@ -6039,7 +6083,7 @@
|
|
6039
6083
|
var delay = getRetryDelay(retryConfig, retryCount);
|
6040
6084
|
// Schedule level/track reload
|
6041
6085
|
this.timer = self.setTimeout(function () {
|
6042
|
-
return
|
6086
|
+
return _this3.loadPlaylist();
|
6043
6087
|
}, delay);
|
6044
6088
|
this.warn("Retrying playlist loading " + (retryCount + 1) + "/" + retryConfig.maxNumRetry + " after \"" + errorDetails + "\" in " + delay + "ms");
|
6045
6089
|
}
|
@@ -6050,7 +6094,7 @@
|
|
6050
6094
|
return retry;
|
6051
6095
|
};
|
6052
6096
|
return BasePlaylistController;
|
6053
|
-
}();
|
6097
|
+
}(Logger);
|
6054
6098
|
|
6055
6099
|
/*
|
6056
6100
|
* compute an Exponential Weighted moving average
|
@@ -6424,30 +6468,33 @@
|
|
6424
6468
|
}, {});
|
6425
6469
|
}
|
6426
6470
|
|
6427
|
-
var AbrController = /*#__PURE__*/function () {
|
6471
|
+
var AbrController = /*#__PURE__*/function (_Logger) {
|
6472
|
+
_inheritsLoose(AbrController, _Logger);
|
6428
6473
|
function AbrController(_hls) {
|
6429
|
-
var _this
|
6430
|
-
this.
|
6431
|
-
|
6432
|
-
|
6433
|
-
|
6434
|
-
|
6435
|
-
|
6436
|
-
|
6437
|
-
|
6438
|
-
|
6439
|
-
|
6440
|
-
|
6441
|
-
|
6442
|
-
|
6474
|
+
var _this;
|
6475
|
+
_this = _Logger.call(this, 'abr', _hls.logger) || this;
|
6476
|
+
_this.hls = void 0;
|
6477
|
+
_this.lastLevelLoadSec = 0;
|
6478
|
+
_this.lastLoadedFragLevel = -1;
|
6479
|
+
_this.firstSelection = -1;
|
6480
|
+
_this._nextAutoLevel = -1;
|
6481
|
+
_this.nextAutoLevelKey = '';
|
6482
|
+
_this.audioTracksByGroup = null;
|
6483
|
+
_this.codecTiers = null;
|
6484
|
+
_this.timer = -1;
|
6485
|
+
_this.fragCurrent = null;
|
6486
|
+
_this.partCurrent = null;
|
6487
|
+
_this.bitrateTestDelay = 0;
|
6488
|
+
_this.bwEstimator = void 0;
|
6443
6489
|
/*
|
6444
6490
|
This method monitors the download rate of the current fragment, and will downswitch if that fragment will not load
|
6445
6491
|
quickly enough to prevent underbuffering
|
6446
6492
|
*/
|
6447
|
-
|
6448
|
-
var
|
6449
|
-
|
6450
|
-
|
6493
|
+
_this._abandonRulesCheck = function () {
|
6494
|
+
var _assertThisInitialize = _assertThisInitialized(_this),
|
6495
|
+
frag = _assertThisInitialize.fragCurrent,
|
6496
|
+
part = _assertThisInitialize.partCurrent,
|
6497
|
+
hls = _assertThisInitialize.hls;
|
6451
6498
|
var autoLevelEnabled = hls.autoLevelEnabled,
|
6452
6499
|
media = hls.media;
|
6453
6500
|
if (!frag || !media) {
|
@@ -6536,21 +6583,22 @@
|
|
6536
6583
|
_this.resetEstimator(nextLoadLevelBitrate);
|
6537
6584
|
}
|
6538
6585
|
_this.clearTimer();
|
6539
|
-
|
6586
|
+
_this.warn("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");
|
6540
6587
|
hls.trigger(Events.FRAG_LOAD_EMERGENCY_ABORTED, {
|
6541
6588
|
frag: frag,
|
6542
6589
|
part: part,
|
6543
6590
|
stats: stats
|
6544
6591
|
});
|
6545
6592
|
};
|
6546
|
-
|
6547
|
-
|
6548
|
-
|
6593
|
+
_this.hls = _hls;
|
6594
|
+
_this.bwEstimator = _this.initEstimator();
|
6595
|
+
_this.registerListeners();
|
6596
|
+
return _this;
|
6549
6597
|
}
|
6550
6598
|
var _proto = AbrController.prototype;
|
6551
6599
|
_proto.resetEstimator = function resetEstimator(abrEwmaDefaultEstimate) {
|
6552
6600
|
if (abrEwmaDefaultEstimate) {
|
6553
|
-
|
6601
|
+
this.log("setting initial bwe to " + abrEwmaDefaultEstimate);
|
6554
6602
|
this.hls.config.abrEwmaDefaultEstimate = abrEwmaDefaultEstimate;
|
6555
6603
|
}
|
6556
6604
|
this.firstSelection = -1;
|
@@ -6765,6 +6813,9 @@
|
|
6765
6813
|
var fragCurrent = this.fragCurrent,
|
6766
6814
|
partCurrent = this.partCurrent,
|
6767
6815
|
hls = this.hls;
|
6816
|
+
if (hls.levels.length <= 1) {
|
6817
|
+
return hls.loadLevel;
|
6818
|
+
}
|
6768
6819
|
var maxAutoLevel = hls.maxAutoLevel,
|
6769
6820
|
config = hls.config,
|
6770
6821
|
minAutoLevel = hls.minAutoLevel;
|
@@ -6795,13 +6846,13 @@
|
|
6795
6846
|
// cap maxLoadingDelay and ensure it is not bigger 'than bitrate test' frag duration
|
6796
6847
|
var maxLoadingDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxLoadingDelay) : config.maxLoadingDelay;
|
6797
6848
|
maxStarvationDelay = maxLoadingDelay - bitrateTestDelay;
|
6798
|
-
|
6849
|
+
this.info("bitrate test took " + Math.round(1000 * bitrateTestDelay) + "ms, set first fragment max fetchDuration to " + Math.round(1000 * maxStarvationDelay) + " ms");
|
6799
6850
|
// don't use conservative factor on bitrate test
|
6800
6851
|
bwFactor = bwUpFactor = 1;
|
6801
6852
|
}
|
6802
6853
|
}
|
6803
6854
|
var bestLevel = this.findBestLevel(avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay, maxStarvationDelay, bwFactor, bwUpFactor);
|
6804
|
-
|
6855
|
+
this.info((bufferStarvationDelay ? 'rebuffering expected' : 'buffer is empty') + ", optimal quality level " + bestLevel);
|
6805
6856
|
if (bestLevel > -1) {
|
6806
6857
|
return bestLevel;
|
6807
6858
|
}
|
@@ -6869,7 +6920,7 @@
|
|
6869
6920
|
currentVideoRange = preferHDR ? videoRanges[videoRanges.length - 1] : videoRanges[0];
|
6870
6921
|
currentFrameRate = minFramerate;
|
6871
6922
|
currentBw = Math.max(currentBw, minBitrate);
|
6872
|
-
|
6923
|
+
this.log("picked start tier " + JSON.stringify(startTier));
|
6873
6924
|
} else {
|
6874
6925
|
currentCodecSet = level == null ? void 0 : level.codecSet;
|
6875
6926
|
currentVideoRange = level == null ? void 0 : level.videoRange;
|
@@ -6922,9 +6973,9 @@
|
|
6922
6973
|
var forcedAutoLevel = _this2.forcedAutoLevel;
|
6923
6974
|
if (i !== loadLevel && (forcedAutoLevel === -1 || forcedAutoLevel !== loadLevel)) {
|
6924
6975
|
if (levelsSkipped.length) {
|
6925
|
-
|
6976
|
+
_this2.trace("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);
|
6926
6977
|
}
|
6927
|
-
|
6978
|
+
_this2.info("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);
|
6928
6979
|
}
|
6929
6980
|
if (firstSelection) {
|
6930
6981
|
_this2.firstSelection = i;
|
@@ -6958,7 +7009,7 @@
|
|
6958
7009
|
}
|
6959
7010
|
var firstLevel = this.hls.firstLevel;
|
6960
7011
|
var clamped = Math.min(Math.max(firstLevel, minAutoLevel), maxAutoLevel);
|
6961
|
-
|
7012
|
+
this.warn("Could not find best starting auto level. Defaulting to first in playlist " + firstLevel + " clamped to " + clamped);
|
6962
7013
|
return clamped;
|
6963
7014
|
}
|
6964
7015
|
}, {
|
@@ -7011,7 +7062,7 @@
|
|
7011
7062
|
}
|
7012
7063
|
}]);
|
7013
7064
|
return AbrController;
|
7014
|
-
}();
|
7065
|
+
}(Logger);
|
7015
7066
|
|
7016
7067
|
/**
|
7017
7068
|
* Provides methods dealing with buffer length retrieval for example.
|
@@ -7036,40 +7087,29 @@
|
|
7036
7087
|
* Return true if `media`'s buffered include `position`
|
7037
7088
|
*/
|
7038
7089
|
BufferHelper.isBuffered = function isBuffered(media, position) {
|
7039
|
-
|
7040
|
-
|
7041
|
-
|
7042
|
-
|
7043
|
-
|
7044
|
-
return true;
|
7045
|
-
}
|
7090
|
+
if (media) {
|
7091
|
+
var buffered = BufferHelper.getBuffered(media);
|
7092
|
+
for (var i = buffered.length; i--;) {
|
7093
|
+
if (position >= buffered.start(i) && position <= buffered.end(i)) {
|
7094
|
+
return true;
|
7046
7095
|
}
|
7047
7096
|
}
|
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
|
7052
7097
|
}
|
7053
7098
|
return false;
|
7054
7099
|
};
|
7055
7100
|
BufferHelper.bufferInfo = function bufferInfo(media, pos, maxHoleDuration) {
|
7056
|
-
|
7057
|
-
|
7058
|
-
|
7101
|
+
if (media) {
|
7102
|
+
var vbuffered = BufferHelper.getBuffered(media);
|
7103
|
+
if (vbuffered.length) {
|
7059
7104
|
var buffered = [];
|
7060
|
-
var i;
|
7061
|
-
for (i = 0; i < vbuffered.length; i++) {
|
7105
|
+
for (var i = 0; i < vbuffered.length; i++) {
|
7062
7106
|
buffered.push({
|
7063
7107
|
start: vbuffered.start(i),
|
7064
7108
|
end: vbuffered.end(i)
|
7065
7109
|
});
|
7066
7110
|
}
|
7067
|
-
return
|
7111
|
+
return BufferHelper.bufferedInfo(buffered, pos, maxHoleDuration);
|
7068
7112
|
}
|
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
|
7073
7113
|
}
|
7074
7114
|
return {
|
7075
7115
|
len: 0,
|
@@ -7082,12 +7122,7 @@
|
|
7082
7122
|
pos = Math.max(0, pos);
|
7083
7123
|
// sort on buffer.start/smaller end (IE does not always return sorted buffered range)
|
7084
7124
|
buffered.sort(function (a, b) {
|
7085
|
-
|
7086
|
-
if (diff) {
|
7087
|
-
return diff;
|
7088
|
-
} else {
|
7089
|
-
return b.end - a.end;
|
7090
|
-
}
|
7125
|
+
return a.start - b.start || b.end - a.end;
|
7091
7126
|
});
|
7092
7127
|
var buffered2 = [];
|
7093
7128
|
if (maxHoleDuration) {
|
@@ -7155,7 +7190,7 @@
|
|
7155
7190
|
*/;
|
7156
7191
|
BufferHelper.getBuffered = function getBuffered(media) {
|
7157
7192
|
try {
|
7158
|
-
return media.buffered;
|
7193
|
+
return media.buffered || noopBuffered;
|
7159
7194
|
} catch (e) {
|
7160
7195
|
logger.log('failed to get media.buffered', e);
|
7161
7196
|
return noopBuffered;
|
@@ -7182,24 +7217,23 @@
|
|
7182
7217
|
this.executeNext(type);
|
7183
7218
|
}
|
7184
7219
|
};
|
7185
|
-
_proto.insertAbort = function insertAbort(operation, type) {
|
7186
|
-
var queue = this.queues[type];
|
7187
|
-
queue.unshift(operation);
|
7188
|
-
this.executeNext(type);
|
7189
|
-
};
|
7190
7220
|
_proto.appendBlocker = function appendBlocker(type) {
|
7191
|
-
var
|
7192
|
-
|
7193
|
-
|
7221
|
+
var _this = this;
|
7222
|
+
return new Promise(function (resolve) {
|
7223
|
+
var operation = {
|
7224
|
+
execute: resolve,
|
7225
|
+
onStart: function onStart() {},
|
7226
|
+
onComplete: function onComplete() {},
|
7227
|
+
onError: function onError() {}
|
7228
|
+
};
|
7229
|
+
_this.append(operation, type);
|
7194
7230
|
});
|
7195
|
-
|
7196
|
-
|
7197
|
-
|
7198
|
-
|
7199
|
-
|
7200
|
-
}
|
7201
|
-
this.append(operation, type);
|
7202
|
-
return promise;
|
7231
|
+
};
|
7232
|
+
_proto.unblockAudio = function unblockAudio(op) {
|
7233
|
+
var queue = this.queues.audio;
|
7234
|
+
if (queue[0] === op) {
|
7235
|
+
this.shiftAndExecuteNext('audio');
|
7236
|
+
}
|
7203
7237
|
};
|
7204
7238
|
_proto.executeNext = function executeNext(type) {
|
7205
7239
|
var queue = this.queues[type];
|
@@ -7232,61 +7266,69 @@
|
|
7232
7266
|
}();
|
7233
7267
|
|
7234
7268
|
var VIDEO_CODEC_PROFILE_REPLACE = /(avc[1234]|hvc1|hev1|dvh[1e]|vp09|av01)(?:\.[^.,]+)+/;
|
7235
|
-
var BufferController = /*#__PURE__*/function () {
|
7236
|
-
|
7237
|
-
|
7269
|
+
var BufferController = /*#__PURE__*/function (_Logger) {
|
7270
|
+
_inheritsLoose(BufferController, _Logger);
|
7271
|
+
function BufferController(hls, fragmentTracker) {
|
7272
|
+
var _this;
|
7273
|
+
_this = _Logger.call(this, 'buffer-controller', hls.logger) || this;
|
7238
7274
|
// The level details used to determine duration, target-duration and live
|
7239
|
-
|
7275
|
+
_this.details = null;
|
7240
7276
|
// cache the self generated object url to detect hijack of video tag
|
7241
|
-
|
7277
|
+
_this._objectUrl = null;
|
7242
7278
|
// A queue of buffer operations which require the SourceBuffer to not be updating upon execution
|
7243
|
-
|
7279
|
+
_this.operationQueue = void 0;
|
7244
7280
|
// References to event listeners for each SourceBuffer, so that they can be referenced for event removal
|
7245
|
-
|
7246
|
-
|
7281
|
+
_this.listeners = void 0;
|
7282
|
+
_this.hls = void 0;
|
7283
|
+
_this.fragmentTracker = void 0;
|
7247
7284
|
// The number of BUFFER_CODEC events received before any sourceBuffers are created
|
7248
|
-
|
7285
|
+
_this.bufferCodecEventsExpected = 0;
|
7249
7286
|
// The total number of BUFFER_CODEC events received
|
7250
|
-
|
7287
|
+
_this._bufferCodecEventsTotal = 0;
|
7251
7288
|
// A reference to the attached media element
|
7252
|
-
|
7289
|
+
_this.media = null;
|
7253
7290
|
// A reference to the active media source
|
7254
|
-
|
7291
|
+
_this.mediaSource = null;
|
7255
7292
|
// Last MP3 audio chunk appended
|
7256
|
-
|
7257
|
-
|
7293
|
+
_this.lastMpegAudioChunk = null;
|
7294
|
+
// Audio fragment blocked from appending until corresponding video appends or context changes
|
7295
|
+
_this.blockedAudioAppend = null;
|
7296
|
+
// Keep track of video append position for unblocking audio
|
7297
|
+
_this.lastVideoAppendEnd = 0;
|
7298
|
+
_this.appendSource = void 0;
|
7258
7299
|
// counters
|
7259
|
-
|
7300
|
+
_this.appendErrors = {
|
7260
7301
|
audio: 0,
|
7261
7302
|
video: 0,
|
7262
7303
|
audiovideo: 0
|
7263
7304
|
};
|
7264
|
-
|
7265
|
-
|
7266
|
-
|
7267
|
-
|
7268
|
-
this.warn = void 0;
|
7269
|
-
this.error = void 0;
|
7270
|
-
this._onEndStreaming = function (event) {
|
7305
|
+
_this.tracks = {};
|
7306
|
+
_this.pendingTracks = {};
|
7307
|
+
_this.sourceBuffer = void 0;
|
7308
|
+
_this._onEndStreaming = function (event) {
|
7271
7309
|
if (!_this.hls) {
|
7272
7310
|
return;
|
7273
7311
|
}
|
7274
7312
|
_this.hls.pauseBuffering();
|
7275
7313
|
};
|
7276
|
-
|
7314
|
+
_this._onStartStreaming = function (event) {
|
7277
7315
|
if (!_this.hls) {
|
7278
7316
|
return;
|
7279
7317
|
}
|
7280
7318
|
_this.hls.resumeBuffering();
|
7281
7319
|
};
|
7282
7320
|
// Keep as arrow functions so that we can directly reference these functions directly as event listeners
|
7283
|
-
|
7284
|
-
var
|
7285
|
-
|
7321
|
+
_this._onMediaSourceOpen = function () {
|
7322
|
+
var _assertThisInitialize = _assertThisInitialized(_this),
|
7323
|
+
media = _assertThisInitialize.media,
|
7324
|
+
mediaSource = _assertThisInitialize.mediaSource;
|
7286
7325
|
_this.log('Media source opened');
|
7287
7326
|
if (media) {
|
7288
7327
|
media.removeEventListener('emptied', _this._onMediaEmptied);
|
7289
|
-
_this.
|
7328
|
+
var durationAndRange = _this.getDurationAndRange();
|
7329
|
+
if (durationAndRange) {
|
7330
|
+
_this.updateMediaSource(durationAndRange);
|
7331
|
+
}
|
7290
7332
|
_this.hls.trigger(Events.MEDIA_ATTACHED, {
|
7291
7333
|
media: media,
|
7292
7334
|
mediaSource: mediaSource
|
@@ -7298,27 +7340,26 @@
|
|
7298
7340
|
}
|
7299
7341
|
_this.checkPendingTracks();
|
7300
7342
|
};
|
7301
|
-
|
7343
|
+
_this._onMediaSourceClose = function () {
|
7302
7344
|
_this.log('Media source closed');
|
7303
7345
|
};
|
7304
|
-
|
7346
|
+
_this._onMediaSourceEnded = function () {
|
7305
7347
|
_this.log('Media source ended');
|
7306
7348
|
};
|
7307
|
-
|
7308
|
-
var
|
7309
|
-
|
7349
|
+
_this._onMediaEmptied = function () {
|
7350
|
+
var _assertThisInitialize2 = _assertThisInitialized(_this),
|
7351
|
+
mediaSrc = _assertThisInitialize2.mediaSrc,
|
7352
|
+
_objectUrl = _assertThisInitialize2._objectUrl;
|
7310
7353
|
if (mediaSrc !== _objectUrl) {
|
7311
|
-
|
7354
|
+
_this.error("Media element src was set while attaching MediaSource (" + _objectUrl + " > " + mediaSrc + ")");
|
7312
7355
|
}
|
7313
7356
|
};
|
7314
|
-
|
7315
|
-
|
7316
|
-
|
7317
|
-
|
7318
|
-
|
7319
|
-
|
7320
|
-
this._initSourceBuffer();
|
7321
|
-
this.registerListeners();
|
7357
|
+
_this.hls = hls;
|
7358
|
+
_this.fragmentTracker = fragmentTracker;
|
7359
|
+
_this.appendSource = hls.config.preferManagedMediaSource && typeof self !== 'undefined' && self.ManagedMediaSource;
|
7360
|
+
_this._initSourceBuffer();
|
7361
|
+
_this.registerListeners();
|
7362
|
+
return _this;
|
7322
7363
|
}
|
7323
7364
|
var _proto = BufferController.prototype;
|
7324
7365
|
_proto.hasSourceTypes = function hasSourceTypes() {
|
@@ -7329,7 +7370,13 @@
|
|
7329
7370
|
this.details = null;
|
7330
7371
|
this.lastMpegAudioChunk = null;
|
7331
7372
|
// @ts-ignore
|
7332
|
-
this.hls = null;
|
7373
|
+
this.hls = this.fragmentTracker = null;
|
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;
|
7333
7380
|
};
|
7334
7381
|
_proto.registerListeners = function registerListeners() {
|
7335
7382
|
var hls = this.hls;
|
@@ -7375,6 +7422,8 @@
|
|
7375
7422
|
audiovideo: 0
|
7376
7423
|
};
|
7377
7424
|
this.lastMpegAudioChunk = null;
|
7425
|
+
this.blockedAudioAppend = null;
|
7426
|
+
this.lastVideoAppendEnd = 0;
|
7378
7427
|
};
|
7379
7428
|
_proto.onManifestLoading = function onManifestLoading() {
|
7380
7429
|
this.bufferCodecEventsExpected = this._bufferCodecEventsTotal = 0;
|
@@ -7491,6 +7540,7 @@
|
|
7491
7540
|
_this2.resetBuffer(type);
|
7492
7541
|
});
|
7493
7542
|
this._initSourceBuffer();
|
7543
|
+
this.hls.resumeBuffering();
|
7494
7544
|
};
|
7495
7545
|
_proto.resetBuffer = function resetBuffer(type) {
|
7496
7546
|
var sb = this.sourceBuffer[type];
|
@@ -7515,9 +7565,10 @@
|
|
7515
7565
|
var trackNames = Object.keys(data);
|
7516
7566
|
trackNames.forEach(function (trackName) {
|
7517
7567
|
if (sourceBufferCount) {
|
7568
|
+
var _track$buffer;
|
7518
7569
|
// check if SourceBuffer codec needs to change
|
7519
7570
|
var track = _this3.tracks[trackName];
|
7520
|
-
if (track && typeof track.buffer.changeType === 'function') {
|
7571
|
+
if (track && typeof ((_track$buffer = track.buffer) == null ? void 0 : _track$buffer.changeType) === 'function') {
|
7521
7572
|
var _trackCodec;
|
7522
7573
|
var _data$trackName = data[trackName],
|
7523
7574
|
id = _data$trackName.id,
|
@@ -7585,17 +7636,52 @@
|
|
7585
7636
|
};
|
7586
7637
|
operationQueue.append(operation, type, !!this.pendingTracks[type]);
|
7587
7638
|
};
|
7639
|
+
_proto.blockAudio = function blockAudio(partOrFrag) {
|
7640
|
+
var _this$fragmentTracker,
|
7641
|
+
_this5 = this;
|
7642
|
+
var pStart = partOrFrag.start;
|
7643
|
+
var pTime = pStart + partOrFrag.duration * 0.05;
|
7644
|
+
var atGap = ((_this$fragmentTracker = this.fragmentTracker.getAppendedFrag(pStart, PlaylistLevelType.MAIN)) == null ? void 0 : _this$fragmentTracker.gap) === true;
|
7645
|
+
if (atGap) {
|
7646
|
+
return;
|
7647
|
+
}
|
7648
|
+
var op = {
|
7649
|
+
execute: function execute() {
|
7650
|
+
var _this5$fragmentTracke;
|
7651
|
+
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) {
|
7652
|
+
_this5.blockedAudioAppend = null;
|
7653
|
+
_this5.operationQueue.shiftAndExecuteNext('audio');
|
7654
|
+
}
|
7655
|
+
},
|
7656
|
+
onStart: function onStart() {},
|
7657
|
+
onComplete: function onComplete() {},
|
7658
|
+
onError: function onError() {}
|
7659
|
+
};
|
7660
|
+
this.blockedAudioAppend = {
|
7661
|
+
op: op,
|
7662
|
+
frag: partOrFrag
|
7663
|
+
};
|
7664
|
+
this.operationQueue.append(op, 'audio', true);
|
7665
|
+
};
|
7666
|
+
_proto.unblockAudio = function unblockAudio() {
|
7667
|
+
var blockedAudioAppend = this.blockedAudioAppend;
|
7668
|
+
if (blockedAudioAppend) {
|
7669
|
+
this.blockedAudioAppend = null;
|
7670
|
+
this.operationQueue.unblockAudio(blockedAudioAppend.op);
|
7671
|
+
}
|
7672
|
+
};
|
7588
7673
|
_proto.onBufferAppending = function onBufferAppending(event, eventData) {
|
7589
|
-
var
|
7590
|
-
var
|
7591
|
-
operationQueue = this.operationQueue,
|
7674
|
+
var _this6 = this;
|
7675
|
+
var operationQueue = this.operationQueue,
|
7592
7676
|
tracks = this.tracks;
|
7593
7677
|
var data = eventData.data,
|
7594
7678
|
type = eventData.type,
|
7679
|
+
parent = eventData.parent,
|
7595
7680
|
frag = eventData.frag,
|
7596
7681
|
part = eventData.part,
|
7597
7682
|
chunkMeta = eventData.chunkMeta;
|
7598
7683
|
var chunkStats = chunkMeta.buffering[type];
|
7684
|
+
var sn = frag.sn;
|
7599
7685
|
var bufferAppendingStart = self.performance.now();
|
7600
7686
|
chunkStats.start = bufferAppendingStart;
|
7601
7687
|
var fragBuffering = frag.stats.buffering;
|
@@ -7618,21 +7704,50 @@
|
|
7618
7704
|
checkTimestampOffset = !this.lastMpegAudioChunk || chunkMeta.id === 1 || this.lastMpegAudioChunk.sn !== chunkMeta.sn;
|
7619
7705
|
this.lastMpegAudioChunk = chunkMeta;
|
7620
7706
|
}
|
7621
|
-
|
7707
|
+
|
7708
|
+
// Block audio append until overlapping video append
|
7709
|
+
var videoSb = this.sourceBuffer.video;
|
7710
|
+
if (videoSb && sn !== 'initSegment') {
|
7711
|
+
var partOrFrag = part || frag;
|
7712
|
+
var blockedAudioAppend = this.blockedAudioAppend;
|
7713
|
+
if (type === 'audio' && parent !== 'main' && !this.blockedAudioAppend) {
|
7714
|
+
var pStart = partOrFrag.start;
|
7715
|
+
var pTime = pStart + partOrFrag.duration * 0.05;
|
7716
|
+
var vbuffered = videoSb.buffered;
|
7717
|
+
var vappending = this.operationQueue.current('video');
|
7718
|
+
if (!vbuffered.length && !vappending) {
|
7719
|
+
// wait for video before appending audio
|
7720
|
+
this.blockAudio(partOrFrag);
|
7721
|
+
} else if (!vappending && !BufferHelper.isBuffered(videoSb, pTime) && this.lastVideoAppendEnd < pTime) {
|
7722
|
+
// audio is ahead of video
|
7723
|
+
this.blockAudio(partOrFrag);
|
7724
|
+
}
|
7725
|
+
} else if (type === 'video') {
|
7726
|
+
var videoAppendEnd = partOrFrag.end;
|
7727
|
+
if (blockedAudioAppend) {
|
7728
|
+
var audioStart = blockedAudioAppend.frag.start;
|
7729
|
+
if (videoAppendEnd > audioStart || videoAppendEnd < this.lastVideoAppendEnd || BufferHelper.isBuffered(videoSb, audioStart)) {
|
7730
|
+
this.unblockAudio();
|
7731
|
+
}
|
7732
|
+
}
|
7733
|
+
this.lastVideoAppendEnd = videoAppendEnd;
|
7734
|
+
}
|
7735
|
+
}
|
7736
|
+
var fragStart = (part || frag).start;
|
7622
7737
|
var operation = {
|
7623
7738
|
execute: function execute() {
|
7624
7739
|
chunkStats.executeStart = self.performance.now();
|
7625
7740
|
if (checkTimestampOffset) {
|
7626
|
-
var sb =
|
7741
|
+
var sb = _this6.sourceBuffer[type];
|
7627
7742
|
if (sb) {
|
7628
7743
|
var delta = fragStart - sb.timestampOffset;
|
7629
7744
|
if (Math.abs(delta) >= 0.1) {
|
7630
|
-
|
7745
|
+
_this6.log("Updating audio SourceBuffer timestampOffset to " + fragStart + " (delta: " + delta + ") sn: " + sn + ")");
|
7631
7746
|
sb.timestampOffset = fragStart;
|
7632
7747
|
}
|
7633
7748
|
}
|
7634
7749
|
}
|
7635
|
-
|
7750
|
+
_this6.appendExecutor(data, type);
|
7636
7751
|
},
|
7637
7752
|
onStart: function onStart() {
|
7638
7753
|
// logger.debug(`[buffer-controller]: ${type} SourceBuffer updatestart`);
|
@@ -7647,19 +7762,19 @@
|
|
7647
7762
|
if (partBuffering && partBuffering.first === 0) {
|
7648
7763
|
partBuffering.first = end;
|
7649
7764
|
}
|
7650
|
-
var sourceBuffer =
|
7765
|
+
var sourceBuffer = _this6.sourceBuffer;
|
7651
7766
|
var timeRanges = {};
|
7652
7767
|
for (var _type in sourceBuffer) {
|
7653
7768
|
timeRanges[_type] = BufferHelper.getBuffered(sourceBuffer[_type]);
|
7654
7769
|
}
|
7655
|
-
|
7770
|
+
_this6.appendErrors[type] = 0;
|
7656
7771
|
if (type === 'audio' || type === 'video') {
|
7657
|
-
|
7772
|
+
_this6.appendErrors.audiovideo = 0;
|
7658
7773
|
} else {
|
7659
|
-
|
7660
|
-
|
7774
|
+
_this6.appendErrors.audio = 0;
|
7775
|
+
_this6.appendErrors.video = 0;
|
7661
7776
|
}
|
7662
|
-
|
7777
|
+
_this6.hls.trigger(Events.BUFFER_APPENDED, {
|
7663
7778
|
type: type,
|
7664
7779
|
frag: frag,
|
7665
7780
|
part: part,
|
@@ -7687,51 +7802,57 @@
|
|
7687
7802
|
// let's stop appending any segments, and report BUFFER_FULL_ERROR error
|
7688
7803
|
event.details = ErrorDetails.BUFFER_FULL_ERROR;
|
7689
7804
|
} else {
|
7690
|
-
var appendErrorCount = ++
|
7805
|
+
var appendErrorCount = ++_this6.appendErrors[type];
|
7691
7806
|
event.details = ErrorDetails.BUFFER_APPEND_ERROR;
|
7692
7807
|
/* with UHD content, we could get loop of quota exceeded error until
|
7693
7808
|
browser is able to evict some data from sourcebuffer. Retrying can help recover.
|
7694
7809
|
*/
|
7695
|
-
|
7696
|
-
if (appendErrorCount >= hls.config.appendErrorMaxRetry) {
|
7810
|
+
_this6.warn("Failed " + appendErrorCount + "/" + _this6.hls.config.appendErrorMaxRetry + " times to append segment in \"" + type + "\" sourceBuffer");
|
7811
|
+
if (appendErrorCount >= _this6.hls.config.appendErrorMaxRetry) {
|
7697
7812
|
event.fatal = true;
|
7698
7813
|
}
|
7699
7814
|
}
|
7700
|
-
hls.trigger(Events.ERROR, event);
|
7815
|
+
_this6.hls.trigger(Events.ERROR, event);
|
7701
7816
|
}
|
7702
7817
|
};
|
7703
7818
|
operationQueue.append(operation, type, !!this.pendingTracks[type]);
|
7704
7819
|
};
|
7705
|
-
_proto.
|
7706
|
-
var
|
7707
|
-
|
7708
|
-
|
7709
|
-
|
7710
|
-
|
7711
|
-
|
7712
|
-
|
7713
|
-
|
7714
|
-
|
7715
|
-
|
7716
|
-
|
7717
|
-
|
7718
|
-
|
7719
|
-
|
7720
|
-
|
7721
|
-
|
7722
|
-
|
7723
|
-
};
|
7820
|
+
_proto.getFlushOp = function getFlushOp(type, start, end) {
|
7821
|
+
var _this7 = this;
|
7822
|
+
return {
|
7823
|
+
execute: function execute() {
|
7824
|
+
_this7.removeExecutor(type, start, end);
|
7825
|
+
},
|
7826
|
+
onStart: function onStart() {
|
7827
|
+
// logger.debug(`[buffer-controller]: Started flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`);
|
7828
|
+
},
|
7829
|
+
onComplete: function onComplete() {
|
7830
|
+
// logger.debug(`[buffer-controller]: Finished flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`);
|
7831
|
+
_this7.hls.trigger(Events.BUFFER_FLUSHED, {
|
7832
|
+
type: type
|
7833
|
+
});
|
7834
|
+
},
|
7835
|
+
onError: function onError(error) {
|
7836
|
+
_this7.warn("Failed to remove from " + type + " SourceBuffer", error);
|
7837
|
+
}
|
7724
7838
|
};
|
7725
|
-
|
7726
|
-
|
7839
|
+
};
|
7840
|
+
_proto.onBufferFlushing = function onBufferFlushing(event, data) {
|
7841
|
+
var _this8 = this;
|
7842
|
+
var operationQueue = this.operationQueue;
|
7843
|
+
var type = data.type,
|
7844
|
+
startOffset = data.startOffset,
|
7845
|
+
endOffset = data.endOffset;
|
7846
|
+
if (type) {
|
7847
|
+
operationQueue.append(this.getFlushOp(type, startOffset, endOffset), type);
|
7727
7848
|
} else {
|
7728
|
-
this.getSourceBufferTypes().forEach(function (
|
7729
|
-
operationQueue.append(
|
7849
|
+
this.getSourceBufferTypes().forEach(function (sbType) {
|
7850
|
+
operationQueue.append(_this8.getFlushOp(sbType, startOffset, endOffset), sbType);
|
7730
7851
|
});
|
7731
7852
|
}
|
7732
7853
|
};
|
7733
7854
|
_proto.onFragParsed = function onFragParsed(event, data) {
|
7734
|
-
var
|
7855
|
+
var _this9 = this;
|
7735
7856
|
var frag = data.frag,
|
7736
7857
|
part = data.part;
|
7737
7858
|
var buffersAppendedTo = [];
|
@@ -7753,7 +7874,7 @@
|
|
7753
7874
|
part.stats.buffering.end = now;
|
7754
7875
|
}
|
7755
7876
|
var stats = part ? part.stats : frag.stats;
|
7756
|
-
|
7877
|
+
_this9.hls.trigger(Events.FRAG_BUFFERED, {
|
7757
7878
|
frag: frag,
|
7758
7879
|
part: part,
|
7759
7880
|
stats: stats,
|
@@ -7773,14 +7894,17 @@
|
|
7773
7894
|
// an undefined data.type will mark all buffers as EOS.
|
7774
7895
|
;
|
7775
7896
|
_proto.onBufferEos = function onBufferEos(event, data) {
|
7776
|
-
var
|
7897
|
+
var _this10 = this;
|
7898
|
+
if (data.type === 'video') {
|
7899
|
+
this.unblockAudio();
|
7900
|
+
}
|
7777
7901
|
var ended = this.getSourceBufferTypes().reduce(function (acc, type) {
|
7778
|
-
var sb =
|
7902
|
+
var sb = _this10.sourceBuffer[type];
|
7779
7903
|
if (sb && (!data.type || data.type === type)) {
|
7780
7904
|
sb.ending = true;
|
7781
7905
|
if (!sb.ended) {
|
7782
7906
|
sb.ended = true;
|
7783
|
-
|
7907
|
+
_this10.log(type + " sourceBuffer now EOS");
|
7784
7908
|
}
|
7785
7909
|
}
|
7786
7910
|
return acc && !!(!sb || sb.ended);
|
@@ -7788,35 +7912,42 @@
|
|
7788
7912
|
if (ended) {
|
7789
7913
|
this.log("Queueing mediaSource.endOfStream()");
|
7790
7914
|
this.blockBuffers(function () {
|
7791
|
-
|
7792
|
-
var sb =
|
7915
|
+
_this10.getSourceBufferTypes().forEach(function (type) {
|
7916
|
+
var sb = _this10.sourceBuffer[type];
|
7793
7917
|
if (sb) {
|
7794
7918
|
sb.ending = false;
|
7795
7919
|
}
|
7796
7920
|
});
|
7797
|
-
var mediaSource =
|
7921
|
+
var mediaSource = _this10.mediaSource;
|
7798
7922
|
if (!mediaSource || mediaSource.readyState !== 'open') {
|
7799
7923
|
if (mediaSource) {
|
7800
|
-
|
7924
|
+
_this10.log("Could not call mediaSource.endOfStream(). mediaSource.readyState: " + mediaSource.readyState);
|
7801
7925
|
}
|
7802
7926
|
return;
|
7803
7927
|
}
|
7804
|
-
|
7928
|
+
_this10.log("Calling mediaSource.endOfStream()");
|
7805
7929
|
// Allow this to throw and be caught by the enqueueing function
|
7806
7930
|
mediaSource.endOfStream();
|
7807
7931
|
});
|
7808
7932
|
}
|
7809
7933
|
};
|
7810
7934
|
_proto.onLevelUpdated = function onLevelUpdated(event, _ref) {
|
7935
|
+
var _this11 = this;
|
7811
7936
|
var details = _ref.details;
|
7812
7937
|
if (!details.fragments.length) {
|
7813
7938
|
return;
|
7814
7939
|
}
|
7815
7940
|
this.details = details;
|
7941
|
+
var durationAndRange = this.getDurationAndRange();
|
7942
|
+
if (!durationAndRange) {
|
7943
|
+
return;
|
7944
|
+
}
|
7816
7945
|
if (this.getSourceBufferTypes().length) {
|
7817
|
-
this.blockBuffers(
|
7946
|
+
this.blockBuffers(function () {
|
7947
|
+
return _this11.updateMediaSource(durationAndRange);
|
7948
|
+
});
|
7818
7949
|
} else {
|
7819
|
-
this.
|
7950
|
+
this.updateMediaSource(durationAndRange);
|
7820
7951
|
}
|
7821
7952
|
};
|
7822
7953
|
_proto.trimBuffers = function trimBuffers() {
|
@@ -7849,7 +7980,7 @@
|
|
7849
7980
|
}
|
7850
7981
|
};
|
7851
7982
|
_proto.flushBackBuffer = function flushBackBuffer(currentTime, targetDuration, targetBackBufferPosition) {
|
7852
|
-
var
|
7983
|
+
var _this12 = this;
|
7853
7984
|
var details = this.details,
|
7854
7985
|
sourceBuffer = this.sourceBuffer;
|
7855
7986
|
var sourceBufferTypes = this.getSourceBufferTypes();
|
@@ -7859,20 +7990,20 @@
|
|
7859
7990
|
var buffered = BufferHelper.getBuffered(sb);
|
7860
7991
|
// when target buffer start exceeds actual buffer start
|
7861
7992
|
if (buffered.length > 0 && targetBackBufferPosition > buffered.start(0)) {
|
7862
|
-
|
7993
|
+
_this12.hls.trigger(Events.BACK_BUFFER_REACHED, {
|
7863
7994
|
bufferEnd: targetBackBufferPosition
|
7864
7995
|
});
|
7865
7996
|
|
7866
7997
|
// Support for deprecated event:
|
7867
7998
|
if (details != null && details.live) {
|
7868
|
-
|
7999
|
+
_this12.hls.trigger(Events.LIVE_BACK_BUFFER_REACHED, {
|
7869
8000
|
bufferEnd: targetBackBufferPosition
|
7870
8001
|
});
|
7871
8002
|
} else if (sb.ended && buffered.end(buffered.length - 1) - currentTime < targetDuration * 2) {
|
7872
|
-
|
8003
|
+
_this12.log("Cannot flush " + type + " back buffer while SourceBuffer is in ended state");
|
7873
8004
|
return;
|
7874
8005
|
}
|
7875
|
-
|
8006
|
+
_this12.hls.trigger(Events.BUFFER_FLUSHING, {
|
7876
8007
|
startOffset: 0,
|
7877
8008
|
endOffset: targetBackBufferPosition,
|
7878
8009
|
type: type
|
@@ -7882,7 +8013,7 @@
|
|
7882
8013
|
});
|
7883
8014
|
};
|
7884
8015
|
_proto.flushFrontBuffer = function flushFrontBuffer(currentTime, targetDuration, targetFrontBufferPosition) {
|
7885
|
-
var
|
8016
|
+
var _this13 = this;
|
7886
8017
|
var sourceBuffer = this.sourceBuffer;
|
7887
8018
|
var sourceBufferTypes = this.getSourceBufferTypes();
|
7888
8019
|
sourceBufferTypes.forEach(function (type) {
|
@@ -7900,10 +8031,10 @@
|
|
7900
8031
|
if (targetFrontBufferPosition > bufferStart || currentTime >= bufferStart && currentTime <= bufferEnd) {
|
7901
8032
|
return;
|
7902
8033
|
} else if (sb.ended && currentTime - bufferEnd < 2 * targetDuration) {
|
7903
|
-
|
8034
|
+
_this13.log("Cannot flush " + type + " front buffer while SourceBuffer is in ended state");
|
7904
8035
|
return;
|
7905
8036
|
}
|
7906
|
-
|
8037
|
+
_this13.hls.trigger(Events.BUFFER_FLUSHING, {
|
7907
8038
|
startOffset: bufferStart,
|
7908
8039
|
endOffset: Infinity,
|
7909
8040
|
type: type
|
@@ -7917,9 +8048,9 @@
|
|
7917
8048
|
* 'liveDurationInfinity` is set to `true`
|
7918
8049
|
* More details: https://github.com/video-dev/hls.js/issues/355
|
7919
8050
|
*/;
|
7920
|
-
_proto.
|
8051
|
+
_proto.getDurationAndRange = function getDurationAndRange() {
|
7921
8052
|
if (!this.details || !this.media || !this.mediaSource || this.mediaSource.readyState !== 'open') {
|
7922
|
-
return;
|
8053
|
+
return null;
|
7923
8054
|
}
|
7924
8055
|
var details = this.details,
|
7925
8056
|
hls = this.hls,
|
@@ -7931,25 +8062,40 @@
|
|
7931
8062
|
if (details.live && hls.config.liveDurationInfinity) {
|
7932
8063
|
// Override duration to Infinity
|
7933
8064
|
mediaSource.duration = Infinity;
|
7934
|
-
|
8065
|
+
var len = details.fragments.length;
|
8066
|
+
if (len && details.live && !!mediaSource.setLiveSeekableRange) {
|
8067
|
+
var start = Math.max(0, details.fragments[0].start);
|
8068
|
+
var end = Math.max(start, start + details.totalduration);
|
8069
|
+
return {
|
8070
|
+
duration: Infinity,
|
8071
|
+
start: start,
|
8072
|
+
end: end
|
8073
|
+
};
|
8074
|
+
}
|
8075
|
+
return {
|
8076
|
+
duration: Infinity
|
8077
|
+
};
|
7935
8078
|
} else if (levelDuration > msDuration && levelDuration > mediaDuration || !isFiniteNumber(mediaDuration)) {
|
7936
|
-
|
7937
|
-
|
7938
|
-
|
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;
|
8079
|
+
return {
|
8080
|
+
duration: levelDuration
|
8081
|
+
};
|
7942
8082
|
}
|
8083
|
+
return null;
|
7943
8084
|
};
|
7944
|
-
_proto.
|
7945
|
-
var
|
7946
|
-
|
7947
|
-
|
7948
|
-
if (
|
7949
|
-
|
7950
|
-
|
7951
|
-
|
7952
|
-
|
8085
|
+
_proto.updateMediaSource = function updateMediaSource(_ref2) {
|
8086
|
+
var duration = _ref2.duration,
|
8087
|
+
start = _ref2.start,
|
8088
|
+
end = _ref2.end;
|
8089
|
+
if (!this.media || !this.mediaSource || this.mediaSource.readyState !== 'open') {
|
8090
|
+
return;
|
8091
|
+
}
|
8092
|
+
if (isFiniteNumber(duration)) {
|
8093
|
+
this.log("Updating Media Source duration to " + duration.toFixed(3));
|
8094
|
+
}
|
8095
|
+
this.mediaSource.duration = duration;
|
8096
|
+
if (start !== undefined && end !== undefined) {
|
8097
|
+
this.log("Media Source duration is set to " + this.mediaSource.duration + ". Setting seekable range to " + start + "-" + end + ".");
|
8098
|
+
this.mediaSource.setLiveSeekableRange(start, end);
|
7953
8099
|
}
|
7954
8100
|
};
|
7955
8101
|
_proto.checkPendingTracks = function checkPendingTracks() {
|
@@ -7988,7 +8134,7 @@
|
|
7988
8134
|
}
|
7989
8135
|
};
|
7990
8136
|
_proto.createSourceBuffers = function createSourceBuffers(tracks) {
|
7991
|
-
var
|
8137
|
+
var _this14 = this;
|
7992
8138
|
var sourceBuffer = this.sourceBuffer,
|
7993
8139
|
mediaSource = this.mediaSource;
|
7994
8140
|
if (!mediaSource) {
|
@@ -8004,30 +8150,30 @@
|
|
8004
8150
|
var codec = track.levelCodec || track.codec;
|
8005
8151
|
if (codec) {
|
8006
8152
|
if (trackName.slice(0, 5) === 'audio') {
|
8007
|
-
codec = getCodecCompatibleName(codec,
|
8153
|
+
codec = getCodecCompatibleName(codec, _this14.appendSource);
|
8008
8154
|
}
|
8009
8155
|
}
|
8010
8156
|
var mimeType = track.container + ";codecs=" + codec;
|
8011
|
-
|
8157
|
+
_this14.log("creating sourceBuffer(" + mimeType + ")");
|
8012
8158
|
try {
|
8013
8159
|
var sb = sourceBuffer[trackName] = mediaSource.addSourceBuffer(mimeType);
|
8014
8160
|
var sbName = trackName;
|
8015
|
-
|
8016
|
-
|
8017
|
-
|
8161
|
+
_this14.addBufferListener(sbName, 'updatestart', _this14._onSBUpdateStart);
|
8162
|
+
_this14.addBufferListener(sbName, 'updateend', _this14._onSBUpdateEnd);
|
8163
|
+
_this14.addBufferListener(sbName, 'error', _this14._onSBUpdateError);
|
8018
8164
|
// ManagedSourceBuffer bufferedchange event
|
8019
|
-
if (
|
8020
|
-
|
8165
|
+
if (_this14.appendSource) {
|
8166
|
+
_this14.addBufferListener(sbName, 'bufferedchange', function (type, event) {
|
8021
8167
|
// If media was ejected check for a change. Added ranges are redundant with changes on 'updateend' event.
|
8022
8168
|
var removedRanges = event.removedRanges;
|
8023
8169
|
if (removedRanges != null && removedRanges.length) {
|
8024
|
-
|
8170
|
+
_this14.hls.trigger(Events.BUFFER_FLUSHED, {
|
8025
8171
|
type: trackName
|
8026
8172
|
});
|
8027
8173
|
}
|
8028
8174
|
});
|
8029
8175
|
}
|
8030
|
-
|
8176
|
+
_this14.tracks[trackName] = {
|
8031
8177
|
buffer: sb,
|
8032
8178
|
codec: codec,
|
8033
8179
|
container: track.container,
|
@@ -8036,8 +8182,8 @@
|
|
8036
8182
|
id: track.id
|
8037
8183
|
};
|
8038
8184
|
} catch (err) {
|
8039
|
-
|
8040
|
-
|
8185
|
+
_this14.error("error while trying to add sourceBuffer: " + err.message);
|
8186
|
+
_this14.hls.trigger(Events.ERROR, {
|
8041
8187
|
type: ErrorTypes.MEDIA_ERROR,
|
8042
8188
|
details: ErrorDetails.BUFFER_ADD_CODEC_ERROR,
|
8043
8189
|
fatal: false,
|
@@ -8125,6 +8271,7 @@
|
|
8125
8271
|
}
|
8126
8272
|
return;
|
8127
8273
|
}
|
8274
|
+
sb.ending = false;
|
8128
8275
|
sb.ended = false;
|
8129
8276
|
sb.appendBuffer(data);
|
8130
8277
|
}
|
@@ -8134,7 +8281,7 @@
|
|
8134
8281
|
// upon completion, since we already do it here
|
8135
8282
|
;
|
8136
8283
|
_proto.blockBuffers = function blockBuffers(onUnblocked, buffers) {
|
8137
|
-
var
|
8284
|
+
var _this15 = this;
|
8138
8285
|
if (buffers === void 0) {
|
8139
8286
|
buffers = this.getSourceBufferTypes();
|
8140
8287
|
}
|
@@ -8149,11 +8296,15 @@
|
|
8149
8296
|
var blockingOperations = buffers.map(function (type) {
|
8150
8297
|
return operationQueue.appendBlocker(type);
|
8151
8298
|
});
|
8152
|
-
|
8299
|
+
var audioBlocked = buffers.length > 1 && !!this.blockedAudioAppend;
|
8300
|
+
if (audioBlocked) {
|
8301
|
+
this.unblockAudio();
|
8302
|
+
}
|
8303
|
+
Promise.all(blockingOperations).then(function (result) {
|
8153
8304
|
// logger.debug(`[buffer-controller]: Blocking operation resolved; unblocking ${buffers} SourceBuffer`);
|
8154
8305
|
onUnblocked();
|
8155
|
-
buffers.forEach(function (type) {
|
8156
|
-
var sb =
|
8306
|
+
buffers.forEach(function (type, i) {
|
8307
|
+
var sb = _this15.sourceBuffer[type];
|
8157
8308
|
// Only cycle the queue if the SB is not updating. There's a bug in Chrome which sets the SB updating flag to
|
8158
8309
|
// true when changing the MediaSource duration (https://bugs.chromium.org/p/chromium/issues/detail?id=959359&can=2&q=mediasource%20duration)
|
8159
8310
|
// While this is a workaround, it's probably useful to have around
|
@@ -8196,7 +8347,7 @@
|
|
8196
8347
|
}
|
8197
8348
|
}]);
|
8198
8349
|
return BufferController;
|
8199
|
-
}();
|
8350
|
+
}(Logger);
|
8200
8351
|
function removeSourceChildren(node) {
|
8201
8352
|
var sourceChildren = node.querySelectorAll('source');
|
8202
8353
|
[].slice.call(sourceChildren).forEach(function (source) {
|
@@ -8320,10 +8471,10 @@
|
|
8320
8471
|
var hls = this.hls;
|
8321
8472
|
var maxLevel = this.getMaxLevel(levels.length - 1);
|
8322
8473
|
if (maxLevel !== this.autoLevelCapping) {
|
8323
|
-
logger.log("Setting autoLevelCapping to " + maxLevel + ": " + levels[maxLevel].height + "p@" + levels[maxLevel].bitrate + " for media " + this.mediaWidth + "x" + this.mediaHeight);
|
8474
|
+
hls.logger.log("Setting autoLevelCapping to " + maxLevel + ": " + levels[maxLevel].height + "p@" + levels[maxLevel].bitrate + " for media " + this.mediaWidth + "x" + this.mediaHeight);
|
8324
8475
|
}
|
8325
8476
|
hls.autoLevelCapping = maxLevel;
|
8326
|
-
if (hls.autoLevelCapping > this.autoLevelCapping && this.streamController) {
|
8477
|
+
if (hls.autoLevelEnabled && hls.autoLevelCapping > this.autoLevelCapping && this.streamController) {
|
8327
8478
|
// if auto level capping has a higher value for the previous one, flush the buffer using nextLevelSwitch
|
8328
8479
|
// usually happen when the user go to the fullscreen mode.
|
8329
8480
|
this.streamController.nextLevelSwitch();
|
@@ -8510,10 +8661,10 @@
|
|
8510
8661
|
totalDroppedFrames: droppedFrames
|
8511
8662
|
});
|
8512
8663
|
if (droppedFPS > 0) {
|
8513
|
-
// logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod));
|
8664
|
+
// hls.logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod));
|
8514
8665
|
if (currentDropped > hls.config.fpsDroppedMonitoringThreshold * currentDecoded) {
|
8515
8666
|
var currentLevel = hls.currentLevel;
|
8516
|
-
logger.warn('drop FPS ratio greater than max allowed value for currentLevel: ' + currentLevel);
|
8667
|
+
hls.logger.warn('drop FPS ratio greater than max allowed value for currentLevel: ' + currentLevel);
|
8517
8668
|
if (currentLevel > 0 && (hls.autoLevelCapping === -1 || hls.autoLevelCapping >= currentLevel)) {
|
8518
8669
|
currentLevel = currentLevel - 1;
|
8519
8670
|
hls.trigger(Events.FPS_DROP_LEVEL_CAPPING, {
|
@@ -8547,26 +8698,28 @@
|
|
8547
8698
|
}();
|
8548
8699
|
|
8549
8700
|
var PATHWAY_PENALTY_DURATION_MS = 300000;
|
8550
|
-
var ContentSteeringController = /*#__PURE__*/function () {
|
8701
|
+
var ContentSteeringController = /*#__PURE__*/function (_Logger) {
|
8702
|
+
_inheritsLoose(ContentSteeringController, _Logger);
|
8551
8703
|
function ContentSteeringController(hls) {
|
8552
|
-
|
8553
|
-
this.
|
8554
|
-
|
8555
|
-
|
8556
|
-
|
8557
|
-
|
8558
|
-
|
8559
|
-
|
8560
|
-
|
8561
|
-
|
8562
|
-
|
8563
|
-
|
8564
|
-
|
8565
|
-
|
8566
|
-
|
8567
|
-
|
8568
|
-
|
8569
|
-
|
8704
|
+
var _this;
|
8705
|
+
_this = _Logger.call(this, 'content-steering', hls.logger) || this;
|
8706
|
+
_this.hls = void 0;
|
8707
|
+
_this.loader = null;
|
8708
|
+
_this.uri = null;
|
8709
|
+
_this.pathwayId = '.';
|
8710
|
+
_this.pathwayPriority = null;
|
8711
|
+
_this.timeToLoad = 300;
|
8712
|
+
_this.reloadTimer = -1;
|
8713
|
+
_this.updated = 0;
|
8714
|
+
_this.started = false;
|
8715
|
+
_this.enabled = true;
|
8716
|
+
_this.levels = null;
|
8717
|
+
_this.audioTracks = null;
|
8718
|
+
_this.subtitleTracks = null;
|
8719
|
+
_this.penalizedPathways = {};
|
8720
|
+
_this.hls = hls;
|
8721
|
+
_this.registerListeners();
|
8722
|
+
return _this;
|
8570
8723
|
}
|
8571
8724
|
var _proto = ContentSteeringController.prototype;
|
8572
8725
|
_proto.registerListeners = function registerListeners() {
|
@@ -8687,7 +8840,7 @@
|
|
8687
8840
|
errorAction.resolved = this.pathwayId !== errorPathway;
|
8688
8841
|
}
|
8689
8842
|
if (!errorAction.resolved) {
|
8690
|
-
|
8843
|
+
this.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));
|
8691
8844
|
}
|
8692
8845
|
}
|
8693
8846
|
};
|
@@ -8767,7 +8920,7 @@
|
|
8767
8920
|
return defaultPathway;
|
8768
8921
|
};
|
8769
8922
|
_proto.clonePathways = function clonePathways(pathwayClones) {
|
8770
|
-
var
|
8923
|
+
var _this2 = this;
|
8771
8924
|
var levels = this.levels;
|
8772
8925
|
if (!levels) {
|
8773
8926
|
return;
|
@@ -8783,7 +8936,7 @@
|
|
8783
8936
|
})) {
|
8784
8937
|
return;
|
8785
8938
|
}
|
8786
|
-
var clonedVariants =
|
8939
|
+
var clonedVariants = _this2.getLevelsForPathway(baseId).map(function (baseLevel) {
|
8787
8940
|
var attributes = new AttrList(baseLevel.attrs);
|
8788
8941
|
attributes['PATHWAY-ID'] = cloneId;
|
8789
8942
|
var clonedAudioGroupId = attributes.AUDIO && attributes.AUDIO + "_clone_" + cloneId;
|
@@ -8820,12 +8973,12 @@
|
|
8820
8973
|
return clonedLevel;
|
8821
8974
|
});
|
8822
8975
|
levels.push.apply(levels, clonedVariants);
|
8823
|
-
cloneRenditionGroups(
|
8824
|
-
cloneRenditionGroups(
|
8976
|
+
cloneRenditionGroups(_this2.audioTracks, audioGroupCloneMap, uriReplacement, cloneId);
|
8977
|
+
cloneRenditionGroups(_this2.subtitleTracks, subtitleGroupCloneMap, uriReplacement, cloneId);
|
8825
8978
|
});
|
8826
8979
|
};
|
8827
8980
|
_proto.loadSteeringManifest = function loadSteeringManifest(uri) {
|
8828
|
-
var
|
8981
|
+
var _this3 = this;
|
8829
8982
|
var config = this.hls.config;
|
8830
8983
|
var Loader = config.loader;
|
8831
8984
|
if (this.loader) {
|
@@ -8860,87 +9013,87 @@
|
|
8860
9013
|
};
|
8861
9014
|
var callbacks = {
|
8862
9015
|
onSuccess: function onSuccess(response, stats, context, networkDetails) {
|
8863
|
-
|
9016
|
+
_this3.log("Loaded steering manifest: \"" + url + "\"");
|
8864
9017
|
var steeringData = response.data;
|
8865
|
-
if (steeringData.VERSION !== 1) {
|
8866
|
-
|
9018
|
+
if ((steeringData == null ? void 0 : steeringData.VERSION) !== 1) {
|
9019
|
+
_this3.log("Steering VERSION " + steeringData.VERSION + " not supported!");
|
8867
9020
|
return;
|
8868
9021
|
}
|
8869
|
-
|
8870
|
-
|
9022
|
+
_this3.updated = performance.now();
|
9023
|
+
_this3.timeToLoad = steeringData.TTL;
|
8871
9024
|
var reloadUri = steeringData['RELOAD-URI'],
|
8872
9025
|
pathwayClones = steeringData['PATHWAY-CLONES'],
|
8873
9026
|
pathwayPriority = steeringData['PATHWAY-PRIORITY'];
|
8874
9027
|
if (reloadUri) {
|
8875
9028
|
try {
|
8876
|
-
|
9029
|
+
_this3.uri = new self.URL(reloadUri, url).href;
|
8877
9030
|
} catch (error) {
|
8878
|
-
|
8879
|
-
|
9031
|
+
_this3.enabled = false;
|
9032
|
+
_this3.log("Failed to parse Steering Manifest RELOAD-URI: " + reloadUri);
|
8880
9033
|
return;
|
8881
9034
|
}
|
8882
9035
|
}
|
8883
|
-
|
9036
|
+
_this3.scheduleRefresh(_this3.uri || context.url);
|
8884
9037
|
if (pathwayClones) {
|
8885
|
-
|
9038
|
+
_this3.clonePathways(pathwayClones);
|
8886
9039
|
}
|
8887
9040
|
var loadedSteeringData = {
|
8888
9041
|
steeringManifest: steeringData,
|
8889
9042
|
url: url.toString()
|
8890
9043
|
};
|
8891
|
-
|
9044
|
+
_this3.hls.trigger(Events.STEERING_MANIFEST_LOADED, loadedSteeringData);
|
8892
9045
|
if (pathwayPriority) {
|
8893
|
-
|
9046
|
+
_this3.updatePathwayPriority(pathwayPriority);
|
8894
9047
|
}
|
8895
9048
|
},
|
8896
9049
|
onError: function onError(error, context, networkDetails, stats) {
|
8897
|
-
|
8898
|
-
|
9050
|
+
_this3.log("Error loading steering manifest: " + error.code + " " + error.text + " (" + context.url + ")");
|
9051
|
+
_this3.stopLoad();
|
8899
9052
|
if (error.code === 410) {
|
8900
|
-
|
8901
|
-
|
9053
|
+
_this3.enabled = false;
|
9054
|
+
_this3.log("Steering manifest " + context.url + " no longer available");
|
8902
9055
|
return;
|
8903
9056
|
}
|
8904
|
-
var ttl =
|
9057
|
+
var ttl = _this3.timeToLoad * 1000;
|
8905
9058
|
if (error.code === 429) {
|
8906
|
-
var loader =
|
9059
|
+
var loader = _this3.loader;
|
8907
9060
|
if (typeof (loader == null ? void 0 : loader.getResponseHeader) === 'function') {
|
8908
9061
|
var retryAfter = loader.getResponseHeader('Retry-After');
|
8909
9062
|
if (retryAfter) {
|
8910
9063
|
ttl = parseFloat(retryAfter) * 1000;
|
8911
9064
|
}
|
8912
9065
|
}
|
8913
|
-
|
9066
|
+
_this3.log("Steering manifest " + context.url + " rate limited");
|
8914
9067
|
return;
|
8915
9068
|
}
|
8916
|
-
|
9069
|
+
_this3.scheduleRefresh(_this3.uri || context.url, ttl);
|
8917
9070
|
},
|
8918
9071
|
onTimeout: function onTimeout(stats, context, networkDetails) {
|
8919
|
-
|
8920
|
-
|
9072
|
+
_this3.log("Timeout loading steering manifest (" + context.url + ")");
|
9073
|
+
_this3.scheduleRefresh(_this3.uri || context.url);
|
8921
9074
|
}
|
8922
9075
|
};
|
8923
9076
|
this.log("Requesting steering manifest: " + url);
|
8924
9077
|
this.loader.load(context, loaderConfig, callbacks);
|
8925
9078
|
};
|
8926
9079
|
_proto.scheduleRefresh = function scheduleRefresh(uri, ttlMs) {
|
8927
|
-
var
|
9080
|
+
var _this4 = this;
|
8928
9081
|
if (ttlMs === void 0) {
|
8929
9082
|
ttlMs = this.timeToLoad * 1000;
|
8930
9083
|
}
|
8931
9084
|
this.clearTimeout();
|
8932
9085
|
this.reloadTimer = self.setTimeout(function () {
|
8933
|
-
var
|
8934
|
-
var media = (
|
9086
|
+
var _this4$hls;
|
9087
|
+
var media = (_this4$hls = _this4.hls) == null ? void 0 : _this4$hls.media;
|
8935
9088
|
if (media && !media.ended) {
|
8936
|
-
|
9089
|
+
_this4.loadSteeringManifest(uri);
|
8937
9090
|
return;
|
8938
9091
|
}
|
8939
|
-
|
9092
|
+
_this4.scheduleRefresh(uri, _this4.timeToLoad * 1000);
|
8940
9093
|
}, ttlMs);
|
8941
9094
|
};
|
8942
9095
|
return ContentSteeringController;
|
8943
|
-
}();
|
9096
|
+
}(Logger);
|
8944
9097
|
function cloneRenditionGroups(tracks, groupCloneMap, uriReplacement, cloneId) {
|
8945
9098
|
if (!tracks) {
|
8946
9099
|
return;
|
@@ -9776,7 +9929,7 @@
|
|
9776
9929
|
});
|
9777
9930
|
function timelineConfig() {
|
9778
9931
|
return {
|
9779
|
-
cueHandler:
|
9932
|
+
cueHandler: HevcVideoParser,
|
9780
9933
|
// used by timeline-controller
|
9781
9934
|
enableWebVTT: false,
|
9782
9935
|
// used by timeline-controller
|
@@ -9807,7 +9960,7 @@
|
|
9807
9960
|
/**
|
9808
9961
|
* @ignore
|
9809
9962
|
*/
|
9810
|
-
function mergeConfig(defaultConfig, userConfig) {
|
9963
|
+
function mergeConfig(defaultConfig, userConfig, logger) {
|
9811
9964
|
if ((userConfig.liveSyncDurationCount || userConfig.liveMaxLatencyDurationCount) && (userConfig.liveSyncDuration || userConfig.liveMaxLatencyDuration)) {
|
9812
9965
|
throw new Error("Illegal hls.js config: don't mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration");
|
9813
9966
|
}
|
@@ -9877,7 +10030,7 @@
|
|
9877
10030
|
/**
|
9878
10031
|
* @ignore
|
9879
10032
|
*/
|
9880
|
-
function enableStreamingMode(config) {
|
10033
|
+
function enableStreamingMode(config, logger) {
|
9881
10034
|
var currentLoader = config.loader;
|
9882
10035
|
if (currentLoader !== FetchLoader && currentLoader !== XhrLoader) {
|
9883
10036
|
// If a developer has configured their own loader, respect that choice
|
@@ -9894,12 +10047,11 @@
|
|
9894
10047
|
}
|
9895
10048
|
}
|
9896
10049
|
|
9897
|
-
var chromeOrFirefox;
|
9898
10050
|
var LevelController = /*#__PURE__*/function (_BasePlaylistControll) {
|
9899
10051
|
_inheritsLoose(LevelController, _BasePlaylistControll);
|
9900
10052
|
function LevelController(hls, contentSteeringController) {
|
9901
10053
|
var _this;
|
9902
|
-
_this = _BasePlaylistControll.call(this, hls, '
|
10054
|
+
_this = _BasePlaylistControll.call(this, hls, 'level-controller') || this;
|
9903
10055
|
_this._levels = [];
|
9904
10056
|
_this._firstLevel = -1;
|
9905
10057
|
_this._maxAutoLevel = -1;
|
@@ -9968,21 +10120,13 @@
|
|
9968
10120
|
var videoCodecFound = false;
|
9969
10121
|
var audioCodecFound = false;
|
9970
10122
|
data.levels.forEach(function (levelParsed) {
|
9971
|
-
var
|
10123
|
+
var _videoCodec;
|
9972
10124
|
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
|
9976
10125
|
var audioCodec = levelParsed.audioCodec,
|
9977
10126
|
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
|
-
}
|
9984
10127
|
if (audioCodec) {
|
9985
|
-
|
10128
|
+
// Returns empty and set to undefined for 'mp4a.40.34' with fallback to 'audio/mpeg' SourceBuffer
|
10129
|
+
levelParsed.audioCodec = audioCodec = getCodecCompatibleName(audioCodec, preferManagedMediaSource) || undefined;
|
9986
10130
|
}
|
9987
10131
|
if (((_videoCodec = videoCodec) == null ? void 0 : _videoCodec.indexOf('avc1')) === 0) {
|
9988
10132
|
videoCodec = levelParsed.videoCodec = convertAVC1ToAVCOTI(videoCodec);
|
@@ -10214,7 +10358,12 @@
|
|
10214
10358
|
if (curLevel.fragmentError === 0) {
|
10215
10359
|
curLevel.loadError = 0;
|
10216
10360
|
}
|
10217
|
-
|
10361
|
+
// Ignore matching details populated by loading a Media Playlist directly
|
10362
|
+
var previousDetails = curLevel.details;
|
10363
|
+
if (previousDetails === data.details && previousDetails.advanced) {
|
10364
|
+
previousDetails = undefined;
|
10365
|
+
}
|
10366
|
+
this.playlistLoaded(level, data, previousDetails);
|
10218
10367
|
} else if ((_data$deliveryDirecti2 = data.deliveryDirectives) != null && _data$deliveryDirecti2.skip) {
|
10219
10368
|
// received a delta playlist update that cannot be merged
|
10220
10369
|
details.deltaUpdateFailed = true;
|
@@ -10514,11 +10663,14 @@
|
|
10514
10663
|
* If not found any Fragment, return null
|
10515
10664
|
*/;
|
10516
10665
|
_proto.getBufferedFrag = function getBufferedFrag(position, levelType) {
|
10666
|
+
return this.getFragAtPos(position, levelType, true);
|
10667
|
+
};
|
10668
|
+
_proto.getFragAtPos = function getFragAtPos(position, levelType, buffered) {
|
10517
10669
|
var fragments = this.fragments;
|
10518
10670
|
var keys = Object.keys(fragments);
|
10519
10671
|
for (var i = keys.length; i--;) {
|
10520
10672
|
var fragmentEntity = fragments[keys[i]];
|
10521
|
-
if ((fragmentEntity == null ? void 0 : fragmentEntity.body.type) === levelType && fragmentEntity.buffered) {
|
10673
|
+
if ((fragmentEntity == null ? void 0 : fragmentEntity.body.type) === levelType && (!buffered || fragmentEntity.buffered)) {
|
10522
10674
|
var frag = fragmentEntity.body;
|
10523
10675
|
if (frag.start <= position && position <= frag.end) {
|
10524
10676
|
return frag;
|
@@ -10768,10 +10920,10 @@
|
|
10768
10920
|
};
|
10769
10921
|
};
|
10770
10922
|
_proto.onBufferAppended = function onBufferAppended(event, data) {
|
10771
|
-
var _this3 = this;
|
10772
10923
|
var frag = data.frag,
|
10773
10924
|
part = data.part,
|
10774
|
-
timeRanges = data.timeRanges
|
10925
|
+
timeRanges = data.timeRanges,
|
10926
|
+
type = data.type;
|
10775
10927
|
if (frag.sn === 'initSegment') {
|
10776
10928
|
return;
|
10777
10929
|
}
|
@@ -10785,10 +10937,8 @@
|
|
10785
10937
|
}
|
10786
10938
|
// Store the latest timeRanges loaded in the buffer
|
10787
10939
|
this.timeRanges = timeRanges;
|
10788
|
-
|
10789
|
-
|
10790
|
-
_this3.detectEvictedFragments(elementaryStream, timeRange, playlistType, part);
|
10791
|
-
});
|
10940
|
+
var timeRange = timeRanges[type];
|
10941
|
+
this.detectEvictedFragments(type, timeRange, playlistType, part);
|
10792
10942
|
};
|
10793
10943
|
_proto.onFragBuffered = function onFragBuffered(event, data) {
|
10794
10944
|
this.detectPartialFragments(data);
|
@@ -10802,12 +10952,12 @@
|
|
10802
10952
|
return !!((_this$activePartLists = this.activePartLists[type]) != null && _this$activePartLists.length);
|
10803
10953
|
};
|
10804
10954
|
_proto.removeFragmentsInRange = function removeFragmentsInRange(start, end, playlistType, withGapOnly, unbufferedOnly) {
|
10805
|
-
var
|
10955
|
+
var _this3 = this;
|
10806
10956
|
if (withGapOnly && !this.hasGaps) {
|
10807
10957
|
return;
|
10808
10958
|
}
|
10809
10959
|
Object.keys(this.fragments).forEach(function (key) {
|
10810
|
-
var fragmentEntity =
|
10960
|
+
var fragmentEntity = _this3.fragments[key];
|
10811
10961
|
if (!fragmentEntity) {
|
10812
10962
|
return;
|
10813
10963
|
}
|
@@ -10816,7 +10966,7 @@
|
|
10816
10966
|
return;
|
10817
10967
|
}
|
10818
10968
|
if (frag.start < end && frag.end > start && (fragmentEntity.buffered || unbufferedOnly)) {
|
10819
|
-
|
10969
|
+
_this3.removeFragment(frag);
|
10820
10970
|
}
|
10821
10971
|
});
|
10822
10972
|
};
|
@@ -11129,8 +11279,8 @@
|
|
11129
11279
|
var _frag$decryptdata;
|
11130
11280
|
var byteRangeStart = start;
|
11131
11281
|
var byteRangeEnd = end;
|
11132
|
-
if (frag.sn === 'initSegment' && ((_frag$decryptdata = frag.decryptdata) == null ? void 0 : _frag$decryptdata.method)
|
11133
|
-
// MAP segment encrypted with method 'AES-128', when served with HTTP Range,
|
11282
|
+
if (frag.sn === 'initSegment' && isMethodFullSegmentAesCbc((_frag$decryptdata = frag.decryptdata) == null ? void 0 : _frag$decryptdata.method)) {
|
11283
|
+
// MAP segment encrypted with method 'AES-128' or 'AES-256' (cbc), when served with HTTP Range,
|
11134
11284
|
// has the unencrypted size specified in the range.
|
11135
11285
|
// Ref: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-6.3.6
|
11136
11286
|
var fragmentLen = end - start;
|
@@ -11163,6 +11313,9 @@
|
|
11163
11313
|
(part ? part : frag).stats.aborted = true;
|
11164
11314
|
return new LoadError(errorData);
|
11165
11315
|
}
|
11316
|
+
function isMethodFullSegmentAesCbc(method) {
|
11317
|
+
return method === 'AES-128' || method === 'AES-256';
|
11318
|
+
}
|
11166
11319
|
var LoadError = /*#__PURE__*/function (_Error) {
|
11167
11320
|
_inheritsLoose(LoadError, _Error);
|
11168
11321
|
function LoadError(data) {
|
@@ -11319,6 +11472,8 @@
|
|
11319
11472
|
}
|
11320
11473
|
return this.loadKeyEME(keyInfo, frag);
|
11321
11474
|
case 'AES-128':
|
11475
|
+
case 'AES-256':
|
11476
|
+
case 'AES-256-CTR':
|
11322
11477
|
return this.loadKeyHTTP(keyInfo, frag);
|
11323
11478
|
default:
|
11324
11479
|
return Promise.reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error("Key supplied with unsupported METHOD: \"" + decryptdata.method + "\"")));
|
@@ -11452,13 +11607,17 @@
|
|
11452
11607
|
* we are limiting the task execution per call stack to exactly one, but scheduling/post-poning further
|
11453
11608
|
* task processing on the next main loop iteration (also known as "next tick" in the Node/JS runtime lingo).
|
11454
11609
|
*/
|
11455
|
-
var TaskLoop = /*#__PURE__*/function () {
|
11456
|
-
|
11457
|
-
|
11458
|
-
|
11459
|
-
|
11460
|
-
|
11461
|
-
|
11610
|
+
var TaskLoop = /*#__PURE__*/function (_Logger) {
|
11611
|
+
_inheritsLoose(TaskLoop, _Logger);
|
11612
|
+
function TaskLoop(label, logger) {
|
11613
|
+
var _this;
|
11614
|
+
_this = _Logger.call(this, label, logger) || this;
|
11615
|
+
_this._boundTick = void 0;
|
11616
|
+
_this._tickTimer = null;
|
11617
|
+
_this._tickInterval = null;
|
11618
|
+
_this._tickCallCount = 0;
|
11619
|
+
_this._boundTick = _this.tick.bind(_assertThisInitialized(_this));
|
11620
|
+
return _this;
|
11462
11621
|
}
|
11463
11622
|
var _proto = TaskLoop.prototype;
|
11464
11623
|
_proto.destroy = function destroy() {
|
@@ -11544,7 +11703,7 @@
|
|
11544
11703
|
*/;
|
11545
11704
|
_proto.doTick = function doTick() {};
|
11546
11705
|
return TaskLoop;
|
11547
|
-
}();
|
11706
|
+
}(Logger);
|
11548
11707
|
|
11549
11708
|
var ChunkMetadata = function ChunkMetadata(level, sn, id, size, part, partial) {
|
11550
11709
|
if (size === void 0) {
|
@@ -11730,37 +11889,65 @@
|
|
11730
11889
|
}
|
11731
11890
|
|
11732
11891
|
var AESCrypto = /*#__PURE__*/function () {
|
11733
|
-
function AESCrypto(subtle, iv) {
|
11892
|
+
function AESCrypto(subtle, iv, aesMode) {
|
11734
11893
|
this.subtle = void 0;
|
11735
11894
|
this.aesIV = void 0;
|
11895
|
+
this.aesMode = void 0;
|
11736
11896
|
this.subtle = subtle;
|
11737
11897
|
this.aesIV = iv;
|
11898
|
+
this.aesMode = aesMode;
|
11738
11899
|
}
|
11739
11900
|
var _proto = AESCrypto.prototype;
|
11740
11901
|
_proto.decrypt = function decrypt(data, key) {
|
11741
|
-
|
11742
|
-
|
11743
|
-
|
11744
|
-
|
11902
|
+
switch (this.aesMode) {
|
11903
|
+
case DecrypterAesMode.cbc:
|
11904
|
+
return this.subtle.decrypt({
|
11905
|
+
name: 'AES-CBC',
|
11906
|
+
iv: this.aesIV
|
11907
|
+
}, key, data);
|
11908
|
+
case DecrypterAesMode.ctr:
|
11909
|
+
return this.subtle.decrypt({
|
11910
|
+
name: 'AES-CTR',
|
11911
|
+
counter: this.aesIV,
|
11912
|
+
length: 64
|
11913
|
+
},
|
11914
|
+
//64 : NIST SP800-38A standard suggests that the counter should occupy half of the counter block
|
11915
|
+
key, data);
|
11916
|
+
default:
|
11917
|
+
throw new Error("[AESCrypto] invalid aes mode " + this.aesMode);
|
11918
|
+
}
|
11745
11919
|
};
|
11746
11920
|
return AESCrypto;
|
11747
11921
|
}();
|
11748
11922
|
|
11749
11923
|
var FastAESKey = /*#__PURE__*/function () {
|
11750
|
-
function FastAESKey(subtle, key) {
|
11924
|
+
function FastAESKey(subtle, key, aesMode) {
|
11751
11925
|
this.subtle = void 0;
|
11752
11926
|
this.key = void 0;
|
11927
|
+
this.aesMode = void 0;
|
11753
11928
|
this.subtle = subtle;
|
11754
11929
|
this.key = key;
|
11930
|
+
this.aesMode = aesMode;
|
11755
11931
|
}
|
11756
11932
|
var _proto = FastAESKey.prototype;
|
11757
11933
|
_proto.expandKey = function expandKey() {
|
11934
|
+
var subtleAlgoName = getSubtleAlgoName(this.aesMode);
|
11758
11935
|
return this.subtle.importKey('raw', this.key, {
|
11759
|
-
name:
|
11936
|
+
name: subtleAlgoName
|
11760
11937
|
}, false, ['encrypt', 'decrypt']);
|
11761
11938
|
};
|
11762
11939
|
return FastAESKey;
|
11763
11940
|
}();
|
11941
|
+
function getSubtleAlgoName(aesMode) {
|
11942
|
+
switch (aesMode) {
|
11943
|
+
case DecrypterAesMode.cbc:
|
11944
|
+
return 'AES-CBC';
|
11945
|
+
case DecrypterAesMode.ctr:
|
11946
|
+
return 'AES-CTR';
|
11947
|
+
default:
|
11948
|
+
throw new Error("[FastAESKey] invalid aes mode " + aesMode);
|
11949
|
+
}
|
11950
|
+
}
|
11764
11951
|
|
11765
11952
|
// PKCS7
|
11766
11953
|
function removePadding(array) {
|
@@ -12013,7 +12200,8 @@
|
|
12013
12200
|
this.currentIV = null;
|
12014
12201
|
this.currentResult = null;
|
12015
12202
|
this.useSoftware = void 0;
|
12016
|
-
this.
|
12203
|
+
this.enableSoftwareAES = void 0;
|
12204
|
+
this.enableSoftwareAES = config.enableSoftwareAES;
|
12017
12205
|
this.removePKCS7Padding = removePKCS7Padding;
|
12018
12206
|
// built in decryptor expects PKCS7 padding
|
12019
12207
|
if (removePKCS7Padding) {
|
@@ -12026,9 +12214,7 @@
|
|
12026
12214
|
/* no-op */
|
12027
12215
|
}
|
12028
12216
|
}
|
12029
|
-
|
12030
|
-
this.useSoftware = true;
|
12031
|
-
}
|
12217
|
+
this.useSoftware = this.subtle === null;
|
12032
12218
|
}
|
12033
12219
|
var _proto = Decrypter.prototype;
|
12034
12220
|
_proto.destroy = function destroy() {
|
@@ -12065,11 +12251,11 @@
|
|
12065
12251
|
this.softwareDecrypter = null;
|
12066
12252
|
}
|
12067
12253
|
};
|
12068
|
-
_proto.decrypt = function decrypt(data, key, iv) {
|
12254
|
+
_proto.decrypt = function decrypt(data, key, iv, aesMode) {
|
12069
12255
|
var _this = this;
|
12070
12256
|
if (this.useSoftware) {
|
12071
12257
|
return new Promise(function (resolve, reject) {
|
12072
|
-
_this.softwareDecrypt(new Uint8Array(data), key, iv);
|
12258
|
+
_this.softwareDecrypt(new Uint8Array(data), key, iv, aesMode);
|
12073
12259
|
var decryptResult = _this.flush();
|
12074
12260
|
if (decryptResult) {
|
12075
12261
|
resolve(decryptResult.buffer);
|
@@ -12078,16 +12264,20 @@
|
|
12078
12264
|
}
|
12079
12265
|
});
|
12080
12266
|
}
|
12081
|
-
return this.webCryptoDecrypt(new Uint8Array(data), key, iv);
|
12267
|
+
return this.webCryptoDecrypt(new Uint8Array(data), key, iv, aesMode);
|
12082
12268
|
}
|
12083
12269
|
|
12084
12270
|
// Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached
|
12085
12271
|
// data is handled in the flush() call
|
12086
12272
|
;
|
12087
|
-
_proto.softwareDecrypt = function softwareDecrypt(data, key, iv) {
|
12273
|
+
_proto.softwareDecrypt = function softwareDecrypt(data, key, iv, aesMode) {
|
12088
12274
|
var currentIV = this.currentIV,
|
12089
12275
|
currentResult = this.currentResult,
|
12090
12276
|
remainderData = this.remainderData;
|
12277
|
+
if (aesMode !== DecrypterAesMode.cbc || key.byteLength !== 16) {
|
12278
|
+
logger.warn('SoftwareDecrypt: can only handle AES-128-CBC');
|
12279
|
+
return null;
|
12280
|
+
}
|
12091
12281
|
this.logOnce('JS AES decrypt');
|
12092
12282
|
// The output is staggered during progressive parsing - the current result is cached, and emitted on the next call
|
12093
12283
|
// This is done in order to strip PKCS7 padding, which is found at the end of each segment. We only know we've reached
|
@@ -12120,12 +12310,12 @@
|
|
12120
12310
|
}
|
12121
12311
|
return result;
|
12122
12312
|
};
|
12123
|
-
_proto.webCryptoDecrypt = function webCryptoDecrypt(data, key, iv) {
|
12313
|
+
_proto.webCryptoDecrypt = function webCryptoDecrypt(data, key, iv, aesMode) {
|
12124
12314
|
var _this2 = this;
|
12125
12315
|
var subtle = this.subtle;
|
12126
12316
|
if (this.key !== key || !this.fastAesKey) {
|
12127
12317
|
this.key = key;
|
12128
|
-
this.fastAesKey = new FastAESKey(subtle, key);
|
12318
|
+
this.fastAesKey = new FastAESKey(subtle, key, aesMode);
|
12129
12319
|
}
|
12130
12320
|
return this.fastAesKey.expandKey().then(function (aesKey) {
|
12131
12321
|
// decrypt using web crypto
|
@@ -12133,22 +12323,25 @@
|
|
12133
12323
|
return Promise.reject(new Error('web crypto not initialized'));
|
12134
12324
|
}
|
12135
12325
|
_this2.logOnce('WebCrypto AES decrypt');
|
12136
|
-
var crypto = new AESCrypto(subtle, new Uint8Array(iv));
|
12326
|
+
var crypto = new AESCrypto(subtle, new Uint8Array(iv), aesMode);
|
12137
12327
|
return crypto.decrypt(data.buffer, aesKey);
|
12138
12328
|
}).catch(function (err) {
|
12139
12329
|
logger.warn("[decrypter]: WebCrypto Error, disable WebCrypto API, " + err.name + ": " + err.message);
|
12140
|
-
return _this2.onWebCryptoError(data, key, iv);
|
12330
|
+
return _this2.onWebCryptoError(data, key, iv, aesMode);
|
12141
12331
|
});
|
12142
12332
|
};
|
12143
|
-
_proto.onWebCryptoError = function onWebCryptoError(data, key, iv) {
|
12144
|
-
|
12145
|
-
|
12146
|
-
|
12147
|
-
|
12148
|
-
|
12149
|
-
|
12333
|
+
_proto.onWebCryptoError = function onWebCryptoError(data, key, iv, aesMode) {
|
12334
|
+
var enableSoftwareAES = this.enableSoftwareAES;
|
12335
|
+
if (enableSoftwareAES) {
|
12336
|
+
this.useSoftware = true;
|
12337
|
+
this.logEnabled = true;
|
12338
|
+
this.softwareDecrypt(data, key, iv, aesMode);
|
12339
|
+
var decryptResult = this.flush();
|
12340
|
+
if (decryptResult) {
|
12341
|
+
return decryptResult.buffer;
|
12342
|
+
}
|
12150
12343
|
}
|
12151
|
-
throw new Error('WebCrypto and softwareDecrypt: failed to decrypt data');
|
12344
|
+
throw new Error('WebCrypto' + (enableSoftwareAES ? ' and softwareDecrypt' : '') + ': failed to decrypt data');
|
12152
12345
|
};
|
12153
12346
|
_proto.getValidChunk = function getValidChunk(data) {
|
12154
12347
|
var currentChunk = data;
|
@@ -12202,7 +12395,7 @@
|
|
12202
12395
|
_inheritsLoose(BaseStreamController, _TaskLoop);
|
12203
12396
|
function BaseStreamController(hls, fragmentTracker, keyLoader, logPrefix, playlistType) {
|
12204
12397
|
var _this;
|
12205
|
-
_this = _TaskLoop.call(this) || this;
|
12398
|
+
_this = _TaskLoop.call(this, logPrefix, hls.logger) || this;
|
12206
12399
|
_this.hls = void 0;
|
12207
12400
|
_this.fragPrevious = null;
|
12208
12401
|
_this.fragCurrent = null;
|
@@ -12227,25 +12420,96 @@
|
|
12227
12420
|
_this.startFragRequested = false;
|
12228
12421
|
_this.decrypter = void 0;
|
12229
12422
|
_this.initPTS = [];
|
12230
|
-
_this.
|
12231
|
-
_this.
|
12232
|
-
_this.
|
12233
|
-
|
12234
|
-
|
12423
|
+
_this.buffering = true;
|
12424
|
+
_this.loadingParts = false;
|
12425
|
+
_this.onMediaSeeking = function () {
|
12426
|
+
var _assertThisInitialize = _assertThisInitialized(_this),
|
12427
|
+
config = _assertThisInitialize.config,
|
12428
|
+
fragCurrent = _assertThisInitialize.fragCurrent,
|
12429
|
+
media = _assertThisInitialize.media,
|
12430
|
+
mediaBuffer = _assertThisInitialize.mediaBuffer,
|
12431
|
+
state = _assertThisInitialize.state;
|
12432
|
+
var currentTime = media ? media.currentTime : 0;
|
12433
|
+
var bufferInfo = BufferHelper.bufferInfo(mediaBuffer ? mediaBuffer : media, currentTime, config.maxBufferHole);
|
12434
|
+
_this.log("media seeking to " + (isFiniteNumber(currentTime) ? currentTime.toFixed(3) : currentTime) + ", state: " + state);
|
12435
|
+
if (_this.state === State.ENDED) {
|
12436
|
+
_this.resetLoadingState();
|
12437
|
+
} else if (fragCurrent) {
|
12438
|
+
// Seeking while frag load is in progress
|
12439
|
+
var tolerance = config.maxFragLookUpTolerance;
|
12440
|
+
var fragStartOffset = fragCurrent.start - tolerance;
|
12441
|
+
var fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance;
|
12442
|
+
// if seeking out of buffered range or into new one
|
12443
|
+
if (!bufferInfo.len || fragEndOffset < bufferInfo.start || fragStartOffset > bufferInfo.end) {
|
12444
|
+
var pastFragment = currentTime > fragEndOffset;
|
12445
|
+
// if the seek position is outside the current fragment range
|
12446
|
+
if (currentTime < fragStartOffset || pastFragment) {
|
12447
|
+
if (pastFragment && fragCurrent.loader) {
|
12448
|
+
_this.log('seeking outside of buffer while fragment load in progress, cancel fragment load');
|
12449
|
+
fragCurrent.abortRequests();
|
12450
|
+
_this.resetLoadingState();
|
12451
|
+
}
|
12452
|
+
_this.fragPrevious = null;
|
12453
|
+
}
|
12454
|
+
}
|
12455
|
+
}
|
12456
|
+
if (media) {
|
12457
|
+
// Remove gap fragments
|
12458
|
+
_this.fragmentTracker.removeFragmentsInRange(currentTime, Infinity, _this.playlistType, true);
|
12459
|
+
_this.lastCurrentTime = currentTime;
|
12460
|
+
if (!_this.loadingParts) {
|
12461
|
+
var bufferEnd = Math.max(bufferInfo.end, currentTime);
|
12462
|
+
var shouldLoadParts = _this.shouldLoadParts(_this.getLevelDetails(), bufferEnd);
|
12463
|
+
if (shouldLoadParts) {
|
12464
|
+
_this.log("LL-Part loading ON after seeking to " + currentTime.toFixed(2) + " with buffer @" + bufferEnd.toFixed(2));
|
12465
|
+
_this.loadingParts = shouldLoadParts;
|
12466
|
+
}
|
12467
|
+
}
|
12468
|
+
}
|
12469
|
+
|
12470
|
+
// in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target
|
12471
|
+
if (!_this.loadedmetadata && !bufferInfo.len) {
|
12472
|
+
_this.nextLoadPosition = _this.startPosition = currentTime;
|
12473
|
+
}
|
12474
|
+
|
12475
|
+
// Async tick to speed up processing
|
12476
|
+
_this.tickImmediate();
|
12477
|
+
};
|
12478
|
+
_this.onMediaEnded = function () {
|
12479
|
+
// reset startPosition and lastCurrentTime to restart playback @ stream beginning
|
12480
|
+
_this.startPosition = _this.lastCurrentTime = 0;
|
12481
|
+
if (_this.playlistType === PlaylistLevelType.MAIN) {
|
12482
|
+
_this.hls.trigger(Events.MEDIA_ENDED, {
|
12483
|
+
stalled: false
|
12484
|
+
});
|
12485
|
+
}
|
12486
|
+
};
|
12235
12487
|
_this.playlistType = playlistType;
|
12236
|
-
_this.logPrefix = logPrefix;
|
12237
|
-
_this.log = logger.log.bind(logger, logPrefix + ":");
|
12238
|
-
_this.warn = logger.warn.bind(logger, logPrefix + ":");
|
12239
12488
|
_this.hls = hls;
|
12240
12489
|
_this.fragmentLoader = new FragmentLoader(hls.config);
|
12241
12490
|
_this.keyLoader = keyLoader;
|
12242
12491
|
_this.fragmentTracker = fragmentTracker;
|
12243
12492
|
_this.config = hls.config;
|
12244
12493
|
_this.decrypter = new Decrypter(hls.config);
|
12245
|
-
hls.on(Events.MANIFEST_LOADED, _this.onManifestLoaded, _assertThisInitialized(_this));
|
12246
12494
|
return _this;
|
12247
12495
|
}
|
12248
12496
|
var _proto = BaseStreamController.prototype;
|
12497
|
+
_proto.registerListeners = function registerListeners() {
|
12498
|
+
var hls = this.hls;
|
12499
|
+
hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
|
12500
|
+
hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
|
12501
|
+
hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
|
12502
|
+
hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
|
12503
|
+
hls.on(Events.ERROR, this.onError, this);
|
12504
|
+
};
|
12505
|
+
_proto.unregisterListeners = function unregisterListeners() {
|
12506
|
+
var hls = this.hls;
|
12507
|
+
hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
|
12508
|
+
hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
|
12509
|
+
hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
|
12510
|
+
hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
|
12511
|
+
hls.off(Events.ERROR, this.onError, this);
|
12512
|
+
};
|
12249
12513
|
_proto.doTick = function doTick() {
|
12250
12514
|
this.onTickEnd();
|
12251
12515
|
};
|
@@ -12269,6 +12533,12 @@
|
|
12269
12533
|
this.clearNextTick();
|
12270
12534
|
this.state = State.STOPPED;
|
12271
12535
|
};
|
12536
|
+
_proto.pauseBuffering = function pauseBuffering() {
|
12537
|
+
this.buffering = false;
|
12538
|
+
};
|
12539
|
+
_proto.resumeBuffering = function resumeBuffering() {
|
12540
|
+
this.buffering = true;
|
12541
|
+
};
|
12272
12542
|
_proto._streamEnded = function _streamEnded(bufferInfo, levelDetails) {
|
12273
12543
|
// If playlist is live, there is another buffered range after the current range, nothing buffered, media is detached,
|
12274
12544
|
// of nothing loading/loaded return false
|
@@ -12299,10 +12569,8 @@
|
|
12299
12569
|
};
|
12300
12570
|
_proto.onMediaAttached = function onMediaAttached(event, data) {
|
12301
12571
|
var media = this.media = this.mediaBuffer = data.media;
|
12302
|
-
|
12303
|
-
|
12304
|
-
media.addEventListener('seeking', this.onvseeking);
|
12305
|
-
media.addEventListener('ended', this.onvended);
|
12572
|
+
media.addEventListener('seeking', this.onMediaSeeking);
|
12573
|
+
media.addEventListener('ended', this.onMediaEnded);
|
12306
12574
|
var config = this.config;
|
12307
12575
|
if (this.levels && config.autoStartLoad && this.state === State.STOPPED) {
|
12308
12576
|
this.startLoad(config.startPosition);
|
@@ -12316,10 +12584,9 @@
|
|
12316
12584
|
}
|
12317
12585
|
|
12318
12586
|
// remove video listeners
|
12319
|
-
if (media
|
12320
|
-
media.removeEventListener('seeking', this.
|
12321
|
-
media.removeEventListener('ended', this.
|
12322
|
-
this.onvseeking = this.onvended = null;
|
12587
|
+
if (media) {
|
12588
|
+
media.removeEventListener('seeking', this.onMediaSeeking);
|
12589
|
+
media.removeEventListener('ended', this.onMediaEnded);
|
12323
12590
|
}
|
12324
12591
|
if (this.keyLoader) {
|
12325
12592
|
this.keyLoader.detach();
|
@@ -12329,54 +12596,8 @@
|
|
12329
12596
|
this.fragmentTracker.removeAllFragments();
|
12330
12597
|
this.stopLoad();
|
12331
12598
|
};
|
12332
|
-
_proto.
|
12333
|
-
|
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
|
-
};
|
12599
|
+
_proto.onManifestLoading = function onManifestLoading() {};
|
12600
|
+
_proto.onError = function onError(event, data) {};
|
12380
12601
|
_proto.onManifestLoaded = function onManifestLoaded(event, data) {
|
12381
12602
|
this.startTimeOffset = data.startTimeOffset;
|
12382
12603
|
this.initPTS = [];
|
@@ -12386,7 +12607,7 @@
|
|
12386
12607
|
this.stopLoad();
|
12387
12608
|
_TaskLoop.prototype.onHandlerDestroying.call(this);
|
12388
12609
|
// @ts-ignore
|
12389
|
-
this.hls = null;
|
12610
|
+
this.hls = this.onMediaSeeking = this.onMediaEnded = null;
|
12390
12611
|
};
|
12391
12612
|
_proto.onHandlerDestroyed = function onHandlerDestroyed() {
|
12392
12613
|
this.state = State.STOPPED;
|
@@ -12516,10 +12737,10 @@
|
|
12516
12737
|
var decryptData = frag.decryptdata;
|
12517
12738
|
|
12518
12739
|
// check to see if the payload needs to be decrypted
|
12519
|
-
if (payload && payload.byteLength > 0 && decryptData != null && decryptData.key && decryptData.iv && decryptData.method
|
12740
|
+
if (payload && payload.byteLength > 0 && decryptData != null && decryptData.key && decryptData.iv && isFullSegmentEncryption(decryptData.method)) {
|
12520
12741
|
var startTime = self.performance.now();
|
12521
12742
|
// decrypt init segment data
|
12522
|
-
return _this3.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer).catch(function (err) {
|
12743
|
+
return _this3.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer, getAesModeFromFullSegmentMethod(decryptData.method)).catch(function (err) {
|
12523
12744
|
hls.trigger(Events.ERROR, {
|
12524
12745
|
type: ErrorTypes.MEDIA_ERROR,
|
12525
12746
|
details: ErrorDetails.FRAG_DECRYPT_ERROR,
|
@@ -12632,7 +12853,7 @@
|
|
12632
12853
|
}
|
12633
12854
|
var keyLoadingPromise = null;
|
12634
12855
|
if (frag.encrypted && !((_frag$decryptdata = frag.decryptdata) != null && _frag$decryptdata.key)) {
|
12635
|
-
this.log("Loading key for " + frag.sn + " of [" + details.startSN + "-" + details.endSN + "], " + (this.
|
12856
|
+
this.log("Loading key for " + frag.sn + " of [" + details.startSN + "-" + details.endSN + "], " + (this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track') + " " + frag.level);
|
12636
12857
|
this.state = State.KEY_LOADING;
|
12637
12858
|
this.fragCurrent = frag;
|
12638
12859
|
keyLoadingPromise = this.keyLoader.load(frag).then(function (keyLoadedData) {
|
@@ -12653,8 +12874,16 @@
|
|
12653
12874
|
} else if (!frag.encrypted && details.encryptedFragments.length) {
|
12654
12875
|
this.keyLoader.loadClear(frag, details.encryptedFragments);
|
12655
12876
|
}
|
12877
|
+
var fragPrevious = this.fragPrevious;
|
12878
|
+
if (frag.sn !== 'initSegment' && (!fragPrevious || frag.sn !== fragPrevious.sn)) {
|
12879
|
+
var shouldLoadParts = this.shouldLoadParts(level.details, frag.end);
|
12880
|
+
if (shouldLoadParts !== this.loadingParts) {
|
12881
|
+
this.log("LL-Part loading " + (shouldLoadParts ? 'ON' : 'OFF') + " loading sn " + (fragPrevious == null ? void 0 : fragPrevious.sn) + "->" + frag.sn);
|
12882
|
+
this.loadingParts = shouldLoadParts;
|
12883
|
+
}
|
12884
|
+
}
|
12656
12885
|
targetBufferTime = Math.max(frag.start, targetBufferTime || 0);
|
12657
|
-
if (this.
|
12886
|
+
if (this.loadingParts && frag.sn !== 'initSegment') {
|
12658
12887
|
var partList = details.partList;
|
12659
12888
|
if (partList && progressCallback) {
|
12660
12889
|
if (targetBufferTime > frag.end && details.fragmentHint) {
|
@@ -12663,7 +12892,7 @@
|
|
12663
12892
|
var partIndex = this.getNextPart(partList, frag, targetBufferTime);
|
12664
12893
|
if (partIndex > -1) {
|
12665
12894
|
var part = partList[partIndex];
|
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.
|
12895
|
+
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.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track') + ": " + frag.level + ", target: " + parseFloat(targetBufferTime.toFixed(3)));
|
12667
12896
|
this.nextLoadPosition = part.start + part.duration;
|
12668
12897
|
this.state = State.FRAG_LOADING;
|
12669
12898
|
var _result;
|
@@ -12696,7 +12925,14 @@
|
|
12696
12925
|
}
|
12697
12926
|
}
|
12698
12927
|
}
|
12699
|
-
|
12928
|
+
if (frag.sn !== 'initSegment' && this.loadingParts) {
|
12929
|
+
this.log("LL-Part loading OFF after next part miss @" + targetBufferTime.toFixed(2));
|
12930
|
+
this.loadingParts = false;
|
12931
|
+
} else if (!frag.url) {
|
12932
|
+
// Selected fragment hint for part but not loading parts
|
12933
|
+
return Promise.resolve(null);
|
12934
|
+
}
|
12935
|
+
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)));
|
12700
12936
|
// Don't update nextLoadPosition for fragments which are not buffered
|
12701
12937
|
if (isFiniteNumber(frag.sn) && !this.bitrateTest) {
|
12702
12938
|
this.nextLoadPosition = frag.start + frag.duration;
|
@@ -12798,8 +13034,36 @@
|
|
12798
13034
|
if (part) {
|
12799
13035
|
part.stats.parsing.end = now;
|
12800
13036
|
}
|
13037
|
+
// See if part loading should be disabled/enabled based on buffer and playback position.
|
13038
|
+
if (frag.sn !== 'initSegment') {
|
13039
|
+
var levelDetails = this.getLevelDetails();
|
13040
|
+
var loadingPartsAtEdge = levelDetails && frag.sn > levelDetails.endSN;
|
13041
|
+
var shouldLoadParts = loadingPartsAtEdge || this.shouldLoadParts(levelDetails, frag.end);
|
13042
|
+
if (shouldLoadParts !== this.loadingParts) {
|
13043
|
+
this.log("LL-Part loading " + (shouldLoadParts ? 'ON' : 'OFF') + " after parsing segment ending @" + frag.end.toFixed(2));
|
13044
|
+
this.loadingParts = shouldLoadParts;
|
13045
|
+
}
|
13046
|
+
}
|
12801
13047
|
this.updateLevelTiming(frag, part, level, chunkMeta.partial);
|
12802
13048
|
};
|
13049
|
+
_proto.shouldLoadParts = function shouldLoadParts(details, bufferEnd) {
|
13050
|
+
if (this.config.lowLatencyMode) {
|
13051
|
+
if (!details) {
|
13052
|
+
return this.loadingParts;
|
13053
|
+
}
|
13054
|
+
if (details != null && details.partList) {
|
13055
|
+
var _details$fragmentHint;
|
13056
|
+
// Buffer must be ahead of first part + duration of parts after last segment
|
13057
|
+
// and playback must be at or past segment adjacent to part list
|
13058
|
+
var firstPart = details.partList[0];
|
13059
|
+
var safePartStart = firstPart.end + (((_details$fragmentHint = details.fragmentHint) == null ? void 0 : _details$fragmentHint.duration) || 0);
|
13060
|
+
if (bufferEnd >= safePartStart && this.lastCurrentTime > firstPart.start - firstPart.fragment.duration) {
|
13061
|
+
return true;
|
13062
|
+
}
|
13063
|
+
}
|
13064
|
+
}
|
13065
|
+
return false;
|
13066
|
+
};
|
12803
13067
|
_proto.getCurrentContext = function getCurrentContext(chunkMeta) {
|
12804
13068
|
var levels = this.levels,
|
12805
13069
|
fragCurrent = this.fragCurrent;
|
@@ -12890,7 +13154,7 @@
|
|
12890
13154
|
// Workaround flaw in getting forward buffer when maxBufferHole is smaller than gap at current pos
|
12891
13155
|
if (bufferInfo.len === 0 && bufferInfo.nextStart !== undefined) {
|
12892
13156
|
var bufferedFragAtPos = this.fragmentTracker.getBufferedFrag(pos, type);
|
12893
|
-
if (bufferedFragAtPos && bufferInfo.nextStart
|
13157
|
+
if (bufferedFragAtPos && (bufferInfo.nextStart <= bufferedFragAtPos.end || bufferedFragAtPos.gap)) {
|
12894
13158
|
return BufferHelper.bufferInfo(bufferable, pos, Math.max(bufferInfo.nextStart, maxBufferHole));
|
12895
13159
|
}
|
12896
13160
|
}
|
@@ -12934,7 +13198,8 @@
|
|
12934
13198
|
// find fragment index, contiguous with end of buffer position
|
12935
13199
|
var config = this.config;
|
12936
13200
|
var start = fragments[0].start;
|
12937
|
-
var
|
13201
|
+
var canLoadParts = config.lowLatencyMode && !!levelDetails.partList;
|
13202
|
+
var frag = null;
|
12938
13203
|
if (levelDetails.live) {
|
12939
13204
|
var initialLiveManifestSize = config.initialLiveManifestSize;
|
12940
13205
|
if (fragLen < initialLiveManifestSize) {
|
@@ -12946,6 +13211,10 @@
|
|
12946
13211
|
// Do not load using live logic if the starting frag is requested - we want to use getFragmentAtPosition() so that
|
12947
13212
|
// we get the fragment matching that start time
|
12948
13213
|
if (!levelDetails.PTSKnown && !this.startFragRequested && this.startPosition === -1 || pos < start) {
|
13214
|
+
if (canLoadParts && !this.loadingParts) {
|
13215
|
+
this.log("LL-Part loading ON for initial live fragment");
|
13216
|
+
this.loadingParts = true;
|
13217
|
+
}
|
12949
13218
|
frag = this.getInitialLiveFragment(levelDetails, fragments);
|
12950
13219
|
this.startPosition = this.nextLoadPosition = frag ? this.hls.liveSyncPosition || frag.start : pos;
|
12951
13220
|
}
|
@@ -12956,7 +13225,7 @@
|
|
12956
13225
|
|
12957
13226
|
// If we haven't run into any special cases already, just load the fragment most closely matching the requested position
|
12958
13227
|
if (!frag) {
|
12959
|
-
var end =
|
13228
|
+
var end = this.loadingParts ? levelDetails.partEnd : levelDetails.fragmentEnd;
|
12960
13229
|
frag = this.getFragmentAtPosition(pos, end, levelDetails);
|
12961
13230
|
}
|
12962
13231
|
return this.mapToInitFragWhenRequired(frag);
|
@@ -13070,7 +13339,7 @@
|
|
13070
13339
|
var fragmentHint = levelDetails.fragmentHint;
|
13071
13340
|
var tolerance = config.maxFragLookUpTolerance;
|
13072
13341
|
var partList = levelDetails.partList;
|
13073
|
-
var loadingParts = !!(
|
13342
|
+
var loadingParts = !!(this.loadingParts && partList != null && partList.length && fragmentHint);
|
13074
13343
|
if (loadingParts && fragmentHint && !this.bitrateTest) {
|
13075
13344
|
// Include incomplete fragment with parts at end
|
13076
13345
|
fragments = fragments.concat(fragmentHint);
|
@@ -13257,7 +13526,7 @@
|
|
13257
13526
|
errorAction.resolved = true;
|
13258
13527
|
}
|
13259
13528
|
} else {
|
13260
|
-
|
13529
|
+
this.warn(data.details + " reached or exceeded max retry (" + retryCount + ")");
|
13261
13530
|
return;
|
13262
13531
|
}
|
13263
13532
|
} else if ((errorAction == null ? void 0 : errorAction.action) === NetworkErrorAction.SendAlternateToPenaltyBox) {
|
@@ -13325,7 +13594,9 @@
|
|
13325
13594
|
this.log('Reset loading state');
|
13326
13595
|
this.fragCurrent = null;
|
13327
13596
|
this.fragPrevious = null;
|
13328
|
-
this.state
|
13597
|
+
if (this.state !== State.STOPPED) {
|
13598
|
+
this.state = State.IDLE;
|
13599
|
+
}
|
13329
13600
|
};
|
13330
13601
|
_proto.resetStartWhenNotLoaded = function resetStartWhenNotLoaded(level) {
|
13331
13602
|
// if loadedmetadata is not set, it means that first frag request failed
|
@@ -13647,6 +13918,7 @@
|
|
13647
13918
|
*/
|
13648
13919
|
function getAudioConfig(observer, data, offset, audioCodec) {
|
13649
13920
|
var adtsObjectType;
|
13921
|
+
var originalAdtsObjectType;
|
13650
13922
|
var adtsExtensionSamplingIndex;
|
13651
13923
|
var adtsChannelConfig;
|
13652
13924
|
var config;
|
@@ -13654,7 +13926,7 @@
|
|
13654
13926
|
var manifestCodec = audioCodec;
|
13655
13927
|
var adtsSamplingRates = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
|
13656
13928
|
// byte 2
|
13657
|
-
adtsObjectType = ((data[offset + 2] & 0xc0) >>> 6) + 1;
|
13929
|
+
adtsObjectType = originalAdtsObjectType = ((data[offset + 2] & 0xc0) >>> 6) + 1;
|
13658
13930
|
var adtsSamplingIndex = (data[offset + 2] & 0x3c) >>> 2;
|
13659
13931
|
if (adtsSamplingIndex > adtsSamplingRates.length - 1) {
|
13660
13932
|
var error = new Error("invalid ADTS sampling index:" + adtsSamplingIndex);
|
@@ -13671,8 +13943,8 @@
|
|
13671
13943
|
// byte 3
|
13672
13944
|
adtsChannelConfig |= (data[offset + 3] & 0xc0) >>> 6;
|
13673
13945
|
logger.log("manifest codec:" + audioCodec + ", ADTS type:" + adtsObjectType + ", samplingIndex:" + adtsSamplingIndex);
|
13674
|
-
//
|
13675
|
-
if (/firefox/i.test(userAgent)) {
|
13946
|
+
// Firefox and Pale Moon: freq less than 24kHz = AAC SBR (HE-AAC)
|
13947
|
+
if (/firefox|palemoon/i.test(userAgent)) {
|
13676
13948
|
if (adtsSamplingIndex >= 6) {
|
13677
13949
|
adtsObjectType = 5;
|
13678
13950
|
config = new Array(4);
|
@@ -13766,6 +14038,7 @@
|
|
13766
14038
|
samplerate: adtsSamplingRates[adtsSamplingIndex],
|
13767
14039
|
channelCount: adtsChannelConfig,
|
13768
14040
|
codec: 'mp4a.40.' + adtsObjectType,
|
14041
|
+
parsedCodec: 'mp4a.40.' + originalAdtsObjectType,
|
13769
14042
|
manifestCodec: manifestCodec
|
13770
14043
|
};
|
13771
14044
|
}
|
@@ -13820,7 +14093,8 @@
|
|
13820
14093
|
track.channelCount = config.channelCount;
|
13821
14094
|
track.codec = config.codec;
|
13822
14095
|
track.manifestCodec = config.manifestCodec;
|
13823
|
-
|
14096
|
+
track.parsedCodec = config.parsedCodec;
|
14097
|
+
logger.log("parsed codec:" + track.parsedCodec + ", codec:" + track.codec + ", rate:" + config.samplerate + ", channels:" + config.channelCount);
|
13824
14098
|
}
|
13825
14099
|
}
|
13826
14100
|
function getFrameDuration(samplerate) {
|
@@ -14300,72 +14574,176 @@
|
|
14300
14574
|
logger.log(VideoSample.pts + '/' + VideoSample.dts + ':' + VideoSample.debug);
|
14301
14575
|
}
|
14302
14576
|
};
|
14303
|
-
|
14304
|
-
|
14305
|
-
|
14306
|
-
|
14307
|
-
|
14308
|
-
|
14309
|
-
|
14310
|
-
|
14311
|
-
|
14312
|
-
|
14313
|
-
|
14314
|
-
|
14315
|
-
this.bitsAvailable = void 0;
|
14316
|
-
this.data = data;
|
14317
|
-
// the number of bytes left to examine in this.data
|
14318
|
-
this.bytesAvailable = data.byteLength;
|
14319
|
-
// the current word being examined
|
14320
|
-
this.word = 0; // :uint
|
14321
|
-
// the number of bits left to examine in the current word
|
14322
|
-
this.bitsAvailable = 0; // :uint
|
14323
|
-
}
|
14324
|
-
|
14325
|
-
// ():void
|
14326
|
-
var _proto = ExpGolomb.prototype;
|
14327
|
-
_proto.loadWord = function loadWord() {
|
14328
|
-
var data = this.data;
|
14329
|
-
var bytesAvailable = this.bytesAvailable;
|
14330
|
-
var position = data.byteLength - bytesAvailable;
|
14331
|
-
var workingBytes = new Uint8Array(4);
|
14332
|
-
var availableBytes = Math.min(4, bytesAvailable);
|
14333
|
-
if (availableBytes === 0) {
|
14334
|
-
throw new Error('no bytes available');
|
14335
|
-
}
|
14336
|
-
workingBytes.set(data.subarray(position, position + availableBytes));
|
14337
|
-
this.word = new DataView(workingBytes.buffer).getUint32(0);
|
14338
|
-
// track the amount of this.data that has been processed
|
14339
|
-
this.bitsAvailable = availableBytes * 8;
|
14340
|
-
this.bytesAvailable -= availableBytes;
|
14341
|
-
}
|
14577
|
+
_proto.parseNALu = function parseNALu(track, array) {
|
14578
|
+
var len = array.byteLength;
|
14579
|
+
var state = track.naluState || 0;
|
14580
|
+
var lastState = state;
|
14581
|
+
var units = [];
|
14582
|
+
var i = 0;
|
14583
|
+
var value;
|
14584
|
+
var overflow;
|
14585
|
+
var unitType;
|
14586
|
+
var lastUnitStart = -1;
|
14587
|
+
var lastUnitType = 0;
|
14588
|
+
// logger.log('PES:' + Hex.hexDump(array));
|
14342
14589
|
|
14343
|
-
|
14344
|
-
|
14345
|
-
|
14346
|
-
|
14347
|
-
|
14348
|
-
|
14349
|
-
|
14350
|
-
this.bitsAvailable -= count;
|
14351
|
-
} else {
|
14352
|
-
count -= this.bitsAvailable;
|
14353
|
-
skipBytes = count >> 3;
|
14354
|
-
count -= skipBytes << 3;
|
14355
|
-
this.bytesAvailable -= skipBytes;
|
14356
|
-
this.loadWord();
|
14357
|
-
this.word <<= count;
|
14358
|
-
this.bitsAvailable -= count;
|
14590
|
+
if (state === -1) {
|
14591
|
+
// special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet
|
14592
|
+
lastUnitStart = 0;
|
14593
|
+
// NALu type is value read from offset 0
|
14594
|
+
lastUnitType = this.getNALuType(array, 0);
|
14595
|
+
state = 0;
|
14596
|
+
i = 1;
|
14359
14597
|
}
|
14360
|
-
|
14361
|
-
|
14362
|
-
|
14363
|
-
|
14364
|
-
|
14365
|
-
|
14366
|
-
|
14367
|
-
|
14368
|
-
|
14598
|
+
while (i < len) {
|
14599
|
+
value = array[i++];
|
14600
|
+
// optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case
|
14601
|
+
if (!state) {
|
14602
|
+
state = value ? 0 : 1;
|
14603
|
+
continue;
|
14604
|
+
}
|
14605
|
+
if (state === 1) {
|
14606
|
+
state = value ? 0 : 2;
|
14607
|
+
continue;
|
14608
|
+
}
|
14609
|
+
// here we have state either equal to 2 or 3
|
14610
|
+
if (!value) {
|
14611
|
+
state = 3;
|
14612
|
+
} else if (value === 1) {
|
14613
|
+
overflow = i - state - 1;
|
14614
|
+
if (lastUnitStart >= 0) {
|
14615
|
+
var unit = {
|
14616
|
+
data: array.subarray(lastUnitStart, overflow),
|
14617
|
+
type: lastUnitType
|
14618
|
+
};
|
14619
|
+
// logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength);
|
14620
|
+
units.push(unit);
|
14621
|
+
} else {
|
14622
|
+
// lastUnitStart is undefined => this is the first start code found in this PES packet
|
14623
|
+
// first check if start code delimiter is overlapping between 2 PES packets,
|
14624
|
+
// ie it started in last packet (lastState not zero)
|
14625
|
+
// and ended at the beginning of this PES packet (i <= 4 - lastState)
|
14626
|
+
var lastUnit = this.getLastNalUnit(track.samples);
|
14627
|
+
if (lastUnit) {
|
14628
|
+
if (lastState && i <= 4 - lastState) {
|
14629
|
+
// start delimiter overlapping between PES packets
|
14630
|
+
// strip start delimiter bytes from the end of last NAL unit
|
14631
|
+
// check if lastUnit had a state different from zero
|
14632
|
+
if (lastUnit.state) {
|
14633
|
+
// strip last bytes
|
14634
|
+
lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState);
|
14635
|
+
}
|
14636
|
+
}
|
14637
|
+
// If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit.
|
14638
|
+
|
14639
|
+
if (overflow > 0) {
|
14640
|
+
// logger.log('first NALU found with overflow:' + overflow);
|
14641
|
+
lastUnit.data = appendUint8Array(lastUnit.data, array.subarray(0, overflow));
|
14642
|
+
lastUnit.state = 0;
|
14643
|
+
}
|
14644
|
+
}
|
14645
|
+
}
|
14646
|
+
// check if we can read unit type
|
14647
|
+
if (i < len) {
|
14648
|
+
unitType = this.getNALuType(array, i);
|
14649
|
+
// logger.log('find NALU @ offset:' + i + ',type:' + unitType);
|
14650
|
+
lastUnitStart = i;
|
14651
|
+
lastUnitType = unitType;
|
14652
|
+
state = 0;
|
14653
|
+
} else {
|
14654
|
+
// not enough byte to read unit type. let's read it on next PES parsing
|
14655
|
+
state = -1;
|
14656
|
+
}
|
14657
|
+
} else {
|
14658
|
+
state = 0;
|
14659
|
+
}
|
14660
|
+
}
|
14661
|
+
if (lastUnitStart >= 0 && state >= 0) {
|
14662
|
+
var _unit = {
|
14663
|
+
data: array.subarray(lastUnitStart, len),
|
14664
|
+
type: lastUnitType,
|
14665
|
+
state: state
|
14666
|
+
};
|
14667
|
+
units.push(_unit);
|
14668
|
+
// logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state);
|
14669
|
+
}
|
14670
|
+
// no NALu found
|
14671
|
+
if (units.length === 0) {
|
14672
|
+
// append pes.data to previous NAL unit
|
14673
|
+
var _lastUnit = this.getLastNalUnit(track.samples);
|
14674
|
+
if (_lastUnit) {
|
14675
|
+
_lastUnit.data = appendUint8Array(_lastUnit.data, array);
|
14676
|
+
}
|
14677
|
+
}
|
14678
|
+
track.naluState = state;
|
14679
|
+
return units;
|
14680
|
+
};
|
14681
|
+
return BaseVideoParser;
|
14682
|
+
}();
|
14683
|
+
|
14684
|
+
/**
|
14685
|
+
* Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264.
|
14686
|
+
*/
|
14687
|
+
|
14688
|
+
var ExpGolomb = /*#__PURE__*/function () {
|
14689
|
+
function ExpGolomb(data) {
|
14690
|
+
this.data = void 0;
|
14691
|
+
this.bytesAvailable = void 0;
|
14692
|
+
this.word = void 0;
|
14693
|
+
this.bitsAvailable = void 0;
|
14694
|
+
this.data = data;
|
14695
|
+
// the number of bytes left to examine in this.data
|
14696
|
+
this.bytesAvailable = data.byteLength;
|
14697
|
+
// the current word being examined
|
14698
|
+
this.word = 0; // :uint
|
14699
|
+
// the number of bits left to examine in the current word
|
14700
|
+
this.bitsAvailable = 0; // :uint
|
14701
|
+
}
|
14702
|
+
|
14703
|
+
// ():void
|
14704
|
+
var _proto = ExpGolomb.prototype;
|
14705
|
+
_proto.loadWord = function loadWord() {
|
14706
|
+
var data = this.data;
|
14707
|
+
var bytesAvailable = this.bytesAvailable;
|
14708
|
+
var position = data.byteLength - bytesAvailable;
|
14709
|
+
var workingBytes = new Uint8Array(4);
|
14710
|
+
var availableBytes = Math.min(4, bytesAvailable);
|
14711
|
+
if (availableBytes === 0) {
|
14712
|
+
throw new Error('no bytes available');
|
14713
|
+
}
|
14714
|
+
workingBytes.set(data.subarray(position, position + availableBytes));
|
14715
|
+
this.word = new DataView(workingBytes.buffer).getUint32(0);
|
14716
|
+
// track the amount of this.data that has been processed
|
14717
|
+
this.bitsAvailable = availableBytes * 8;
|
14718
|
+
this.bytesAvailable -= availableBytes;
|
14719
|
+
}
|
14720
|
+
|
14721
|
+
// (count:int):void
|
14722
|
+
;
|
14723
|
+
_proto.skipBits = function skipBits(count) {
|
14724
|
+
var skipBytes; // :int
|
14725
|
+
count = Math.min(count, this.bytesAvailable * 8 + this.bitsAvailable);
|
14726
|
+
if (this.bitsAvailable > count) {
|
14727
|
+
this.word <<= count;
|
14728
|
+
this.bitsAvailable -= count;
|
14729
|
+
} else {
|
14730
|
+
count -= this.bitsAvailable;
|
14731
|
+
skipBytes = count >> 3;
|
14732
|
+
count -= skipBytes << 3;
|
14733
|
+
this.bytesAvailable -= skipBytes;
|
14734
|
+
this.loadWord();
|
14735
|
+
this.word <<= count;
|
14736
|
+
this.bitsAvailable -= count;
|
14737
|
+
}
|
14738
|
+
}
|
14739
|
+
|
14740
|
+
// (size:int):uint
|
14741
|
+
;
|
14742
|
+
_proto.readBits = function readBits(size) {
|
14743
|
+
var bits = Math.min(this.bitsAvailable, size); // :uint
|
14744
|
+
var valu = this.word >>> 32 - bits; // :uint
|
14745
|
+
if (size > 32) {
|
14746
|
+
logger.error('Cannot read more than 32 bits at a time');
|
14369
14747
|
}
|
14370
14748
|
this.bitsAvailable -= bits;
|
14371
14749
|
if (this.bitsAvailable > 0) {
|
@@ -14454,189 +14832,6 @@
|
|
14454
14832
|
;
|
14455
14833
|
_proto.readUInt = function readUInt() {
|
14456
14834
|
return this.readBits(32);
|
14457
|
-
}
|
14458
|
-
|
14459
|
-
/**
|
14460
|
-
* Advance the ExpGolomb decoder past a scaling list. The scaling
|
14461
|
-
* list is optionally transmitted as part of a sequence parameter
|
14462
|
-
* set and is not relevant to transmuxing.
|
14463
|
-
* @param count the number of entries in this scaling list
|
14464
|
-
* @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
|
14465
|
-
*/;
|
14466
|
-
_proto.skipScalingList = function skipScalingList(count) {
|
14467
|
-
var lastScale = 8;
|
14468
|
-
var nextScale = 8;
|
14469
|
-
var deltaScale;
|
14470
|
-
for (var j = 0; j < count; j++) {
|
14471
|
-
if (nextScale !== 0) {
|
14472
|
-
deltaScale = this.readEG();
|
14473
|
-
nextScale = (lastScale + deltaScale + 256) % 256;
|
14474
|
-
}
|
14475
|
-
lastScale = nextScale === 0 ? lastScale : nextScale;
|
14476
|
-
}
|
14477
|
-
}
|
14478
|
-
|
14479
|
-
/**
|
14480
|
-
* Read a sequence parameter set and return some interesting video
|
14481
|
-
* properties. A sequence parameter set is the H264 metadata that
|
14482
|
-
* describes the properties of upcoming video frames.
|
14483
|
-
* @returns an object with configuration parsed from the
|
14484
|
-
* sequence parameter set, including the dimensions of the
|
14485
|
-
* associated video frames.
|
14486
|
-
*/;
|
14487
|
-
_proto.readSPS = function readSPS() {
|
14488
|
-
var frameCropLeftOffset = 0;
|
14489
|
-
var frameCropRightOffset = 0;
|
14490
|
-
var frameCropTopOffset = 0;
|
14491
|
-
var frameCropBottomOffset = 0;
|
14492
|
-
var numRefFramesInPicOrderCntCycle;
|
14493
|
-
var scalingListCount;
|
14494
|
-
var i;
|
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);
|
14502
|
-
var skipScalingList = this.skipScalingList.bind(this);
|
14503
|
-
readUByte();
|
14504
|
-
var profileIdc = readUByte(); // profile_idc
|
14505
|
-
readBits(5); // profileCompat constraint_set[0-4]_flag, u(5)
|
14506
|
-
skipBits(3); // reserved_zero_3bits u(3),
|
14507
|
-
readUByte(); // level_idc u(8)
|
14508
|
-
skipUEG(); // seq_parameter_set_id
|
14509
|
-
// some profiles have more optional data we don't need
|
14510
|
-
if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) {
|
14511
|
-
var chromaFormatIdc = readUEG();
|
14512
|
-
if (chromaFormatIdc === 3) {
|
14513
|
-
skipBits(1);
|
14514
|
-
} // separate_colour_plane_flag
|
14515
|
-
|
14516
|
-
skipUEG(); // bit_depth_luma_minus8
|
14517
|
-
skipUEG(); // bit_depth_chroma_minus8
|
14518
|
-
skipBits(1); // qpprime_y_zero_transform_bypass_flag
|
14519
|
-
if (readBoolean()) {
|
14520
|
-
// seq_scaling_matrix_present_flag
|
14521
|
-
scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;
|
14522
|
-
for (i = 0; i < scalingListCount; i++) {
|
14523
|
-
if (readBoolean()) {
|
14524
|
-
// seq_scaling_list_present_flag[ i ]
|
14525
|
-
if (i < 6) {
|
14526
|
-
skipScalingList(16);
|
14527
|
-
} else {
|
14528
|
-
skipScalingList(64);
|
14529
|
-
}
|
14530
|
-
}
|
14531
|
-
}
|
14532
|
-
}
|
14533
|
-
}
|
14534
|
-
skipUEG(); // log2_max_frame_num_minus4
|
14535
|
-
var picOrderCntType = readUEG();
|
14536
|
-
if (picOrderCntType === 0) {
|
14537
|
-
readUEG(); // log2_max_pic_order_cnt_lsb_minus4
|
14538
|
-
} else if (picOrderCntType === 1) {
|
14539
|
-
skipBits(1); // delta_pic_order_always_zero_flag
|
14540
|
-
skipEG(); // offset_for_non_ref_pic
|
14541
|
-
skipEG(); // offset_for_top_to_bottom_field
|
14542
|
-
numRefFramesInPicOrderCntCycle = readUEG();
|
14543
|
-
for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
|
14544
|
-
skipEG();
|
14545
|
-
} // offset_for_ref_frame[ i ]
|
14546
|
-
}
|
14547
|
-
skipUEG(); // max_num_ref_frames
|
14548
|
-
skipBits(1); // gaps_in_frame_num_value_allowed_flag
|
14549
|
-
var picWidthInMbsMinus1 = readUEG();
|
14550
|
-
var picHeightInMapUnitsMinus1 = readUEG();
|
14551
|
-
var frameMbsOnlyFlag = readBits(1);
|
14552
|
-
if (frameMbsOnlyFlag === 0) {
|
14553
|
-
skipBits(1);
|
14554
|
-
} // mb_adaptive_frame_field_flag
|
14555
|
-
|
14556
|
-
skipBits(1); // direct_8x8_inference_flag
|
14557
|
-
if (readBoolean()) {
|
14558
|
-
// frame_cropping_flag
|
14559
|
-
frameCropLeftOffset = readUEG();
|
14560
|
-
frameCropRightOffset = readUEG();
|
14561
|
-
frameCropTopOffset = readUEG();
|
14562
|
-
frameCropBottomOffset = readUEG();
|
14563
|
-
}
|
14564
|
-
var pixelRatio = [1, 1];
|
14565
|
-
if (readBoolean()) {
|
14566
|
-
// vui_parameters_present_flag
|
14567
|
-
if (readBoolean()) {
|
14568
|
-
// aspect_ratio_info_present_flag
|
14569
|
-
var aspectRatioIdc = readUByte();
|
14570
|
-
switch (aspectRatioIdc) {
|
14571
|
-
case 1:
|
14572
|
-
pixelRatio = [1, 1];
|
14573
|
-
break;
|
14574
|
-
case 2:
|
14575
|
-
pixelRatio = [12, 11];
|
14576
|
-
break;
|
14577
|
-
case 3:
|
14578
|
-
pixelRatio = [10, 11];
|
14579
|
-
break;
|
14580
|
-
case 4:
|
14581
|
-
pixelRatio = [16, 11];
|
14582
|
-
break;
|
14583
|
-
case 5:
|
14584
|
-
pixelRatio = [40, 33];
|
14585
|
-
break;
|
14586
|
-
case 6:
|
14587
|
-
pixelRatio = [24, 11];
|
14588
|
-
break;
|
14589
|
-
case 7:
|
14590
|
-
pixelRatio = [20, 11];
|
14591
|
-
break;
|
14592
|
-
case 8:
|
14593
|
-
pixelRatio = [32, 11];
|
14594
|
-
break;
|
14595
|
-
case 9:
|
14596
|
-
pixelRatio = [80, 33];
|
14597
|
-
break;
|
14598
|
-
case 10:
|
14599
|
-
pixelRatio = [18, 11];
|
14600
|
-
break;
|
14601
|
-
case 11:
|
14602
|
-
pixelRatio = [15, 11];
|
14603
|
-
break;
|
14604
|
-
case 12:
|
14605
|
-
pixelRatio = [64, 33];
|
14606
|
-
break;
|
14607
|
-
case 13:
|
14608
|
-
pixelRatio = [160, 99];
|
14609
|
-
break;
|
14610
|
-
case 14:
|
14611
|
-
pixelRatio = [4, 3];
|
14612
|
-
break;
|
14613
|
-
case 15:
|
14614
|
-
pixelRatio = [3, 2];
|
14615
|
-
break;
|
14616
|
-
case 16:
|
14617
|
-
pixelRatio = [2, 1];
|
14618
|
-
break;
|
14619
|
-
case 255:
|
14620
|
-
{
|
14621
|
-
pixelRatio = [readUByte() << 8 | readUByte(), readUByte() << 8 | readUByte()];
|
14622
|
-
break;
|
14623
|
-
}
|
14624
|
-
}
|
14625
|
-
}
|
14626
|
-
}
|
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
14835
|
};
|
14641
14836
|
return ExpGolomb;
|
14642
14837
|
}();
|
@@ -14647,9 +14842,9 @@
|
|
14647
14842
|
return _BaseVideoParser.apply(this, arguments) || this;
|
14648
14843
|
}
|
14649
14844
|
var _proto = AvcVideoParser.prototype;
|
14650
|
-
_proto.
|
14845
|
+
_proto.parsePES = function parsePES(track, textTrack, pes, last, duration) {
|
14651
14846
|
var _this = this;
|
14652
|
-
var units = this.
|
14847
|
+
var units = this.parseNALu(track, pes.data);
|
14653
14848
|
var VideoSample = this.VideoSample;
|
14654
14849
|
var push;
|
14655
14850
|
var spsfound = false;
|
@@ -14674,7 +14869,7 @@
|
|
14674
14869
|
// only check slice type to detect KF in case SPS found in same packet (any keyframe is preceded by SPS ...)
|
14675
14870
|
if (spsfound && data.length > 4) {
|
14676
14871
|
// retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR
|
14677
|
-
var sliceType =
|
14872
|
+
var sliceType = _this.readSliceType(data);
|
14678
14873
|
// 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice
|
14679
14874
|
// SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples.
|
14680
14875
|
// An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice.
|
@@ -14728,8 +14923,7 @@
|
|
14728
14923
|
push = true;
|
14729
14924
|
spsfound = true;
|
14730
14925
|
var sps = unit.data;
|
14731
|
-
var
|
14732
|
-
var config = expGolombDecoder.readSPS();
|
14926
|
+
var config = _this.readSPS(sps);
|
14733
14927
|
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
14928
|
track.width = config.width;
|
14735
14929
|
track.height = config.height;
|
@@ -14785,109 +14979,192 @@
|
|
14785
14979
|
this.VideoSample = null;
|
14786
14980
|
}
|
14787
14981
|
};
|
14788
|
-
_proto.
|
14789
|
-
|
14790
|
-
|
14791
|
-
|
14792
|
-
var
|
14793
|
-
|
14794
|
-
|
14795
|
-
|
14796
|
-
|
14797
|
-
|
14798
|
-
|
14799
|
-
|
14982
|
+
_proto.getNALuType = function getNALuType(data, offset) {
|
14983
|
+
return data[offset] & 0x1f;
|
14984
|
+
};
|
14985
|
+
_proto.readSliceType = function readSliceType(data) {
|
14986
|
+
var eg = new ExpGolomb(data);
|
14987
|
+
// skip NALu type
|
14988
|
+
eg.readUByte();
|
14989
|
+
// discard first_mb_in_slice
|
14990
|
+
eg.readUEG();
|
14991
|
+
// return slice_type
|
14992
|
+
return eg.readUEG();
|
14993
|
+
}
|
14800
14994
|
|
14801
|
-
|
14802
|
-
|
14803
|
-
|
14804
|
-
|
14805
|
-
|
14806
|
-
|
14807
|
-
|
14808
|
-
|
14809
|
-
|
14810
|
-
|
14811
|
-
|
14812
|
-
if (
|
14813
|
-
|
14814
|
-
|
14815
|
-
}
|
14816
|
-
if (state === 1) {
|
14817
|
-
state = value ? 0 : 2;
|
14818
|
-
continue;
|
14995
|
+
/**
|
14996
|
+
* The scaling list is optionally transmitted as part of a sequence parameter
|
14997
|
+
* set and is not relevant to transmuxing.
|
14998
|
+
* @param count the number of entries in this scaling list
|
14999
|
+
* @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
|
15000
|
+
*/;
|
15001
|
+
_proto.skipScalingList = function skipScalingList(count, reader) {
|
15002
|
+
var lastScale = 8;
|
15003
|
+
var nextScale = 8;
|
15004
|
+
var deltaScale;
|
15005
|
+
for (var j = 0; j < count; j++) {
|
15006
|
+
if (nextScale !== 0) {
|
15007
|
+
deltaScale = reader.readEG();
|
15008
|
+
nextScale = (lastScale + deltaScale + 256) % 256;
|
14819
15009
|
}
|
14820
|
-
|
14821
|
-
|
14822
|
-
|
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.
|
15010
|
+
lastScale = nextScale === 0 ? lastScale : nextScale;
|
15011
|
+
}
|
15012
|
+
}
|
14849
15013
|
|
14850
|
-
|
14851
|
-
|
14852
|
-
|
14853
|
-
|
15014
|
+
/**
|
15015
|
+
* Read a sequence parameter set and return some interesting video
|
15016
|
+
* properties. A sequence parameter set is the H264 metadata that
|
15017
|
+
* describes the properties of upcoming video frames.
|
15018
|
+
* @returns an object with configuration parsed from the
|
15019
|
+
* sequence parameter set, including the dimensions of the
|
15020
|
+
* associated video frames.
|
15021
|
+
*/;
|
15022
|
+
_proto.readSPS = function readSPS(sps) {
|
15023
|
+
var eg = new ExpGolomb(sps);
|
15024
|
+
var frameCropLeftOffset = 0;
|
15025
|
+
var frameCropRightOffset = 0;
|
15026
|
+
var frameCropTopOffset = 0;
|
15027
|
+
var frameCropBottomOffset = 0;
|
15028
|
+
var numRefFramesInPicOrderCntCycle;
|
15029
|
+
var scalingListCount;
|
15030
|
+
var i;
|
15031
|
+
var readUByte = eg.readUByte.bind(eg);
|
15032
|
+
var readBits = eg.readBits.bind(eg);
|
15033
|
+
var readUEG = eg.readUEG.bind(eg);
|
15034
|
+
var readBoolean = eg.readBoolean.bind(eg);
|
15035
|
+
var skipBits = eg.skipBits.bind(eg);
|
15036
|
+
var skipEG = eg.skipEG.bind(eg);
|
15037
|
+
var skipUEG = eg.skipUEG.bind(eg);
|
15038
|
+
var skipScalingList = this.skipScalingList.bind(this);
|
15039
|
+
readUByte();
|
15040
|
+
var profileIdc = readUByte(); // profile_idc
|
15041
|
+
readBits(5); // profileCompat constraint_set[0-4]_flag, u(5)
|
15042
|
+
skipBits(3); // reserved_zero_3bits u(3),
|
15043
|
+
readUByte(); // level_idc u(8)
|
15044
|
+
skipUEG(); // seq_parameter_set_id
|
15045
|
+
// some profiles have more optional data we don't need
|
15046
|
+
if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) {
|
15047
|
+
var chromaFormatIdc = readUEG();
|
15048
|
+
if (chromaFormatIdc === 3) {
|
15049
|
+
skipBits(1);
|
15050
|
+
} // separate_colour_plane_flag
|
15051
|
+
|
15052
|
+
skipUEG(); // bit_depth_luma_minus8
|
15053
|
+
skipUEG(); // bit_depth_chroma_minus8
|
15054
|
+
skipBits(1); // qpprime_y_zero_transform_bypass_flag
|
15055
|
+
if (readBoolean()) {
|
15056
|
+
// seq_scaling_matrix_present_flag
|
15057
|
+
scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;
|
15058
|
+
for (i = 0; i < scalingListCount; i++) {
|
15059
|
+
if (readBoolean()) {
|
15060
|
+
// seq_scaling_list_present_flag[ i ]
|
15061
|
+
if (i < 6) {
|
15062
|
+
skipScalingList(16, eg);
|
15063
|
+
} else {
|
15064
|
+
skipScalingList(64, eg);
|
14854
15065
|
}
|
14855
15066
|
}
|
14856
15067
|
}
|
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
15068
|
}
|
14871
15069
|
}
|
14872
|
-
|
14873
|
-
|
14874
|
-
|
14875
|
-
|
14876
|
-
|
14877
|
-
|
14878
|
-
|
14879
|
-
|
15070
|
+
skipUEG(); // log2_max_frame_num_minus4
|
15071
|
+
var picOrderCntType = readUEG();
|
15072
|
+
if (picOrderCntType === 0) {
|
15073
|
+
readUEG(); // log2_max_pic_order_cnt_lsb_minus4
|
15074
|
+
} else if (picOrderCntType === 1) {
|
15075
|
+
skipBits(1); // delta_pic_order_always_zero_flag
|
15076
|
+
skipEG(); // offset_for_non_ref_pic
|
15077
|
+
skipEG(); // offset_for_top_to_bottom_field
|
15078
|
+
numRefFramesInPicOrderCntCycle = readUEG();
|
15079
|
+
for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
|
15080
|
+
skipEG();
|
15081
|
+
} // offset_for_ref_frame[ i ]
|
15082
|
+
}
|
15083
|
+
skipUEG(); // max_num_ref_frames
|
15084
|
+
skipBits(1); // gaps_in_frame_num_value_allowed_flag
|
15085
|
+
var picWidthInMbsMinus1 = readUEG();
|
15086
|
+
var picHeightInMapUnitsMinus1 = readUEG();
|
15087
|
+
var frameMbsOnlyFlag = readBits(1);
|
15088
|
+
if (frameMbsOnlyFlag === 0) {
|
15089
|
+
skipBits(1);
|
15090
|
+
} // mb_adaptive_frame_field_flag
|
15091
|
+
|
15092
|
+
skipBits(1); // direct_8x8_inference_flag
|
15093
|
+
if (readBoolean()) {
|
15094
|
+
// frame_cropping_flag
|
15095
|
+
frameCropLeftOffset = readUEG();
|
15096
|
+
frameCropRightOffset = readUEG();
|
15097
|
+
frameCropTopOffset = readUEG();
|
15098
|
+
frameCropBottomOffset = readUEG();
|
14880
15099
|
}
|
14881
|
-
|
14882
|
-
if (
|
14883
|
-
//
|
14884
|
-
|
14885
|
-
|
14886
|
-
|
15100
|
+
var pixelRatio = [1, 1];
|
15101
|
+
if (readBoolean()) {
|
15102
|
+
// vui_parameters_present_flag
|
15103
|
+
if (readBoolean()) {
|
15104
|
+
// aspect_ratio_info_present_flag
|
15105
|
+
var aspectRatioIdc = readUByte();
|
15106
|
+
switch (aspectRatioIdc) {
|
15107
|
+
case 1:
|
15108
|
+
pixelRatio = [1, 1];
|
15109
|
+
break;
|
15110
|
+
case 2:
|
15111
|
+
pixelRatio = [12, 11];
|
15112
|
+
break;
|
15113
|
+
case 3:
|
15114
|
+
pixelRatio = [10, 11];
|
15115
|
+
break;
|
15116
|
+
case 4:
|
15117
|
+
pixelRatio = [16, 11];
|
15118
|
+
break;
|
15119
|
+
case 5:
|
15120
|
+
pixelRatio = [40, 33];
|
15121
|
+
break;
|
15122
|
+
case 6:
|
15123
|
+
pixelRatio = [24, 11];
|
15124
|
+
break;
|
15125
|
+
case 7:
|
15126
|
+
pixelRatio = [20, 11];
|
15127
|
+
break;
|
15128
|
+
case 8:
|
15129
|
+
pixelRatio = [32, 11];
|
15130
|
+
break;
|
15131
|
+
case 9:
|
15132
|
+
pixelRatio = [80, 33];
|
15133
|
+
break;
|
15134
|
+
case 10:
|
15135
|
+
pixelRatio = [18, 11];
|
15136
|
+
break;
|
15137
|
+
case 11:
|
15138
|
+
pixelRatio = [15, 11];
|
15139
|
+
break;
|
15140
|
+
case 12:
|
15141
|
+
pixelRatio = [64, 33];
|
15142
|
+
break;
|
15143
|
+
case 13:
|
15144
|
+
pixelRatio = [160, 99];
|
15145
|
+
break;
|
15146
|
+
case 14:
|
15147
|
+
pixelRatio = [4, 3];
|
15148
|
+
break;
|
15149
|
+
case 15:
|
15150
|
+
pixelRatio = [3, 2];
|
15151
|
+
break;
|
15152
|
+
case 16:
|
15153
|
+
pixelRatio = [2, 1];
|
15154
|
+
break;
|
15155
|
+
case 255:
|
15156
|
+
{
|
15157
|
+
pixelRatio = [readUByte() << 8 | readUByte(), readUByte() << 8 | readUByte()];
|
15158
|
+
break;
|
15159
|
+
}
|
15160
|
+
}
|
14887
15161
|
}
|
14888
15162
|
}
|
14889
|
-
|
14890
|
-
|
15163
|
+
return {
|
15164
|
+
width: Math.ceil((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2),
|
15165
|
+
height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset),
|
15166
|
+
pixelRatio: pixelRatio
|
15167
|
+
};
|
14891
15168
|
};
|
14892
15169
|
return AvcVideoParser;
|
14893
15170
|
}(BaseVideoParser);
|
@@ -14907,7 +15184,7 @@
|
|
14907
15184
|
}
|
14908
15185
|
var _proto = SampleAesDecrypter.prototype;
|
14909
15186
|
_proto.decryptBuffer = function decryptBuffer(encryptedData) {
|
14910
|
-
return this.decrypter.decrypt(encryptedData, this.keyData.key.buffer, this.keyData.iv.buffer);
|
15187
|
+
return this.decrypter.decrypt(encryptedData, this.keyData.key.buffer, this.keyData.iv.buffer, DecrypterAesMode.cbc);
|
14911
15188
|
}
|
14912
15189
|
|
14913
15190
|
// AAC - encrypt all full 16 bytes blocks starting from offset 16
|
@@ -15026,7 +15303,7 @@
|
|
15026
15303
|
this.observer = observer;
|
15027
15304
|
this.config = config;
|
15028
15305
|
this.typeSupported = typeSupported;
|
15029
|
-
this.videoParser =
|
15306
|
+
this.videoParser = null;
|
15030
15307
|
}
|
15031
15308
|
TSDemuxer.probe = function probe(data) {
|
15032
15309
|
var syncOffset = TSDemuxer.syncOffset(data);
|
@@ -15196,7 +15473,16 @@
|
|
15196
15473
|
case videoPid:
|
15197
15474
|
if (stt) {
|
15198
15475
|
if (videoData && (pes = parsePES(videoData))) {
|
15199
|
-
this.videoParser
|
15476
|
+
if (this.videoParser === null) {
|
15477
|
+
switch (videoTrack.segmentCodec) {
|
15478
|
+
case 'avc':
|
15479
|
+
this.videoParser = new AvcVideoParser();
|
15480
|
+
break;
|
15481
|
+
}
|
15482
|
+
}
|
15483
|
+
if (this.videoParser !== null) {
|
15484
|
+
this.videoParser.parsePES(videoTrack, textTrack, pes, false, this._duration);
|
15485
|
+
}
|
15200
15486
|
}
|
15201
15487
|
videoData = {
|
15202
15488
|
data: [],
|
@@ -15354,8 +15640,17 @@
|
|
15354
15640
|
// try to parse last PES packets
|
15355
15641
|
var pes;
|
15356
15642
|
if (videoData && (pes = parsePES(videoData))) {
|
15357
|
-
this.videoParser
|
15358
|
-
|
15643
|
+
if (this.videoParser === null) {
|
15644
|
+
switch (videoTrack.segmentCodec) {
|
15645
|
+
case 'avc':
|
15646
|
+
this.videoParser = new AvcVideoParser();
|
15647
|
+
break;
|
15648
|
+
}
|
15649
|
+
}
|
15650
|
+
if (this.videoParser !== null) {
|
15651
|
+
this.videoParser.parsePES(videoTrack, textTrack, pes, true, this._duration);
|
15652
|
+
videoTrack.pesData = null;
|
15653
|
+
}
|
15359
15654
|
} else {
|
15360
15655
|
// either avcData null or PES truncated, keep it for next frag parsing
|
15361
15656
|
videoTrack.pesData = videoData;
|
@@ -15657,7 +15952,10 @@
|
|
15657
15952
|
logger.warn('Unsupported EC-3 in M2TS found');
|
15658
15953
|
break;
|
15659
15954
|
case 0x24:
|
15660
|
-
|
15955
|
+
// ITU-T Rec. H.265 and ISO/IEC 23008-2 (HEVC)
|
15956
|
+
{
|
15957
|
+
logger.warn('Unsupported HEVC in M2TS found');
|
15958
|
+
}
|
15661
15959
|
break;
|
15662
15960
|
}
|
15663
15961
|
// move to the next table entry
|
@@ -15885,6 +16183,8 @@
|
|
15885
16183
|
avc1: [],
|
15886
16184
|
// codingname
|
15887
16185
|
avcC: [],
|
16186
|
+
hvc1: [],
|
16187
|
+
hvcC: [],
|
15888
16188
|
btrt: [],
|
15889
16189
|
dinf: [],
|
15890
16190
|
dref: [],
|
@@ -16312,8 +16612,10 @@
|
|
16312
16612
|
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.ac3(track));
|
16313
16613
|
}
|
16314
16614
|
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track));
|
16315
|
-
} else {
|
16615
|
+
} else if (track.segmentCodec === 'avc') {
|
16316
16616
|
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track));
|
16617
|
+
} else {
|
16618
|
+
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.hvc1(track));
|
16317
16619
|
}
|
16318
16620
|
};
|
16319
16621
|
MP4.tkhd = function tkhd(track) {
|
@@ -16451,6 +16753,84 @@
|
|
16451
16753
|
var result = appendUint8Array(MP4.FTYP, movie);
|
16452
16754
|
return result;
|
16453
16755
|
};
|
16756
|
+
MP4.hvc1 = function hvc1(track) {
|
16757
|
+
var ps = track.params;
|
16758
|
+
var units = [track.vps, track.sps, track.pps];
|
16759
|
+
var NALuLengthSize = 4;
|
16760
|
+
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]);
|
16761
|
+
|
16762
|
+
// compute hvcC size in bytes
|
16763
|
+
var length = config.length;
|
16764
|
+
for (var i = 0; i < units.length; i += 1) {
|
16765
|
+
length += 3;
|
16766
|
+
for (var j = 0; j < units[i].length; j += 1) {
|
16767
|
+
length += 2 + units[i][j].length;
|
16768
|
+
}
|
16769
|
+
}
|
16770
|
+
var hvcC = new Uint8Array(length);
|
16771
|
+
hvcC.set(config, 0);
|
16772
|
+
length = config.length;
|
16773
|
+
// append parameter set units: one vps, one or more sps and pps
|
16774
|
+
var iMax = units.length - 1;
|
16775
|
+
for (var _i = 0; _i < units.length; _i += 1) {
|
16776
|
+
hvcC.set(new Uint8Array([32 + _i | (_i === iMax ? 128 : 0), 0x00, units[_i].length]), length);
|
16777
|
+
length += 3;
|
16778
|
+
for (var _j = 0; _j < units[_i].length; _j += 1) {
|
16779
|
+
hvcC.set(new Uint8Array([units[_i][_j].length >> 8, units[_i][_j].length & 255]), length);
|
16780
|
+
length += 2;
|
16781
|
+
hvcC.set(units[_i][_j], length);
|
16782
|
+
length += units[_i][_j].length;
|
16783
|
+
}
|
16784
|
+
}
|
16785
|
+
var hvcc = MP4.box(MP4.types.hvcC, hvcC);
|
16786
|
+
var width = track.width;
|
16787
|
+
var height = track.height;
|
16788
|
+
var hSpacing = track.pixelRatio[0];
|
16789
|
+
var vSpacing = track.pixelRatio[1];
|
16790
|
+
return MP4.box(MP4.types.hvc1, new Uint8Array([0x00, 0x00, 0x00,
|
16791
|
+
// reserved
|
16792
|
+
0x00, 0x00, 0x00,
|
16793
|
+
// reserved
|
16794
|
+
0x00, 0x01,
|
16795
|
+
// data_reference_index
|
16796
|
+
0x00, 0x00,
|
16797
|
+
// pre_defined
|
16798
|
+
0x00, 0x00,
|
16799
|
+
// reserved
|
16800
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
16801
|
+
// pre_defined
|
16802
|
+
width >> 8 & 0xff, width & 0xff,
|
16803
|
+
// width
|
16804
|
+
height >> 8 & 0xff, height & 0xff,
|
16805
|
+
// height
|
16806
|
+
0x00, 0x48, 0x00, 0x00,
|
16807
|
+
// horizresolution
|
16808
|
+
0x00, 0x48, 0x00, 0x00,
|
16809
|
+
// vertresolution
|
16810
|
+
0x00, 0x00, 0x00, 0x00,
|
16811
|
+
// reserved
|
16812
|
+
0x00, 0x01,
|
16813
|
+
// frame_count
|
16814
|
+
0x12, 0x64, 0x61, 0x69, 0x6c,
|
16815
|
+
// dailymotion/hls.js
|
16816
|
+
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,
|
16817
|
+
// compressorname
|
16818
|
+
0x00, 0x18,
|
16819
|
+
// depth = 24
|
16820
|
+
0x11, 0x11]),
|
16821
|
+
// pre_defined = -1
|
16822
|
+
hvcc, MP4.box(MP4.types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80,
|
16823
|
+
// bufferSizeDB
|
16824
|
+
0x00, 0x2d, 0xc6, 0xc0,
|
16825
|
+
// maxBitrate
|
16826
|
+
0x00, 0x2d, 0xc6, 0xc0])),
|
16827
|
+
// avgBitrate
|
16828
|
+
MP4.box(MP4.types.pasp, new Uint8Array([hSpacing >> 24,
|
16829
|
+
// hSpacing
|
16830
|
+
hSpacing >> 16 & 0xff, hSpacing >> 8 & 0xff, hSpacing & 0xff, vSpacing >> 24,
|
16831
|
+
// vSpacing
|
16832
|
+
vSpacing >> 16 & 0xff, vSpacing >> 8 & 0xff, vSpacing & 0xff])));
|
16833
|
+
};
|
16454
16834
|
return MP4;
|
16455
16835
|
}();
|
16456
16836
|
MP4.types = void 0;
|
@@ -16837,9 +17217,9 @@
|
|
16837
17217
|
var foundOverlap = delta < -1;
|
16838
17218
|
if (foundHole || foundOverlap) {
|
16839
17219
|
if (foundHole) {
|
16840
|
-
logger.warn("
|
17220
|
+
logger.warn((track.segmentCodec || '').toUpperCase() + ": " + toMsFromMpegTsClock(delta, true) + " ms (" + delta + "dts) hole between fragments detected at " + timeOffset.toFixed(3));
|
16841
17221
|
} else {
|
16842
|
-
logger.warn("
|
17222
|
+
logger.warn((track.segmentCodec || '').toUpperCase() + ": " + toMsFromMpegTsClock(-delta, true) + " ms (" + delta + "dts) overlapping between fragments detected at " + timeOffset.toFixed(3));
|
16843
17223
|
}
|
16844
17224
|
if (!foundOverlap || nextAvcDts >= inputSamples[0].pts || chromeVersion) {
|
16845
17225
|
firstDTS = nextAvcDts;
|
@@ -16848,12 +17228,24 @@
|
|
16848
17228
|
inputSamples[0].dts = firstDTS;
|
16849
17229
|
inputSamples[0].pts = firstPTS;
|
16850
17230
|
} else {
|
17231
|
+
var isPTSOrderRetained = true;
|
16851
17232
|
for (var _i = 0; _i < inputSamples.length; _i++) {
|
16852
|
-
if (inputSamples[_i].dts > firstPTS) {
|
17233
|
+
if (inputSamples[_i].dts > firstPTS && isPTSOrderRetained) {
|
16853
17234
|
break;
|
16854
17235
|
}
|
17236
|
+
var prevPTS = inputSamples[_i].pts;
|
16855
17237
|
inputSamples[_i].dts -= delta;
|
16856
17238
|
inputSamples[_i].pts -= delta;
|
17239
|
+
|
17240
|
+
// check to see if this sample's PTS order has changed
|
17241
|
+
// relative to the next one
|
17242
|
+
if (_i < inputSamples.length - 1) {
|
17243
|
+
var nextSamplePTS = inputSamples[_i + 1].pts;
|
17244
|
+
var currentSamplePTS = inputSamples[_i].pts;
|
17245
|
+
var currentOrder = nextSamplePTS <= currentSamplePTS;
|
17246
|
+
var prevOrder = nextSamplePTS <= prevPTS;
|
17247
|
+
isPTSOrderRetained = currentOrder == prevOrder;
|
17248
|
+
}
|
16857
17249
|
}
|
16858
17250
|
}
|
16859
17251
|
logger.log("Video: Initial PTS/DTS adjusted: " + toMsFromMpegTsClock(firstPTS, true) + "/" + toMsFromMpegTsClock(firstDTS, true) + ", delta: " + toMsFromMpegTsClock(delta, true) + " ms");
|
@@ -17001,7 +17393,7 @@
|
|
17001
17393
|
}
|
17002
17394
|
}
|
17003
17395
|
}
|
17004
|
-
// next AVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)
|
17396
|
+
// next AVC/HEVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)
|
17005
17397
|
mp4SampleDuration = stretchedLastFrame || !mp4SampleDuration ? averageSampleDuration : mp4SampleDuration;
|
17006
17398
|
this.nextAvcDts = nextAvcDts = lastDTS + mp4SampleDuration;
|
17007
17399
|
this.videoSampleDuration = mp4SampleDuration;
|
@@ -17136,7 +17528,7 @@
|
|
17136
17528
|
logger.warn("[mp4-remuxer]: Injecting " + missing + " audio frame @ " + (nextPts / inputTimeScale).toFixed(3) + "s due to " + Math.round(1000 * delta / inputTimeScale) + " ms gap.");
|
17137
17529
|
for (var j = 0; j < missing; j++) {
|
17138
17530
|
var newStamp = Math.max(nextPts, 0);
|
17139
|
-
var fillFrame = AAC.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);
|
17531
|
+
var fillFrame = AAC.getSilentFrame(track.parsedCodec || track.manifestCodec || track.codec, track.channelCount);
|
17140
17532
|
if (!fillFrame) {
|
17141
17533
|
logger.log('[mp4-remuxer]: Unable to get silent frame for given audio codec; duplicating last frame instead.');
|
17142
17534
|
fillFrame = sample.unit.subarray();
|
@@ -17264,7 +17656,7 @@
|
|
17264
17656
|
// samples count of this segment's duration
|
17265
17657
|
var nbSamples = Math.ceil((endDTS - startDTS) / frameDuration);
|
17266
17658
|
// silent frame
|
17267
|
-
var silentFrame = AAC.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);
|
17659
|
+
var silentFrame = AAC.getSilentFrame(track.parsedCodec || track.manifestCodec || track.codec, track.channelCount);
|
17268
17660
|
logger.warn('[mp4-remuxer]: remux empty Audio');
|
17269
17661
|
// Can't remux if we can't generate a silent frame...
|
17270
17662
|
if (!silentFrame) {
|
@@ -17651,13 +18043,15 @@
|
|
17651
18043
|
duration = transmuxConfig.duration,
|
17652
18044
|
initSegmentData = transmuxConfig.initSegmentData;
|
17653
18045
|
var keyData = getEncryptionType(uintData, decryptdata);
|
17654
|
-
if (keyData && keyData.method
|
18046
|
+
if (keyData && isFullSegmentEncryption(keyData.method)) {
|
17655
18047
|
var decrypter = this.getDecrypter();
|
18048
|
+
var aesMode = getAesModeFromFullSegmentMethod(keyData.method);
|
18049
|
+
|
17656
18050
|
// Software decryption is synchronous; webCrypto is not
|
17657
18051
|
if (decrypter.isSync()) {
|
17658
18052
|
// Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached
|
17659
18053
|
// data is handled in the flush() call
|
17660
|
-
var decryptedData = decrypter.softwareDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer);
|
18054
|
+
var decryptedData = decrypter.softwareDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer, aesMode);
|
17661
18055
|
// For Low-Latency HLS Parts, decrypt in place, since part parsing is expected on push progress
|
17662
18056
|
var loadingParts = chunkMeta.part > -1;
|
17663
18057
|
if (loadingParts) {
|
@@ -17669,7 +18063,7 @@
|
|
17669
18063
|
}
|
17670
18064
|
uintData = new Uint8Array(decryptedData);
|
17671
18065
|
} else {
|
17672
|
-
this.decryptionPromise = decrypter.webCryptoDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer).then(function (decryptedData) {
|
18066
|
+
this.decryptionPromise = decrypter.webCryptoDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer, aesMode).then(function (decryptedData) {
|
17673
18067
|
// Calling push here is important; if flush() is called while this is still resolving, this ensures that
|
17674
18068
|
// the decrypted data has been transmuxed
|
17675
18069
|
var result = _this.push(decryptedData, null, chunkMeta);
|
@@ -18290,7 +18684,7 @@
|
|
18290
18684
|
observer.on(Events.ERROR, forwardMessage);
|
18291
18685
|
|
18292
18686
|
// forward logger events to main thread
|
18293
|
-
var forwardWorkerLogs = function forwardWorkerLogs() {
|
18687
|
+
var forwardWorkerLogs = function forwardWorkerLogs(logger) {
|
18294
18688
|
var _loop = function _loop(logFn) {
|
18295
18689
|
var func = function func(message) {
|
18296
18690
|
forwardMessage('workerLog', {
|
@@ -18311,8 +18705,8 @@
|
|
18311
18705
|
{
|
18312
18706
|
var config = JSON.parse(data.config);
|
18313
18707
|
self.transmuxer = new Transmuxer(observer, data.typeSupported, config, data.vendor, data.id);
|
18314
|
-
enableLogs(config.debug, data.id);
|
18315
|
-
forwardWorkerLogs();
|
18708
|
+
var logger = enableLogs(config.debug, data.id);
|
18709
|
+
forwardWorkerLogs(logger);
|
18316
18710
|
forwardMessage('init', null);
|
18317
18711
|
break;
|
18318
18712
|
}
|
@@ -18486,16 +18880,7 @@
|
|
18486
18880
|
this.observer = new EventEmitter();
|
18487
18881
|
this.observer.on(Events.FRAG_DECRYPTED, forwardMessage);
|
18488
18882
|
this.observer.on(Events.ERROR, forwardMessage);
|
18489
|
-
var
|
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
|
-
};
|
18883
|
+
var m2tsTypeSupported = getM2TSSupportedAudioTypes(config.preferManagedMediaSource);
|
18499
18884
|
|
18500
18885
|
// navigator.vendor is not always available in Web Worker
|
18501
18886
|
// refer to https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/navigator
|
@@ -18752,21 +19137,26 @@
|
|
18752
19137
|
var MAX_START_GAP_JUMP = 2.0;
|
18753
19138
|
var SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1;
|
18754
19139
|
var SKIP_BUFFER_RANGE_START = 0.05;
|
18755
|
-
var GapController = /*#__PURE__*/function () {
|
19140
|
+
var GapController = /*#__PURE__*/function (_Logger) {
|
19141
|
+
_inheritsLoose(GapController, _Logger);
|
18756
19142
|
function GapController(config, media, fragmentTracker, hls) {
|
18757
|
-
|
18758
|
-
this.
|
18759
|
-
|
18760
|
-
|
18761
|
-
|
18762
|
-
|
18763
|
-
|
18764
|
-
|
18765
|
-
|
18766
|
-
|
18767
|
-
|
18768
|
-
|
18769
|
-
|
19143
|
+
var _this;
|
19144
|
+
_this = _Logger.call(this, 'gap-controller', hls.logger) || this;
|
19145
|
+
_this.config = void 0;
|
19146
|
+
_this.media = null;
|
19147
|
+
_this.fragmentTracker = void 0;
|
19148
|
+
_this.hls = void 0;
|
19149
|
+
_this.nudgeRetry = 0;
|
19150
|
+
_this.stallReported = false;
|
19151
|
+
_this.stalled = null;
|
19152
|
+
_this.moved = false;
|
19153
|
+
_this.seeking = false;
|
19154
|
+
_this.ended = 0;
|
19155
|
+
_this.config = config;
|
19156
|
+
_this.media = media;
|
19157
|
+
_this.fragmentTracker = fragmentTracker;
|
19158
|
+
_this.hls = hls;
|
19159
|
+
return _this;
|
18770
19160
|
}
|
18771
19161
|
var _proto = GapController.prototype;
|
18772
19162
|
_proto.destroy = function destroy() {
|
@@ -18781,7 +19171,7 @@
|
|
18781
19171
|
*
|
18782
19172
|
* @param lastCurrentTime - Previously read playhead position
|
18783
19173
|
*/;
|
18784
|
-
_proto.poll = function poll(lastCurrentTime, activeFrag) {
|
19174
|
+
_proto.poll = function poll(lastCurrentTime, activeFrag, levelDetails, state) {
|
18785
19175
|
var config = this.config,
|
18786
19176
|
media = this.media,
|
18787
19177
|
stalled = this.stalled;
|
@@ -18796,6 +19186,7 @@
|
|
18796
19186
|
|
18797
19187
|
// The playhead is moving, no-op
|
18798
19188
|
if (currentTime !== lastCurrentTime) {
|
19189
|
+
this.ended = 0;
|
18799
19190
|
this.moved = true;
|
18800
19191
|
if (!seeking) {
|
18801
19192
|
this.nudgeRetry = 0;
|
@@ -18804,7 +19195,7 @@
|
|
18804
19195
|
// The playhead is now moving, but was previously stalled
|
18805
19196
|
if (this.stallReported) {
|
18806
19197
|
var _stalledDuration = self.performance.now() - stalled;
|
18807
|
-
|
19198
|
+
this.warn("playback not stuck anymore @" + currentTime + ", after " + Math.round(_stalledDuration) + "ms");
|
18808
19199
|
this.stallReported = false;
|
18809
19200
|
}
|
18810
19201
|
this.stalled = null;
|
@@ -18840,7 +19231,6 @@
|
|
18840
19231
|
// Skip start gaps if we haven't played, but the last poll detected the start of a stall
|
18841
19232
|
// The addition poll gives the browser a chance to jump the gap for us
|
18842
19233
|
if (!this.moved && this.stalled !== null) {
|
18843
|
-
var _level$details;
|
18844
19234
|
// There is no playable buffer (seeked, waiting for buffer)
|
18845
19235
|
var isBuffered = bufferInfo.len > 0;
|
18846
19236
|
if (!isBuffered && !nextStart) {
|
@@ -18852,9 +19242,8 @@
|
|
18852
19242
|
// When joining a live stream with audio tracks, account for live playlist window sliding by allowing
|
18853
19243
|
// a larger jump over start gaps caused by the audio-stream-controller buffering a start fragment
|
18854
19244
|
// that begins over 1 target duration after the video start position.
|
18855
|
-
var
|
18856
|
-
var
|
18857
|
-
var maxStartGapJump = isLive ? level.details.targetduration * 2 : MAX_START_GAP_JUMP;
|
19245
|
+
var isLive = !!(levelDetails != null && levelDetails.live);
|
19246
|
+
var maxStartGapJump = isLive ? levelDetails.targetduration * 2 : MAX_START_GAP_JUMP;
|
18858
19247
|
var partialOrGap = this.fragmentTracker.getPartialFragment(currentTime);
|
18859
19248
|
if (startJump > 0 && (startJump <= maxStartGapJump || partialOrGap)) {
|
18860
19249
|
if (!media.paused) {
|
@@ -18872,6 +19261,17 @@
|
|
18872
19261
|
}
|
18873
19262
|
var stalledDuration = tnow - stalled;
|
18874
19263
|
if (!seeking && stalledDuration >= STALL_MINIMUM_DURATION_MS) {
|
19264
|
+
// Dispatch MEDIA_ENDED when media.ended/ended event is not signalled at end of stream
|
19265
|
+
if (state === State.ENDED && !(levelDetails && levelDetails.live) && Math.abs(currentTime - ((levelDetails == null ? void 0 : levelDetails.edge) || 0)) < 1) {
|
19266
|
+
if (stalledDuration < 1000 || this.ended) {
|
19267
|
+
return;
|
19268
|
+
}
|
19269
|
+
this.ended = currentTime;
|
19270
|
+
this.hls.trigger(Events.MEDIA_ENDED, {
|
19271
|
+
stalled: true
|
19272
|
+
});
|
19273
|
+
return;
|
19274
|
+
}
|
18875
19275
|
// Report stalling after trying to fix
|
18876
19276
|
this._reportStall(bufferInfo);
|
18877
19277
|
if (!this.media) {
|
@@ -18913,7 +19313,7 @@
|
|
18913
19313
|
// needs to cross some sort of threshold covering all source-buffers content
|
18914
19314
|
// to start playing properly.
|
18915
19315
|
if ((bufferInfo.len > config.maxBufferHole || bufferInfo.nextStart && bufferInfo.nextStart - currentTime < config.maxBufferHole) && stalledDurationMs > config.highBufferWatchdogPeriod * 1000) {
|
18916
|
-
|
19316
|
+
this.warn('Trying to nudge playhead over buffer-hole');
|
18917
19317
|
// Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
|
18918
19318
|
// We only try to jump the hole if it's under the configured size
|
18919
19319
|
// Reset stalled so to rearm watchdog timer
|
@@ -18935,7 +19335,7 @@
|
|
18935
19335
|
// Report stalled error once
|
18936
19336
|
this.stallReported = true;
|
18937
19337
|
var error = new Error("Playback stalling at @" + media.currentTime + " due to low buffer (" + JSON.stringify(bufferInfo) + ")");
|
18938
|
-
|
19338
|
+
this.warn(error.message);
|
18939
19339
|
hls.trigger(Events.ERROR, {
|
18940
19340
|
type: ErrorTypes.MEDIA_ERROR,
|
18941
19341
|
details: ErrorDetails.BUFFER_STALLED_ERROR,
|
@@ -18999,7 +19399,7 @@
|
|
18999
19399
|
}
|
19000
19400
|
}
|
19001
19401
|
var targetTime = Math.max(startTime + SKIP_BUFFER_RANGE_START, currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS);
|
19002
|
-
|
19402
|
+
this.warn("skipping hole, adjusting currentTime from " + currentTime + " to " + targetTime);
|
19003
19403
|
this.moved = true;
|
19004
19404
|
this.stalled = null;
|
19005
19405
|
media.currentTime = targetTime;
|
@@ -19038,7 +19438,7 @@
|
|
19038
19438
|
var targetTime = currentTime + (nudgeRetry + 1) * config.nudgeOffset;
|
19039
19439
|
// playback stalled in buffered area ... let's nudge currentTime to try to overcome this
|
19040
19440
|
var error = new Error("Nudging 'currentTime' from " + currentTime + " to " + targetTime);
|
19041
|
-
|
19441
|
+
this.warn(error.message);
|
19042
19442
|
media.currentTime = targetTime;
|
19043
19443
|
hls.trigger(Events.ERROR, {
|
19044
19444
|
type: ErrorTypes.MEDIA_ERROR,
|
@@ -19048,7 +19448,7 @@
|
|
19048
19448
|
});
|
19049
19449
|
} else {
|
19050
19450
|
var _error = new Error("Playhead still not moving while enough data buffered @" + currentTime + " after " + config.nudgeMaxRetry + " nudges");
|
19051
|
-
|
19451
|
+
this.error(_error.message);
|
19052
19452
|
hls.trigger(Events.ERROR, {
|
19053
19453
|
type: ErrorTypes.MEDIA_ERROR,
|
19054
19454
|
details: ErrorDetails.BUFFER_STALLED_ERROR,
|
@@ -19058,14 +19458,14 @@
|
|
19058
19458
|
}
|
19059
19459
|
};
|
19060
19460
|
return GapController;
|
19061
|
-
}();
|
19461
|
+
}(Logger);
|
19062
19462
|
|
19063
19463
|
var TICK_INTERVAL = 100; // how often to tick in ms
|
19064
19464
|
var StreamController = /*#__PURE__*/function (_BaseStreamController) {
|
19065
19465
|
_inheritsLoose(StreamController, _BaseStreamController);
|
19066
19466
|
function StreamController(hls, fragmentTracker, keyLoader) {
|
19067
19467
|
var _this;
|
19068
|
-
_this = _BaseStreamController.call(this, hls, fragmentTracker, keyLoader, '
|
19468
|
+
_this = _BaseStreamController.call(this, hls, fragmentTracker, keyLoader, 'stream-controller', PlaylistLevelType.MAIN) || this;
|
19069
19469
|
_this.audioCodecSwap = false;
|
19070
19470
|
_this.gapController = null;
|
19071
19471
|
_this.level = -1;
|
@@ -19073,27 +19473,43 @@
|
|
19073
19473
|
_this.altAudio = false;
|
19074
19474
|
_this.audioOnly = false;
|
19075
19475
|
_this.fragPlaying = null;
|
19076
|
-
_this.onvplaying = null;
|
19077
|
-
_this.onvseeked = null;
|
19078
19476
|
_this.fragLastKbps = 0;
|
19079
19477
|
_this.couldBacktrack = false;
|
19080
19478
|
_this.backtrackFragment = null;
|
19081
19479
|
_this.audioCodecSwitch = false;
|
19082
19480
|
_this.videoBuffer = null;
|
19083
|
-
_this.
|
19481
|
+
_this.onMediaPlaying = function () {
|
19482
|
+
// tick to speed up FRAG_CHANGED triggering
|
19483
|
+
_this.tick();
|
19484
|
+
};
|
19485
|
+
_this.onMediaSeeked = function () {
|
19486
|
+
var media = _this.media;
|
19487
|
+
var currentTime = media ? media.currentTime : null;
|
19488
|
+
if (isFiniteNumber(currentTime)) {
|
19489
|
+
_this.log("Media seeked to " + currentTime.toFixed(3));
|
19490
|
+
}
|
19491
|
+
|
19492
|
+
// If seeked was issued before buffer was appended do not tick immediately
|
19493
|
+
var bufferInfo = _this.getMainFwdBufferInfo();
|
19494
|
+
if (bufferInfo === null || bufferInfo.len === 0) {
|
19495
|
+
_this.warn("Main forward buffer length on \"seeked\" event " + (bufferInfo ? bufferInfo.len : 'empty') + ")");
|
19496
|
+
return;
|
19497
|
+
}
|
19498
|
+
|
19499
|
+
// tick to speed up FRAG_CHANGED triggering
|
19500
|
+
_this.tick();
|
19501
|
+
};
|
19502
|
+
_this.registerListeners();
|
19084
19503
|
return _this;
|
19085
19504
|
}
|
19086
19505
|
var _proto = StreamController.prototype;
|
19087
|
-
_proto.
|
19506
|
+
_proto.registerListeners = function registerListeners() {
|
19507
|
+
_BaseStreamController.prototype.registerListeners.call(this);
|
19088
19508
|
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);
|
19092
19509
|
hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this);
|
19093
19510
|
hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this);
|
19094
19511
|
hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);
|
19095
19512
|
hls.on(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this);
|
19096
|
-
hls.on(Events.ERROR, this.onError, this);
|
19097
19513
|
hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
|
19098
19514
|
hls.on(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
|
19099
19515
|
hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this);
|
@@ -19101,15 +19517,12 @@
|
|
19101
19517
|
hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this);
|
19102
19518
|
hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);
|
19103
19519
|
};
|
19104
|
-
_proto.
|
19520
|
+
_proto.unregisterListeners = function unregisterListeners() {
|
19521
|
+
_BaseStreamController.prototype.unregisterListeners.call(this);
|
19105
19522
|
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);
|
19109
19523
|
hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this);
|
19110
19524
|
hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);
|
19111
19525
|
hls.off(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this);
|
19112
|
-
hls.off(Events.ERROR, this.onError, this);
|
19113
19526
|
hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
|
19114
19527
|
hls.off(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
|
19115
19528
|
hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this);
|
@@ -19118,7 +19531,9 @@
|
|
19118
19531
|
hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this);
|
19119
19532
|
};
|
19120
19533
|
_proto.onHandlerDestroying = function onHandlerDestroying() {
|
19121
|
-
|
19534
|
+
// @ts-ignore
|
19535
|
+
this.onMediaPlaying = this.onMediaSeeked = null;
|
19536
|
+
this.unregisterListeners();
|
19122
19537
|
_BaseStreamController.prototype.onHandlerDestroying.call(this);
|
19123
19538
|
};
|
19124
19539
|
_proto.startLoad = function startLoad(startPosition) {
|
@@ -19210,6 +19625,9 @@
|
|
19210
19625
|
this.checkFragmentChanged();
|
19211
19626
|
};
|
19212
19627
|
_proto.doTickIdle = function doTickIdle() {
|
19628
|
+
if (!this.buffering) {
|
19629
|
+
return;
|
19630
|
+
}
|
19213
19631
|
var hls = this.hls,
|
19214
19632
|
levelLastLoaded = this.levelLastLoaded,
|
19215
19633
|
levels = this.levels,
|
@@ -19433,18 +19851,15 @@
|
|
19433
19851
|
_proto.onMediaAttached = function onMediaAttached(event, data) {
|
19434
19852
|
_BaseStreamController.prototype.onMediaAttached.call(this, event, data);
|
19435
19853
|
var media = data.media;
|
19436
|
-
|
19437
|
-
|
19438
|
-
media.addEventListener('playing', this.onvplaying);
|
19439
|
-
media.addEventListener('seeked', this.onvseeked);
|
19854
|
+
media.addEventListener('playing', this.onMediaPlaying);
|
19855
|
+
media.addEventListener('seeked', this.onMediaSeeked);
|
19440
19856
|
this.gapController = new GapController(this.config, media, this.fragmentTracker, this.hls);
|
19441
19857
|
};
|
19442
19858
|
_proto.onMediaDetaching = function onMediaDetaching() {
|
19443
19859
|
var media = this.media;
|
19444
|
-
if (media
|
19445
|
-
media.removeEventListener('playing', this.
|
19446
|
-
media.removeEventListener('seeked', this.
|
19447
|
-
this.onvplaying = this.onvseeked = null;
|
19860
|
+
if (media) {
|
19861
|
+
media.removeEventListener('playing', this.onMediaPlaying);
|
19862
|
+
media.removeEventListener('seeked', this.onMediaSeeked);
|
19448
19863
|
this.videoBuffer = null;
|
19449
19864
|
}
|
19450
19865
|
this.fragPlaying = null;
|
@@ -19454,27 +19869,6 @@
|
|
19454
19869
|
}
|
19455
19870
|
_BaseStreamController.prototype.onMediaDetaching.call(this);
|
19456
19871
|
};
|
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
|
-
};
|
19478
19872
|
_proto.onManifestLoading = function onManifestLoading() {
|
19479
19873
|
// reset buffer on manifest loading
|
19480
19874
|
this.log('Trigger BUFFER_RESET');
|
@@ -19755,8 +20149,10 @@
|
|
19755
20149
|
}
|
19756
20150
|
if (this.loadedmetadata || !BufferHelper.getBuffered(media).length) {
|
19757
20151
|
// Resolve gaps using the main buffer, whose ranges are the intersections of the A/V sourcebuffers
|
19758
|
-
var
|
19759
|
-
|
20152
|
+
var state = this.state;
|
20153
|
+
var activeFrag = state !== State.IDLE ? this.fragCurrent : null;
|
20154
|
+
var levelDetails = this.getLevelDetails();
|
20155
|
+
gapController.poll(this.lastCurrentTime, activeFrag, levelDetails, state);
|
19760
20156
|
}
|
19761
20157
|
this.lastCurrentTime = media.currentTime;
|
19762
20158
|
};
|
@@ -20122,6 +20518,17 @@
|
|
20122
20518
|
}
|
20123
20519
|
};
|
20124
20520
|
_createClass(StreamController, [{
|
20521
|
+
key: "maxBufferLength",
|
20522
|
+
get: function get() {
|
20523
|
+
var levels = this.levels,
|
20524
|
+
level = this.level;
|
20525
|
+
var levelInfo = levels == null ? void 0 : levels[level];
|
20526
|
+
if (!levelInfo) {
|
20527
|
+
return this.config.maxBufferLength;
|
20528
|
+
}
|
20529
|
+
return this.getMaxBufferLength(levelInfo.maxBitrate);
|
20530
|
+
}
|
20531
|
+
}, {
|
20125
20532
|
key: "nextLevel",
|
20126
20533
|
get: function get() {
|
20127
20534
|
var frag = this.nextBufferedFrag;
|
@@ -20221,9 +20628,12 @@
|
|
20221
20628
|
* The configuration object provided on player instantiation.
|
20222
20629
|
*/
|
20223
20630
|
this.userConfig = void 0;
|
20631
|
+
/**
|
20632
|
+
* The logger functions used by this player instance, configured on player instantiation.
|
20633
|
+
*/
|
20634
|
+
this.logger = void 0;
|
20224
20635
|
this.coreComponents = void 0;
|
20225
20636
|
this.networkControllers = void 0;
|
20226
|
-
this.started = false;
|
20227
20637
|
this._emitter = new EventEmitter();
|
20228
20638
|
this._autoLevelCapping = -1;
|
20229
20639
|
this._maxHdcpLevel = null;
|
@@ -20240,11 +20650,11 @@
|
|
20240
20650
|
this._media = null;
|
20241
20651
|
this.url = null;
|
20242
20652
|
this.triggeringException = void 0;
|
20243
|
-
enableLogs(userConfig.debug || false, 'Hls instance');
|
20244
|
-
var config = this.config = mergeConfig(Hls.DefaultConfig, userConfig);
|
20653
|
+
var logger = this.logger = enableLogs(userConfig.debug || false, 'Hls instance');
|
20654
|
+
var config = this.config = mergeConfig(Hls.DefaultConfig, userConfig, logger);
|
20245
20655
|
this.userConfig = userConfig;
|
20246
20656
|
if (config.progressive) {
|
20247
|
-
enableStreamingMode(config);
|
20657
|
+
enableStreamingMode(config, logger);
|
20248
20658
|
}
|
20249
20659
|
|
20250
20660
|
// core controllers and network loaders
|
@@ -20255,7 +20665,9 @@
|
|
20255
20665
|
ConfigFpsController = config.fpsController;
|
20256
20666
|
var errorController = new ConfigErrorController(this);
|
20257
20667
|
var abrController = this.abrController = new ConfigAbrController(this);
|
20258
|
-
|
20668
|
+
// FragmentTracker must be defined before StreamController because the order of event handling is important
|
20669
|
+
var fragmentTracker = new FragmentTracker(this);
|
20670
|
+
var bufferController = this.bufferController = new ConfigBufferController(this, fragmentTracker);
|
20259
20671
|
var capLevelController = this.capLevelController = new ConfigCapLevelController(this);
|
20260
20672
|
var fpsController = new ConfigFpsController(this);
|
20261
20673
|
var playListLoader = new PlaylistLoader(this);
|
@@ -20264,8 +20676,6 @@
|
|
20264
20676
|
// ConentSteeringController is defined before LevelController to receive Multivariant Playlist events first
|
20265
20677
|
var contentSteering = ConfigContentSteeringController ? new ConfigContentSteeringController(this) : null;
|
20266
20678
|
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);
|
20269
20679
|
var keyLoader = new KeyLoader(this.config);
|
20270
20680
|
var streamController = this.streamController = new StreamController(this, fragmentTracker, keyLoader);
|
20271
20681
|
|
@@ -20352,7 +20762,7 @@
|
|
20352
20762
|
try {
|
20353
20763
|
return this.emit(event, event, eventObject);
|
20354
20764
|
} catch (error) {
|
20355
|
-
logger.error('An internal error happened while handling event ' + event + '. Error message: "' + error.message + '". Here is a stacktrace:', error);
|
20765
|
+
this.logger.error('An internal error happened while handling event ' + event + '. Error message: "' + error.message + '". Here is a stacktrace:', error);
|
20356
20766
|
// Prevent recursion in error event handlers that throw #5497
|
20357
20767
|
if (!this.triggeringException) {
|
20358
20768
|
this.triggeringException = true;
|
@@ -20378,7 +20788,7 @@
|
|
20378
20788
|
* Dispose of the instance
|
20379
20789
|
*/;
|
20380
20790
|
_proto.destroy = function destroy() {
|
20381
|
-
logger.log('destroy');
|
20791
|
+
this.logger.log('destroy');
|
20382
20792
|
this.trigger(Events.DESTROYING, undefined);
|
20383
20793
|
this.detachMedia();
|
20384
20794
|
this.removeAllListeners();
|
@@ -20403,7 +20813,7 @@
|
|
20403
20813
|
* Attaches Hls.js to a media element
|
20404
20814
|
*/;
|
20405
20815
|
_proto.attachMedia = function attachMedia(media) {
|
20406
|
-
logger.log('attachMedia');
|
20816
|
+
this.logger.log('attachMedia');
|
20407
20817
|
this._media = media;
|
20408
20818
|
this.trigger(Events.MEDIA_ATTACHING, {
|
20409
20819
|
media: media
|
@@ -20414,7 +20824,7 @@
|
|
20414
20824
|
* Detach Hls.js from the media
|
20415
20825
|
*/;
|
20416
20826
|
_proto.detachMedia = function detachMedia() {
|
20417
|
-
logger.log('detachMedia');
|
20827
|
+
this.logger.log('detachMedia');
|
20418
20828
|
this.trigger(Events.MEDIA_DETACHING, undefined);
|
20419
20829
|
this._media = null;
|
20420
20830
|
}
|
@@ -20431,7 +20841,7 @@
|
|
20431
20841
|
});
|
20432
20842
|
this._autoLevelCapping = -1;
|
20433
20843
|
this._maxHdcpLevel = null;
|
20434
|
-
logger.log("loadSource:" + loadingSource);
|
20844
|
+
this.logger.log("loadSource:" + loadingSource);
|
20435
20845
|
if (media && loadedSource && (loadedSource !== loadingSource || this.bufferController.hasSourceTypes())) {
|
20436
20846
|
this.detachMedia();
|
20437
20847
|
this.attachMedia(media);
|
@@ -20453,8 +20863,7 @@
|
|
20453
20863
|
if (startPosition === void 0) {
|
20454
20864
|
startPosition = -1;
|
20455
20865
|
}
|
20456
|
-
logger.log("startLoad(" + startPosition + ")");
|
20457
|
-
this.started = true;
|
20866
|
+
this.logger.log("startLoad(" + startPosition + ")");
|
20458
20867
|
this.networkControllers.forEach(function (controller) {
|
20459
20868
|
controller.startLoad(startPosition);
|
20460
20869
|
});
|
@@ -20464,34 +20873,31 @@
|
|
20464
20873
|
* Stop loading of any stream data.
|
20465
20874
|
*/;
|
20466
20875
|
_proto.stopLoad = function stopLoad() {
|
20467
|
-
logger.log('stopLoad');
|
20468
|
-
this.started = false;
|
20876
|
+
this.logger.log('stopLoad');
|
20469
20877
|
this.networkControllers.forEach(function (controller) {
|
20470
20878
|
controller.stopLoad();
|
20471
20879
|
});
|
20472
20880
|
}
|
20473
20881
|
|
20474
20882
|
/**
|
20475
|
-
* Resumes stream controller segment loading
|
20883
|
+
* Resumes stream controller segment loading after `pauseBuffering` has been called.
|
20476
20884
|
*/;
|
20477
20885
|
_proto.resumeBuffering = function resumeBuffering() {
|
20478
|
-
|
20479
|
-
|
20480
|
-
|
20481
|
-
|
20482
|
-
|
20483
|
-
});
|
20484
|
-
}
|
20886
|
+
this.networkControllers.forEach(function (controller) {
|
20887
|
+
if (controller.resumeBuffering) {
|
20888
|
+
controller.resumeBuffering();
|
20889
|
+
}
|
20890
|
+
});
|
20485
20891
|
}
|
20486
20892
|
|
20487
20893
|
/**
|
20488
|
-
*
|
20894
|
+
* Prevents stream controller from loading new segments until `resumeBuffering` is called.
|
20489
20895
|
* This allows for media buffering to be paused without interupting playlist loading.
|
20490
20896
|
*/;
|
20491
20897
|
_proto.pauseBuffering = function pauseBuffering() {
|
20492
20898
|
this.networkControllers.forEach(function (controller) {
|
20493
|
-
if (
|
20494
|
-
controller.
|
20899
|
+
if (controller.pauseBuffering) {
|
20900
|
+
controller.pauseBuffering();
|
20495
20901
|
}
|
20496
20902
|
});
|
20497
20903
|
}
|
@@ -20500,7 +20906,7 @@
|
|
20500
20906
|
* Swap through possible audio codecs in the stream (for example to switch from stereo to 5.1)
|
20501
20907
|
*/;
|
20502
20908
|
_proto.swapAudioCodec = function swapAudioCodec() {
|
20503
|
-
logger.log('swapAudioCodec');
|
20909
|
+
this.logger.log('swapAudioCodec');
|
20504
20910
|
this.streamController.swapAudioCodec();
|
20505
20911
|
}
|
20506
20912
|
|
@@ -20511,7 +20917,7 @@
|
|
20511
20917
|
* Automatic recovery of media-errors by this process is configurable.
|
20512
20918
|
*/;
|
20513
20919
|
_proto.recoverMediaError = function recoverMediaError() {
|
20514
|
-
logger.log('recoverMediaError');
|
20920
|
+
this.logger.log('recoverMediaError');
|
20515
20921
|
var media = this._media;
|
20516
20922
|
this.detachMedia();
|
20517
20923
|
if (media) {
|
@@ -20566,7 +20972,7 @@
|
|
20566
20972
|
* 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.
|
20567
20973
|
*/,
|
20568
20974
|
set: function set(newLevel) {
|
20569
|
-
logger.log("set currentLevel:" + newLevel);
|
20975
|
+
this.logger.log("set currentLevel:" + newLevel);
|
20570
20976
|
this.levelController.manualLevel = newLevel;
|
20571
20977
|
this.streamController.immediateLevelSwitch();
|
20572
20978
|
}
|
@@ -20587,7 +20993,7 @@
|
|
20587
20993
|
* @param newLevel - Pass -1 for automatic level selection
|
20588
20994
|
*/,
|
20589
20995
|
set: function set(newLevel) {
|
20590
|
-
logger.log("set nextLevel:" + newLevel);
|
20996
|
+
this.logger.log("set nextLevel:" + newLevel);
|
20591
20997
|
this.levelController.manualLevel = newLevel;
|
20592
20998
|
this.streamController.nextLevelSwitch();
|
20593
20999
|
}
|
@@ -20608,7 +21014,7 @@
|
|
20608
21014
|
* @param newLevel - Pass -1 for automatic level selection
|
20609
21015
|
*/,
|
20610
21016
|
set: function set(newLevel) {
|
20611
|
-
logger.log("set loadLevel:" + newLevel);
|
21017
|
+
this.logger.log("set loadLevel:" + newLevel);
|
20612
21018
|
this.levelController.manualLevel = newLevel;
|
20613
21019
|
}
|
20614
21020
|
|
@@ -20643,7 +21049,7 @@
|
|
20643
21049
|
* Sets "first-level", see getter.
|
20644
21050
|
*/,
|
20645
21051
|
set: function set(newLevel) {
|
20646
|
-
logger.log("set firstLevel:" + newLevel);
|
21052
|
+
this.logger.log("set firstLevel:" + newLevel);
|
20647
21053
|
this.levelController.firstLevel = newLevel;
|
20648
21054
|
}
|
20649
21055
|
|
@@ -20670,7 +21076,7 @@
|
|
20670
21076
|
* (determined from download of first segment)
|
20671
21077
|
*/,
|
20672
21078
|
set: function set(newLevel) {
|
20673
|
-
logger.log("set startLevel:" + newLevel);
|
21079
|
+
this.logger.log("set startLevel:" + newLevel);
|
20674
21080
|
// if not in automatic start level detection, ensure startLevel is greater than minAutoLevel
|
20675
21081
|
if (newLevel !== -1) {
|
20676
21082
|
newLevel = Math.max(newLevel, this.minAutoLevel);
|
@@ -20723,7 +21129,7 @@
|
|
20723
21129
|
*/
|
20724
21130
|
function set(newLevel) {
|
20725
21131
|
if (this._autoLevelCapping !== newLevel) {
|
20726
|
-
logger.log("set autoLevelCapping:" + newLevel);
|
21132
|
+
this.logger.log("set autoLevelCapping:" + newLevel);
|
20727
21133
|
this._autoLevelCapping = newLevel;
|
20728
21134
|
this.levelController.checkMaxAutoUpdated();
|
20729
21135
|
}
|
@@ -20866,6 +21272,11 @@
|
|
20866
21272
|
get: function get() {
|
20867
21273
|
return this.streamController.getMainFwdBufferInfo();
|
20868
21274
|
}
|
21275
|
+
}, {
|
21276
|
+
key: "maxBufferLength",
|
21277
|
+
get: function get() {
|
21278
|
+
return this.streamController.maxBufferLength;
|
21279
|
+
}
|
20869
21280
|
}, {
|
20870
21281
|
key: "allAudioTracks",
|
20871
21282
|
get: function get() {
|
@@ -21048,7 +21459,7 @@
|
|
21048
21459
|
* Get the video-dev/hls.js package version.
|
21049
21460
|
*/
|
21050
21461
|
function get() {
|
21051
|
-
return "1.5.
|
21462
|
+
return "1.5.8-0.canary.10044";
|
21052
21463
|
}
|
21053
21464
|
}, {
|
21054
21465
|
key: "Events",
|