@clappr/hlsjs-playback 1.9.11 → 1.9.13

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.
@@ -4,149 +4,6 @@
4
4
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.HlsjsPlayback = factory(global.Clappr));
5
5
  })(this, (function (core) { 'use strict';
6
6
 
7
- function _arrayLikeToArray(r, a) {
8
- (null == a || a > r.length) && (a = r.length);
9
- for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
10
- return n;
11
- }
12
- function _arrayWithoutHoles(r) {
13
- if (Array.isArray(r)) return _arrayLikeToArray(r);
14
- }
15
- function _assertThisInitialized(e) {
16
- if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
17
- return e;
18
- }
19
- function _callSuper(t, o, e) {
20
- return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e));
21
- }
22
- function _classCallCheck(a, n) {
23
- if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function");
24
- }
25
- function _defineProperties(e, r) {
26
- for (var t = 0; t < r.length; t++) {
27
- var o = r[t];
28
- o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey$1(o.key), o);
29
- }
30
- }
31
- function _createClass(e, r, t) {
32
- return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", {
33
- writable: !1
34
- }), e;
35
- }
36
- function _defineProperty$1(e, r, t) {
37
- return (r = _toPropertyKey$1(r)) in e ? Object.defineProperty(e, r, {
38
- value: t,
39
- enumerable: !0,
40
- configurable: !0,
41
- writable: !0
42
- }) : e[r] = t, e;
43
- }
44
- function _get() {
45
- return _get = "undefined" != typeof Reflect && Reflect.get ? Reflect.get.bind() : function (e, t, r) {
46
- var p = _superPropBase(e, t);
47
- if (p) {
48
- var n = Object.getOwnPropertyDescriptor(p, t);
49
- return n.get ? n.get.call(arguments.length < 3 ? e : r) : n.value;
50
- }
51
- }, _get.apply(null, arguments);
52
- }
53
- function _getPrototypeOf(t) {
54
- return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) {
55
- return t.__proto__ || Object.getPrototypeOf(t);
56
- }, _getPrototypeOf(t);
57
- }
58
- function _inherits(t, e) {
59
- if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function");
60
- t.prototype = Object.create(e && e.prototype, {
61
- constructor: {
62
- value: t,
63
- writable: !0,
64
- configurable: !0
65
- }
66
- }), Object.defineProperty(t, "prototype", {
67
- writable: !1
68
- }), e && _setPrototypeOf(t, e);
69
- }
70
- function _isNativeReflectConstruct() {
71
- try {
72
- var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
73
- } catch (t) {}
74
- return (_isNativeReflectConstruct = function () {
75
- return !!t;
76
- })();
77
- }
78
- function _iterableToArray(r) {
79
- if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r);
80
- }
81
- function _nonIterableSpread() {
82
- throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
83
- }
84
- function ownKeys$1(e, r) {
85
- var t = Object.keys(e);
86
- if (Object.getOwnPropertySymbols) {
87
- var o = Object.getOwnPropertySymbols(e);
88
- r && (o = o.filter(function (r) {
89
- return Object.getOwnPropertyDescriptor(e, r).enumerable;
90
- })), t.push.apply(t, o);
91
- }
92
- return t;
93
- }
94
- function _objectSpread2$1(e) {
95
- for (var r = 1; r < arguments.length; r++) {
96
- var t = null != arguments[r] ? arguments[r] : {};
97
- r % 2 ? ownKeys$1(Object(t), !0).forEach(function (r) {
98
- _defineProperty$1(e, r, t[r]);
99
- }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$1(Object(t)).forEach(function (r) {
100
- Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
101
- });
102
- }
103
- return e;
104
- }
105
- function _possibleConstructorReturn(t, e) {
106
- if (e && ("object" == typeof e || "function" == typeof e)) return e;
107
- if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined");
108
- return _assertThisInitialized(t);
109
- }
110
- function _setPrototypeOf(t, e) {
111
- return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) {
112
- return t.__proto__ = e, t;
113
- }, _setPrototypeOf(t, e);
114
- }
115
- function _superPropBase(t, o) {
116
- for (; !{}.hasOwnProperty.call(t, o) && null !== (t = _getPrototypeOf(t)););
117
- return t;
118
- }
119
- function _superPropGet(t, o, e, r) {
120
- var p = _get(_getPrototypeOf(1 & r ? t.prototype : t), o, e);
121
- return 2 & r && "function" == typeof p ? function (t) {
122
- return p.apply(e, t);
123
- } : p;
124
- }
125
- function _toConsumableArray(r) {
126
- return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread();
127
- }
128
- function _toPrimitive$1(t, r) {
129
- if ("object" != typeof t || !t) return t;
130
- var e = t[Symbol.toPrimitive];
131
- if (void 0 !== e) {
132
- var i = e.call(t, r || "default");
133
- if ("object" != typeof i) return i;
134
- throw new TypeError("@@toPrimitive must return a primitive value.");
135
- }
136
- return ("string" === r ? String : Number)(t);
137
- }
138
- function _toPropertyKey$1(t) {
139
- var i = _toPrimitive$1(t, "string");
140
- return "symbol" == typeof i ? i : i + "";
141
- }
142
- function _unsupportedIterableToArray(r, a) {
143
- if (r) {
144
- if ("string" == typeof r) return _arrayLikeToArray(r, a);
145
- var t = {}.toString.call(r).slice(8, -1);
146
- return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
147
- }
148
- }
149
-
150
7
  // https://caniuse.com/mdn-javascript_builtins_number_isfinite
151
8
  const isFiniteNumber = Number.isFinite || function (value) {
152
9
  return typeof value === 'number' && isFinite(value);
@@ -36848,878 +36705,726 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
36848
36705
  }
36849
36706
  Hls.defaultConfig = void 0;
36850
36707
 
36851
- var now = core.Utils.now,
36852
- listContainsIgnoreCase = core.Utils.listContainsIgnoreCase;
36853
- var AUTO = -1;
36854
- var DEFAULT_RECOVER_ATTEMPTS = 16;
36708
+ // Copyright 2014 Globo.com Player authors. All rights reserved.
36709
+ // Use of this source code is governed by a BSD-style
36710
+ // license that can be found in the LICENSE file.
36711
+
36712
+ const {
36713
+ now,
36714
+ listContainsIgnoreCase
36715
+ } = core.Utils;
36716
+ const AUTO = -1;
36717
+ const DEFAULT_RECOVER_ATTEMPTS = 16;
36855
36718
  core.Events.register('PLAYBACK_FRAGMENT_CHANGED');
36856
36719
  core.Events.register('PLAYBACK_FRAGMENT_PARSING_METADATA');
36857
- var HlsjsPlayback = /*#__PURE__*/function (_HTML5Video) {
36858
- function HlsjsPlayback() {
36859
- var _this;
36860
- _classCallCheck(this, HlsjsPlayback);
36861
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
36862
- args[_key] = arguments[_key];
36863
- }
36864
- _this = _callSuper(this, HlsjsPlayback, [].concat(args));
36865
- _this.options.hlsPlayback = _objectSpread2$1(_objectSpread2$1({}, _this.defaultOptions), _this.options.hlsPlayback);
36866
- _this._timeUpdateThrottleDelay = 200;
36867
- _this._timeUpdateFiringRate = 0.2;
36868
- _this._durationChangeMinOffset = 0.5;
36869
- _this._setInitialState();
36870
- return _this;
36871
- }
36872
- _inherits(HlsjsPlayback, _HTML5Video);
36873
- return _createClass(HlsjsPlayback, [{
36874
- key: "name",
36875
- get: function get() {
36876
- return 'hls';
36877
- }
36878
- }, {
36879
- key: "supportedVersion",
36880
- get: function get() {
36881
- return {
36882
- min: "0.14.6"
36883
- };
36884
- }
36885
- }, {
36886
- key: "levels",
36887
- get: function get() {
36888
- return this._levels || [];
36889
- }
36890
- }, {
36891
- key: "currentLevel",
36892
- get: function get() {
36893
- if (this._currentLevel === null || this._currentLevel === undefined) {
36894
- return AUTO;
36895
- } else {
36896
- return this._currentLevel;
36897
- } // 0 is a valid level ID
36898
- },
36899
- set: function set(id) {
36900
- this._currentLevel = id;
36901
- this.trigger(core.Events.PLAYBACK_LEVEL_SWITCH_START);
36902
- if (this.options.playback.hlsUseNextLevel) {
36903
- this._hls.nextLevel = this._currentLevel;
36904
- } else {
36905
- this._hls.currentLevel = this._currentLevel;
36906
- }
36907
- }
36908
- }, {
36909
- key: "isReady",
36910
- get: function get() {
36911
- return this._isReadyState;
36912
- }
36913
- }, {
36914
- key: "latency",
36915
- get: function get() {
36916
- return this._hls.latency;
36917
- }
36918
- }, {
36919
- key: "liveSyncPosition",
36920
- get: function get() {
36921
- return this._hls.liveSyncPosition;
36922
- }
36923
- }, {
36924
- key: "currentProgramDateTime",
36925
- get: function get() {
36926
- return this._hls.playingDate;
36927
- }
36928
- }, {
36929
- key: "_startTime",
36930
- get: function get() {
36931
- if (this._playbackType === core.Playback.LIVE && this._playlistType !== 'EVENT') {
36932
- return this._extrapolatedStartTime;
36933
- }
36934
- return this._playableRegionStartTime;
36935
- }
36936
- }, {
36937
- key: "_now",
36938
- get: function get() {
36939
- return now();
36940
- }
36941
-
36942
- // the time in the video element which should represent the start of the sliding window
36943
- // extrapolated to increase in real time (instead of jumping as the early segments are removed)
36944
- }, {
36945
- key: "_extrapolatedStartTime",
36946
- get: function get() {
36947
- if (!this._localStartTimeCorrelation) {
36948
- return this._playableRegionStartTime;
36949
- }
36950
- var corr = this._localStartTimeCorrelation;
36951
- var timePassed = this._now - corr.local;
36952
- var extrapolatedWindowStartTime = (corr.remote + timePassed) / 1000;
36953
- // cap at the end of the extrapolated window duration
36954
- return Math.min(extrapolatedWindowStartTime, this._playableRegionStartTime + this._extrapolatedWindowDuration);
36955
- }
36956
-
36957
- // the time in the video element which should represent the end of the content
36958
- // extrapolated to increase in real time (instead of jumping as segments are added)
36959
- }, {
36960
- key: "_extrapolatedEndTime",
36961
- get: function get() {
36962
- var actualEndTime = this._playableRegionStartTime + this._playableRegionDuration;
36963
- if (!this._localEndTimeCorrelation) return actualEndTime;
36964
- var correlation = this._localEndTimeCorrelation;
36965
- var timePassed = this._now - correlation.local;
36966
- var extrapolatedEndTime = (correlation.remote + timePassed) / 1000;
36967
- return Math.max(actualEndTime - this._extrapolatedWindowDuration, Math.min(extrapolatedEndTime, actualEndTime));
36720
+ class HlsjsPlayback extends core.HTML5Video {
36721
+ get name() {
36722
+ return 'hls';
36723
+ }
36724
+ get supportedVersion() {
36725
+ return {
36726
+ min: "0.14.8"
36727
+ };
36728
+ }
36729
+ get levels() {
36730
+ return this._levels || [];
36731
+ }
36732
+ get currentLevel() {
36733
+ if (this._currentLevel === null || this._currentLevel === undefined) {
36734
+ return AUTO;
36735
+ } else {
36736
+ return this._currentLevel;
36737
+ } // 0 is a valid level ID
36738
+ }
36739
+ get isReady() {
36740
+ return this._isReadyState;
36741
+ }
36742
+ set currentLevel(id) {
36743
+ this._currentLevel = id;
36744
+ this.trigger(core.Events.PLAYBACK_LEVEL_SWITCH_START);
36745
+ if (this.options.playback.hlsUseNextLevel) {
36746
+ this._hls.nextLevel = this._currentLevel;
36747
+ } else {
36748
+ this._hls.currentLevel = this._currentLevel;
36968
36749
  }
36969
- }, {
36970
- key: "_duration",
36971
- get: function get() {
36972
- return this._extrapolatedEndTime - this._startTime;
36750
+ }
36751
+ get latency() {
36752
+ return this._hls.latency;
36753
+ }
36754
+ get liveSyncPosition() {
36755
+ return this._hls.liveSyncPosition;
36756
+ }
36757
+ get currentProgramDateTime() {
36758
+ return this._hls.playingDate;
36759
+ }
36760
+ get _startTime() {
36761
+ if (this._playbackType === core.Playback.LIVE && this._playlistType !== 'EVENT') {
36762
+ return this._extrapolatedStartTime;
36973
36763
  }
36764
+ return this._playableRegionStartTime;
36765
+ }
36766
+ get _now() {
36767
+ return now();
36768
+ }
36974
36769
 
36975
- // Returns the duration (seconds) of the window that the extrapolated start time is allowed
36976
- // to move in before being capped.
36977
- // The extrapolated start time should never reach the cap at the end of the window as the
36978
- // window should slide as chunks are removed from the start.
36979
- // This also applies to the extrapolated end time in the same way.
36980
- //
36981
- // If chunks aren't being removed for some reason that the start time will reach and remain fixed at
36982
- // playableRegionStartTime + extrapolatedWindowDuration
36983
- //
36984
- // <-- window duration -->
36985
- // I.e playableRegionStartTime |-----------------------|
36986
- // | --> . . .
36987
- // . --> | --> . .
36988
- // . . --> | --> .
36989
- // . . . --> |
36990
- // . . . .
36991
- // extrapolatedStartTime
36992
- }, {
36993
- key: "_extrapolatedWindowDuration",
36994
- get: function get() {
36995
- if (this._segmentTargetDuration === null) {
36996
- return 0;
36997
- }
36998
- return this._extrapolatedWindowNumSegments * this._segmentTargetDuration;
36999
- }
37000
- }, {
37001
- key: "bandwidthEstimate",
37002
- get: function get() {
37003
- return this._hls && this._hls.bandwidthEstimate;
37004
- }
37005
- }, {
37006
- key: "defaultOptions",
37007
- get: function get() {
37008
- return {
37009
- preload: true
37010
- };
37011
- }
37012
- }, {
37013
- key: "customListeners",
37014
- get: function get() {
37015
- return this.options.hlsPlayback && this.options.hlsPlayback.customListeners || [];
37016
- }
37017
- }, {
37018
- key: "sourceMedia",
37019
- get: function get() {
37020
- return this.options.src;
37021
- }
37022
- }, {
37023
- key: "currentTimestamp",
37024
- get: function get() {
37025
- if (!this._currentFragment) return null;
37026
- var startTime = this._currentFragment.programDateTime;
37027
- var playbackTime = this.el.currentTime;
37028
- var playTimeOffSet = playbackTime - this._currentFragment.start;
37029
- var currentTimestampInMs = startTime + playTimeOffSet * 1000;
37030
- return currentTimestampInMs / 1000;
37031
- }
37032
- }, {
37033
- key: "_setInitialState",
37034
- value: function _setInitialState() {
37035
- this._minDvrSize = typeof this.options.hlsMinimumDvrSize === 'undefined' ? 60 : this.options.hlsMinimumDvrSize;
37036
- // The size of the start time extrapolation window measured as a multiple of segments.
37037
- // Should be 2 or higher, or 0 to disable. Should only need to be increased above 2 if more than one segment is
37038
- // removed from the start of the playlist at a time. E.g if the playlist is cached for 10 seconds and new chunks are
37039
- // added/removed every 5.
37040
- this._extrapolatedWindowNumSegments = !this.options.playback || typeof this.options.playback.extrapolatedWindowNumSegments === 'undefined' ? 2 : this.options.playback.extrapolatedWindowNumSegments;
37041
- this._playbackType = core.Playback.VOD;
37042
- this._lastTimeUpdate = {
37043
- current: 0,
37044
- total: 0,
37045
- firstFragDateTime: 0
37046
- };
37047
- this._lastTimeUpdateFiredTime = 0;
37048
- this._lastDuration = null;
37049
- // for hls streams which have dvr with a sliding window,
37050
- // the content at the start of the playlist is removed as new
37051
- // content is appended at the end.
37052
- // this means the actual playable start time will increase as the
37053
- // start content is deleted
37054
- // For streams with dvr where the entire recording is kept from the
37055
- // beginning this should stay as 0
37056
- this._playableRegionStartTime = 0;
37057
- // {local, remote} remote is the time in the video element that should represent 0
37058
- // local is the system time when the 'remote' measurment took place
37059
- this._localStartTimeCorrelation = null;
37060
- // {local, remote} remote is the time in the video element that should represents the end
37061
- // local is the system time when the 'remote' measurment took place
37062
- this._localEndTimeCorrelation = null;
37063
- // if content is removed from the beginning then this empty area should
37064
- // be ignored. "playableRegionDuration" excludes the empty area
37065
- this._playableRegionDuration = 0;
37066
- // #EXT-X-PROGRAM-DATE-TIME
37067
- this._programDateTime = 0;
37068
- // true when the actual duration is longer than hlsjs's live sync point
37069
- // when this is false playableRegionDuration will be the actual duration
37070
- // when this is true playableRegionDuration will exclude the time after the sync point
37071
- this._durationExcludesAfterLiveSyncPoint = false;
37072
- // #EXT-X-TARGETDURATION
37073
- this._segmentTargetDuration = null;
37074
- // #EXT-X-PLAYLIST-TYPE
37075
- this._playlistType = null;
37076
- this._recoverAttemptsRemaining = this.options.hlsRecoverAttempts || DEFAULT_RECOVER_ATTEMPTS;
37077
- }
37078
- }, {
37079
- key: "_setup",
37080
- value: function _setup() {
37081
- this._destroyHLSInstance();
37082
- this._createHLSInstance();
37083
- this._listenHLSEvents();
37084
- this._attachHLSMedia();
37085
- }
37086
- }, {
37087
- key: "_destroyHLSInstance",
37088
- value: function _destroyHLSInstance() {
37089
- if (!this._hls) return;
37090
- this._manifestLoading = false;
37091
- this._ccIsSetup = false;
37092
- this._ccTracksUpdated = false;
37093
- this._setInitialState();
37094
- this._hls.destroy();
37095
- this._hls = null;
37096
- }
37097
- }, {
37098
- key: "_createHLSInstance",
37099
- value: function _createHLSInstance() {
37100
- var config = _objectSpread2$1({}, this.options.playback.hlsjsConfig);
37101
- this._hls = new Hls(config);
37102
- }
37103
- }, {
37104
- key: "_attachHLSMedia",
37105
- value: function _attachHLSMedia() {
37106
- if (!this._hls) return;
37107
- this._hls.attachMedia(this.el);
37108
- }
37109
- }, {
37110
- key: "_listenHLSEvents",
37111
- value: function _listenHLSEvents() {
37112
- var _this2 = this;
37113
- if (!this._hls) return;
37114
- this._hls.once(Hls.Events.MEDIA_ATTACHED, function () {
37115
- _this2.options.hlsPlayback.preload && _this2._hls.loadSource(_this2.options.src);
37116
- });
37117
- this._hls.on(Hls.Events.MANIFEST_LOADING, function () {
37118
- _this2._manifestLoading = true;
37119
- });
37120
- this._hls.on(Hls.Events.LEVEL_LOADED, function (evt, data) {
37121
- return _this2._updatePlaybackType(evt, data);
37122
- });
37123
- this._hls.on(Hls.Events.LEVEL_UPDATED, function (evt, data) {
37124
- return _this2._onLevelUpdated(evt, data);
37125
- });
37126
- this._hls.on(Hls.Events.LEVEL_SWITCHED, function (evt, data) {
37127
- return _this2._onLevelSwitch(evt, data);
37128
- });
37129
- this._hls.on(Hls.Events.FRAG_CHANGED, function (evt, data) {
37130
- return _this2._onFragmentChanged(evt, data);
37131
- });
37132
- this._hls.on(Hls.Events.FRAG_LOADED, function (evt, data) {
37133
- return _this2._onFragmentLoaded(evt, data);
37134
- });
37135
- this._hls.on(Hls.Events.FRAG_BUFFERED, function (evt, data) {
37136
- return _this2._onFragmentBuffered(evt, data);
37137
- });
37138
- this._hls.on(Hls.Events.FRAG_PARSING_METADATA, function (evt, data) {
37139
- return _this2._onFragmentParsingMetadata(evt, data);
37140
- });
37141
- this._hls.on(Hls.Events.ERROR, function (evt, data) {
37142
- return _this2._onHLSJSError(evt, data);
37143
- });
37144
- this._hls.on(Hls.Events.SUBTITLE_TRACK_LOADED, function (evt, data) {
37145
- return _this2._onSubtitleLoaded(evt, data);
37146
- });
37147
- this._hls.on(Hls.Events.SUBTITLE_TRACKS_UPDATED, function () {
37148
- return _this2._ccTracksUpdated = true;
37149
- });
37150
- this.bindCustomListeners();
37151
- }
37152
- }, {
37153
- key: "bindCustomListeners",
37154
- value: function bindCustomListeners() {
37155
- var _this3 = this;
37156
- this.customListeners.forEach(function (item) {
37157
- var requestedEventName = item.eventName;
37158
- var typeOfListener = item.once ? 'once' : 'on';
37159
- requestedEventName && _this3._hls["".concat(typeOfListener)](requestedEventName, item.callback);
37160
- });
36770
+ // the time in the video element which should represent the start of the sliding window
36771
+ // extrapolated to increase in real time (instead of jumping as the early segments are removed)
36772
+ get _extrapolatedStartTime() {
36773
+ if (!this._localStartTimeCorrelation) {
36774
+ return this._playableRegionStartTime;
37161
36775
  }
37162
- }, {
37163
- key: "unbindCustomListeners",
37164
- value: function unbindCustomListeners() {
37165
- var _this4 = this;
37166
- this.customListeners.forEach(function (item) {
37167
- var requestedEventName = item.eventName;
37168
- requestedEventName && _this4._hls.off(requestedEventName, item.callback);
37169
- });
36776
+ const corr = this._localStartTimeCorrelation;
36777
+ const timePassed = this._now - corr.local;
36778
+ const extrapolatedWindowStartTime = (corr.remote + timePassed) / 1000;
36779
+ // cap at the end of the extrapolated window duration
36780
+ return Math.min(extrapolatedWindowStartTime, this._playableRegionStartTime + this._extrapolatedWindowDuration);
36781
+ }
36782
+
36783
+ // the time in the video element which should represent the end of the content
36784
+ // extrapolated to increase in real time (instead of jumping as segments are added)
36785
+ get _extrapolatedEndTime() {
36786
+ const actualEndTime = this._playableRegionStartTime + this._playableRegionDuration;
36787
+ if (!this._localEndTimeCorrelation) return actualEndTime;
36788
+ const correlation = this._localEndTimeCorrelation;
36789
+ const timePassed = this._now - correlation.local;
36790
+ const extrapolatedEndTime = (correlation.remote + timePassed) / 1000;
36791
+ return Math.max(actualEndTime - this._extrapolatedWindowDuration, Math.min(extrapolatedEndTime, actualEndTime));
36792
+ }
36793
+ get _duration() {
36794
+ return this._extrapolatedEndTime - this._startTime;
36795
+ }
36796
+
36797
+ // Returns the duration (seconds) of the window that the extrapolated start time is allowed
36798
+ // to move in before being capped.
36799
+ // The extrapolated start time should never reach the cap at the end of the window as the
36800
+ // window should slide as chunks are removed from the start.
36801
+ // This also applies to the extrapolated end time in the same way.
36802
+ //
36803
+ // If chunks aren't being removed for some reason that the start time will reach and remain fixed at
36804
+ // playableRegionStartTime + extrapolatedWindowDuration
36805
+ //
36806
+ // <-- window duration -->
36807
+ // I.e playableRegionStartTime |-----------------------|
36808
+ // | --> . . .
36809
+ // . --> | --> . .
36810
+ // . . --> | --> .
36811
+ // . . . --> |
36812
+ // . . . .
36813
+ // extrapolatedStartTime
36814
+ get _extrapolatedWindowDuration() {
36815
+ if (this._segmentTargetDuration === null) {
36816
+ return 0;
37170
36817
  }
37171
- }, {
37172
- key: "_onFragmentParsingMetadata",
37173
- value: function _onFragmentParsingMetadata(evt, data) {
37174
- this.trigger(core.Events.Custom.PLAYBACK_FRAGMENT_PARSING_METADATA, {
37175
- evt: evt,
37176
- data: data
36818
+ return this._extrapolatedWindowNumSegments * this._segmentTargetDuration;
36819
+ }
36820
+ get bandwidthEstimate() {
36821
+ return this._hls && this._hls.bandwidthEstimate;
36822
+ }
36823
+ get defaultOptions() {
36824
+ return {
36825
+ preload: true
36826
+ };
36827
+ }
36828
+ get customListeners() {
36829
+ return this.options.hlsPlayback && this.options.hlsPlayback.customListeners || [];
36830
+ }
36831
+ get sourceMedia() {
36832
+ return this.options.src;
36833
+ }
36834
+ get currentTimestamp() {
36835
+ if (!this._currentFragment) return null;
36836
+ const startTime = this._currentFragment.programDateTime;
36837
+ const playbackTime = this.el.currentTime;
36838
+ const playTimeOffSet = playbackTime - this._currentFragment.start;
36839
+ const currentTimestampInMs = startTime + playTimeOffSet * 1000;
36840
+ return currentTimestampInMs / 1000;
36841
+ }
36842
+ static get HLSJS() {
36843
+ return Hls;
36844
+ }
36845
+ constructor(...args) {
36846
+ super(...args);
36847
+ this.options.hlsPlayback = {
36848
+ ...this.defaultOptions,
36849
+ ...this.options.hlsPlayback
36850
+ };
36851
+ this._timeUpdateThrottleDelay = 200;
36852
+ this._timeUpdateFiringRate = 0.2;
36853
+ this._durationChangeMinOffset = 0.5;
36854
+ this._setInitialState();
36855
+ }
36856
+ _setInitialState() {
36857
+ this._minDvrSize = typeof this.options.hlsMinimumDvrSize === 'undefined' ? 60 : this.options.hlsMinimumDvrSize;
36858
+ // The size of the start time extrapolation window measured as a multiple of segments.
36859
+ // Should be 2 or higher, or 0 to disable. Should only need to be increased above 2 if more than one segment is
36860
+ // removed from the start of the playlist at a time. E.g if the playlist is cached for 10 seconds and new chunks are
36861
+ // added/removed every 5.
36862
+ this._extrapolatedWindowNumSegments = !this.options.playback || typeof this.options.playback.extrapolatedWindowNumSegments === 'undefined' ? 2 : this.options.playback.extrapolatedWindowNumSegments;
36863
+ this._playbackType = core.Playback.VOD;
36864
+ this._lastTimeUpdate = {
36865
+ current: 0,
36866
+ total: 0,
36867
+ firstFragDateTime: 0
36868
+ };
36869
+ this._lastTimeUpdateFiredTime = 0;
36870
+ this._lastDuration = null;
36871
+ // for hls streams which have dvr with a sliding window,
36872
+ // the content at the start of the playlist is removed as new
36873
+ // content is appended at the end.
36874
+ // this means the actual playable start time will increase as the
36875
+ // start content is deleted
36876
+ // For streams with dvr where the entire recording is kept from the
36877
+ // beginning this should stay as 0
36878
+ this._playableRegionStartTime = 0;
36879
+ // {local, remote} remote is the time in the video element that should represent 0
36880
+ // local is the system time when the 'remote' measurment took place
36881
+ this._localStartTimeCorrelation = null;
36882
+ // {local, remote} remote is the time in the video element that should represents the end
36883
+ // local is the system time when the 'remote' measurment took place
36884
+ this._localEndTimeCorrelation = null;
36885
+ // if content is removed from the beginning then this empty area should
36886
+ // be ignored. "playableRegionDuration" excludes the empty area
36887
+ this._playableRegionDuration = 0;
36888
+ // #EXT-X-PROGRAM-DATE-TIME
36889
+ this._programDateTime = 0;
36890
+ // true when the actual duration is longer than hlsjs's live sync point
36891
+ // when this is false playableRegionDuration will be the actual duration
36892
+ // when this is true playableRegionDuration will exclude the time after the sync point
36893
+ this._durationExcludesAfterLiveSyncPoint = false;
36894
+ // #EXT-X-TARGETDURATION
36895
+ this._segmentTargetDuration = null;
36896
+ // #EXT-X-PLAYLIST-TYPE
36897
+ this._playlistType = null;
36898
+ this._recoverAttemptsRemaining = this.options.hlsRecoverAttempts || DEFAULT_RECOVER_ATTEMPTS;
36899
+ }
36900
+ _setup() {
36901
+ this._destroyHLSInstance();
36902
+ this._createHLSInstance();
36903
+ this._listenHLSEvents();
36904
+ this._attachHLSMedia();
36905
+ }
36906
+ _destroyHLSInstance() {
36907
+ if (!this._hls) return;
36908
+ this._manifestLoading = false;
36909
+ this._ccIsSetup = false;
36910
+ this._ccTracksUpdated = false;
36911
+ this._setInitialState();
36912
+ this._hls.destroy();
36913
+ this._hls = null;
36914
+ }
36915
+ _createHLSInstance() {
36916
+ const config = {
36917
+ ...this.options.playback.hlsjsConfig
36918
+ };
36919
+ this._hls = new Hls(config);
36920
+ }
36921
+ _attachHLSMedia() {
36922
+ if (!this._hls) return;
36923
+ this._hls.attachMedia(this.el);
36924
+ }
36925
+ _listenHLSEvents() {
36926
+ if (!this._hls) return;
36927
+ this._hls.once(Hls.Events.MEDIA_ATTACHED, () => {
36928
+ this.options.hlsPlayback.preload && this._hls.loadSource(this.options.src);
36929
+ });
36930
+ this._hls.on(Hls.Events.MANIFEST_LOADING, () => {
36931
+ this._manifestLoading = true;
36932
+ });
36933
+ this._hls.on(Hls.Events.LEVEL_LOADED, (evt, data) => this._updatePlaybackType(evt, data));
36934
+ this._hls.on(Hls.Events.LEVEL_UPDATED, (evt, data) => this._onLevelUpdated(evt, data));
36935
+ this._hls.on(Hls.Events.LEVEL_SWITCHED, (evt, data) => this._onLevelSwitch(evt, data));
36936
+ this._hls.on(Hls.Events.FRAG_CHANGED, (evt, data) => this._onFragmentChanged(evt, data));
36937
+ this._hls.on(Hls.Events.FRAG_LOADED, (evt, data) => this._onFragmentLoaded(evt, data));
36938
+ this._hls.on(Hls.Events.FRAG_BUFFERED, (evt, data) => this._onFragmentBuffered(evt, data));
36939
+ this._hls.on(Hls.Events.FRAG_PARSING_METADATA, (evt, data) => this._onFragmentParsingMetadata(evt, data));
36940
+ this._hls.on(Hls.Events.ERROR, (evt, data) => this._onHLSJSError(evt, data));
36941
+ this._hls.on(Hls.Events.SUBTITLE_TRACK_LOADED, (evt, data) => this._onSubtitleLoaded(evt, data));
36942
+ this._hls.on(Hls.Events.SUBTITLE_TRACKS_UPDATED, () => this._ccTracksUpdated = true);
36943
+ this.bindCustomListeners();
36944
+ }
36945
+ bindCustomListeners() {
36946
+ this.customListeners.forEach(item => {
36947
+ const requestedEventName = item.eventName;
36948
+ const typeOfListener = item.once ? 'once' : 'on';
36949
+ requestedEventName && this._hls[`${typeOfListener}`](requestedEventName, item.callback);
36950
+ });
36951
+ }
36952
+ unbindCustomListeners() {
36953
+ this.customListeners.forEach(item => {
36954
+ const requestedEventName = item.eventName;
36955
+ requestedEventName && this._hls.off(requestedEventName, item.callback);
36956
+ });
36957
+ }
36958
+ _onFragmentParsingMetadata(evt, data) {
36959
+ this.trigger(core.Events.Custom.PLAYBACK_FRAGMENT_PARSING_METADATA, {
36960
+ evt,
36961
+ data
36962
+ });
36963
+ }
36964
+ render() {
36965
+ this._ready();
36966
+ return super.render();
36967
+ }
36968
+ _ready() {
36969
+ if (this._isReadyState) return;
36970
+ !this._hls && this._setup();
36971
+ this._isReadyState = true;
36972
+ this.trigger(core.Events.PLAYBACK_READY, this.name);
36973
+ }
36974
+ _recover(evt, data, error) {
36975
+ if (!this._recoveredDecodingError) {
36976
+ this._recoveredDecodingError = true;
36977
+ this._hls.recoverMediaError();
36978
+ this.play();
36979
+ } else if (!this._recoveredAudioCodecError) {
36980
+ this._recoveredAudioCodecError = true;
36981
+ this._hls.swapAudioCodec();
36982
+ this._hls.recoverMediaError();
36983
+ this.play();
36984
+ } else {
36985
+ core.Log.error('hlsjs: failed to recover', {
36986
+ evt,
36987
+ data
37177
36988
  });
36989
+ error.level = core.PlayerError.Levels.FATAL;
36990
+ const formattedError = this.createError(error);
36991
+ this.trigger(core.Events.PLAYBACK_ERROR, formattedError);
36992
+ this.stop();
37178
36993
  }
37179
- }, {
37180
- key: "render",
37181
- value: function render() {
37182
- this._ready();
37183
- return _superPropGet(HlsjsPlayback, "render", this, 3)([]);
37184
- }
37185
- }, {
37186
- key: "_ready",
37187
- value: function _ready() {
37188
- if (this._isReadyState) return;
37189
- !this._hls && this._setup();
37190
- this._isReadyState = true;
37191
- this.trigger(core.Events.PLAYBACK_READY, this.name);
37192
- }
37193
- }, {
37194
- key: "_recover",
37195
- value: function _recover(evt, data, error) {
37196
- if (!this._recoveredDecodingError) {
37197
- this._recoveredDecodingError = true;
37198
- this._hls.recoverMediaError();
37199
- this.play();
37200
- } else if (!this._recoveredAudioCodecError) {
37201
- this._recoveredAudioCodecError = true;
37202
- this._hls.swapAudioCodec();
37203
- this._hls.recoverMediaError();
37204
- this.play();
37205
- } else {
37206
- core.Log.error('hlsjs: failed to recover', {
37207
- evt: evt,
37208
- data: data
37209
- });
37210
- error.level = core.PlayerError.Levels.FATAL;
37211
- var formattedError = this.createError(error);
37212
- this.trigger(core.Events.PLAYBACK_ERROR, formattedError);
37213
- this.stop();
37214
- }
37215
- }
36994
+ }
37216
36995
 
37217
- // override
37218
- // this playback manages the src on the video element itself
37219
- }, {
37220
- key: "_setupSrc",
37221
- value: function _setupSrc(srcUrl) {} // eslint-disable-line no-unused-vars
37222
- }, {
37223
- key: "_startTimeUpdateTimer",
37224
- value: function _startTimeUpdateTimer() {
37225
- var _this5 = this;
37226
- if (this._timeUpdateTimer) return;
37227
- this._timeUpdateTimer = setInterval(function () {
37228
- _this5._onDurationChange();
37229
- _this5._onTimeUpdate();
37230
- }, 100);
37231
- }
37232
- }, {
37233
- key: "_stopTimeUpdateTimer",
37234
- value: function _stopTimeUpdateTimer() {
37235
- if (!this._timeUpdateTimer) return;
37236
- clearInterval(this._timeUpdateTimer);
37237
- this._timeUpdateTimer = null;
37238
- }
37239
- }, {
37240
- key: "getProgramDateTime",
37241
- value: function getProgramDateTime() {
37242
- return this._programDateTime;
37243
- }
36996
+ // override
36997
+ // this playback manages the src on the video element itself
36998
+ _setupSrc(srcUrl) {} // eslint-disable-line no-unused-vars
37244
36999
 
37245
- // the duration on the video element itself should not be used
37246
- // as this does not necesarily represent the duration of the stream
37247
- // https://github.com/clappr/clappr/issues/668#issuecomment-157036678
37248
- }, {
37249
- key: "getDuration",
37250
- value: function getDuration() {
37251
- return this._duration;
37252
- }
37000
+ _startTimeUpdateTimer() {
37001
+ if (this._timeUpdateTimer) return;
37002
+ this._timeUpdateTimer = setInterval(() => {
37003
+ this._onDurationChange();
37004
+ this._onTimeUpdate();
37005
+ }, 100);
37006
+ }
37007
+ _stopTimeUpdateTimer() {
37008
+ if (!this._timeUpdateTimer) return;
37009
+ clearInterval(this._timeUpdateTimer);
37010
+ this._timeUpdateTimer = null;
37011
+ }
37012
+ getProgramDateTime() {
37013
+ return this._programDateTime;
37014
+ }
37253
37015
 
37254
- // the frame rate should be retrieved from the HLS.js level information
37255
- // as it is not necessarily exposed directly by the video element
37256
- }, {
37257
- key: "getFrameRate",
37258
- value: function getFrameRate() {
37259
- if (this._hls && this._hls.levels && this._hls.currentLevel >= 0) {
37260
- var level = this._hls.levels[this._hls.currentLevel];
37261
- return level && level.frameRate ? level.frameRate : null;
37262
- }
37263
- return null;
37264
- }
37265
- }, {
37266
- key: "getCurrentTime",
37267
- value: function getCurrentTime() {
37268
- // e.g. can be < 0 if user pauses near the start
37269
- // eventually they will then be kicked to the end by hlsjs if they run out of buffer
37270
- // before the official start time
37271
- return Math.max(0, this.el.currentTime - this._startTime);
37272
- }
37016
+ // the duration on the video element itself should not be used
37017
+ // as this does not necesarily represent the duration of the stream
37018
+ // https://github.com/clappr/clappr/issues/668#issuecomment-157036678
37019
+ getDuration() {
37020
+ return this._duration;
37021
+ }
37273
37022
 
37274
- // the time that "0" now represents relative to when playback started
37275
- // for a stream with a sliding window this will increase as content is
37276
- // removed from the beginning
37277
- }, {
37278
- key: "getStartTimeOffset",
37279
- value: function getStartTimeOffset() {
37280
- return this._startTime;
37281
- }
37282
- }, {
37283
- key: "seekPercentage",
37284
- value: function seekPercentage(percentage) {
37285
- var seekTo = percentage > 0 ? this._duration * (percentage / 100) : this._duration;
37286
- this.seek(seekTo);
37023
+ // the frame rate should be retrieved from the HLS.js level information
37024
+ // as it is not necessarily exposed directly by the video element
37025
+ getFrameRate() {
37026
+ if (this._hls && this._hls.levels && this._hls.currentLevel >= 0) {
37027
+ const level = this._hls.levels[this._hls.currentLevel];
37028
+ return level && level.frameRate ? level.frameRate : null;
37287
37029
  }
37288
- }, {
37289
- key: "seek",
37290
- value: function seek(time) {
37291
- if (time < 0) {
37292
- core.Log.warn('Attempt to seek to a negative time. Resetting to live point. Use seekToLivePoint() to seek to the live point.');
37293
- time = this.getDuration();
37294
- }
37295
- time += this._startTime;
37296
- this.el.currentTime = time;
37297
- }
37298
- }, {
37299
- key: "seekToLivePoint",
37300
- value: function seekToLivePoint() {
37301
- this.seek(this.getDuration());
37030
+ return null;
37031
+ }
37032
+ getCurrentTime() {
37033
+ // e.g. can be < 0 if user pauses near the start
37034
+ // eventually they will then be kicked to the end by hlsjs if they run out of buffer
37035
+ // before the official start time
37036
+ return Math.max(0, this.el.currentTime - this._startTime);
37037
+ }
37038
+
37039
+ // the time that "0" now represents relative to when playback started
37040
+ // for a stream with a sliding window this will increase as content is
37041
+ // removed from the beginning
37042
+ getStartTimeOffset() {
37043
+ return this._startTime;
37044
+ }
37045
+ seekPercentage(percentage) {
37046
+ const seekTo = percentage > 0 ? this._duration * (percentage / 100) : this._duration;
37047
+ this.seek(seekTo);
37048
+ }
37049
+ seek(time) {
37050
+ if (time < 0) {
37051
+ core.Log.warn('Attempt to seek to a negative time. Resetting to live point. Use seekToLivePoint() to seek to the live point.');
37052
+ time = this.getDuration();
37302
37053
  }
37303
- }, {
37304
- key: "_updateSettings",
37305
- value: function _updateSettings() {
37306
- if (this._playbackType === core.Playback.VOD) {
37307
- this.settings.left = ['playpause', 'position', 'duration'];
37308
- } else if (this.dvrEnabled) {
37309
- this.settings.left = ['playpause'];
37310
- } else {
37311
- this.settings.left = ['playstop'];
37312
- }
37313
- this.settings.seekEnabled = this.isSeekEnabled();
37314
- this.trigger(core.Events.PLAYBACK_SETTINGSUPDATE);
37054
+ time += this._startTime;
37055
+ this.el.currentTime = time;
37056
+ }
37057
+ seekToLivePoint() {
37058
+ this.seek(this.getDuration());
37059
+ }
37060
+ _updateSettings() {
37061
+ if (this._playbackType === core.Playback.VOD) {
37062
+ this.settings.left = ['playpause', 'position', 'duration'];
37063
+ } else if (this.dvrEnabled) {
37064
+ this.settings.left = ['playpause'];
37065
+ } else {
37066
+ this.settings.left = ['playstop'];
37315
37067
  }
37316
- }, {
37317
- key: "_onHLSJSError",
37318
- value: function _onHLSJSError(evt, data) {
37319
- var error = {
37320
- code: "".concat(data.type, "_").concat(data.details),
37321
- description: "".concat(this.name, " error: type: ").concat(data.type, ", details: ").concat(data.details),
37322
- raw: data
37323
- };
37324
- var formattedError;
37325
- if (data.response) error.description += ", response: ".concat(JSON.stringify(data.response));
37326
- // only report/handle errors if they are fatal
37327
- // hlsjs should automatically handle non fatal errors
37328
- if (data.fatal) {
37329
- if (this._recoverAttemptsRemaining > 0) {
37330
- this._recoverAttemptsRemaining -= 1;
37331
- switch (data.type) {
37332
- case Hls.ErrorTypes.NETWORK_ERROR:
37333
- switch (data.details) {
37334
- // The following network errors cannot be recovered with HLS.startLoad()
37335
- // For more details, see https://github.com/video-dev/hls.js/blob/master/doc/design.md#error-detection-and-handling
37336
- // For "level load" fatal errors, see https://github.com/video-dev/hls.js/issues/1138
37337
- case Hls.ErrorDetails.MANIFEST_LOAD_ERROR:
37338
- case Hls.ErrorDetails.MANIFEST_LOAD_TIMEOUT:
37339
- case Hls.ErrorDetails.MANIFEST_PARSING_ERROR:
37340
- case Hls.ErrorDetails.LEVEL_LOAD_ERROR:
37341
- case Hls.ErrorDetails.LEVEL_LOAD_TIMEOUT:
37342
- core.Log.error('hlsjs: unrecoverable network fatal error.', {
37343
- evt: evt,
37344
- data: data
37345
- });
37346
- formattedError = this.createError(error);
37347
- this.trigger(core.Events.PLAYBACK_ERROR, formattedError);
37348
- this.stop();
37349
- break;
37350
- default:
37351
- core.Log.warn('hlsjs: trying to recover from network error.', {
37352
- evt: evt,
37353
- data: data
37354
- });
37355
- error.level = core.PlayerError.Levels.WARN;
37356
- this._hls.startLoad();
37357
- break;
37358
- }
37359
- break;
37360
- case Hls.ErrorTypes.MEDIA_ERROR:
37361
- core.Log.warn('hlsjs: trying to recover from media error.', {
37362
- evt: evt,
37363
- data: data
37364
- });
37365
- error.level = core.PlayerError.Levels.WARN;
37366
- this._recover(evt, data, error);
37367
- break;
37368
- default:
37369
- core.Log.error('hlsjs: could not recover from error.', {
37370
- evt: evt,
37371
- data: data
37372
- });
37373
- formattedError = this.createError(error);
37374
- this.trigger(core.Events.PLAYBACK_ERROR, formattedError);
37375
- this.stop();
37376
- break;
37377
- }
37378
- } else {
37379
- core.Log.error('hlsjs: could not recover from error after maximum number of attempts.', {
37380
- evt: evt,
37381
- data: data
37382
- });
37383
- formattedError = this.createError(error);
37384
- this.trigger(core.Events.PLAYBACK_ERROR, formattedError);
37385
- this.stop();
37068
+ this.settings.seekEnabled = this.isSeekEnabled();
37069
+ this.trigger(core.Events.PLAYBACK_SETTINGSUPDATE);
37070
+ }
37071
+ _onHLSJSError(evt, data) {
37072
+ const error = {
37073
+ code: `${data.type}_${data.details}`,
37074
+ description: `${this.name} error: type: ${data.type}, details: ${data.details}`,
37075
+ raw: data
37076
+ };
37077
+ let formattedError;
37078
+ if (data.response) error.description += `, response: ${JSON.stringify(data.response)}`;
37079
+ // only report/handle errors if they are fatal
37080
+ // hlsjs should automatically handle non fatal errors
37081
+ if (data.fatal) {
37082
+ if (this._recoverAttemptsRemaining > 0) {
37083
+ this._recoverAttemptsRemaining -= 1;
37084
+ switch (data.type) {
37085
+ case Hls.ErrorTypes.NETWORK_ERROR:
37086
+ switch (data.details) {
37087
+ // The following network errors cannot be recovered with HLS.startLoad()
37088
+ // For more details, see https://github.com/video-dev/hls.js/blob/master/doc/design.md#error-detection-and-handling
37089
+ // For "level load" fatal errors, see https://github.com/video-dev/hls.js/issues/1138
37090
+ case Hls.ErrorDetails.MANIFEST_LOAD_ERROR:
37091
+ case Hls.ErrorDetails.MANIFEST_LOAD_TIMEOUT:
37092
+ case Hls.ErrorDetails.MANIFEST_PARSING_ERROR:
37093
+ case Hls.ErrorDetails.LEVEL_LOAD_ERROR:
37094
+ case Hls.ErrorDetails.LEVEL_LOAD_TIMEOUT:
37095
+ core.Log.error('hlsjs: unrecoverable network fatal error.', {
37096
+ evt,
37097
+ data
37098
+ });
37099
+ formattedError = this.createError(error);
37100
+ this.trigger(core.Events.PLAYBACK_ERROR, formattedError);
37101
+ this.stop();
37102
+ break;
37103
+ default:
37104
+ core.Log.warn('hlsjs: trying to recover from network error.', {
37105
+ evt,
37106
+ data
37107
+ });
37108
+ error.level = core.PlayerError.Levels.WARN;
37109
+ this._hls.startLoad();
37110
+ break;
37111
+ }
37112
+ break;
37113
+ case Hls.ErrorTypes.MEDIA_ERROR:
37114
+ core.Log.warn('hlsjs: trying to recover from media error.', {
37115
+ evt,
37116
+ data
37117
+ });
37118
+ error.level = core.PlayerError.Levels.WARN;
37119
+ this._recover(evt, data, error);
37120
+ break;
37121
+ default:
37122
+ core.Log.error('hlsjs: could not recover from error.', {
37123
+ evt,
37124
+ data
37125
+ });
37126
+ formattedError = this.createError(error);
37127
+ this.trigger(core.Events.PLAYBACK_ERROR, formattedError);
37128
+ this.stop();
37129
+ break;
37386
37130
  }
37387
37131
  } else {
37388
- // Transforms HLSJS.ErrorDetails.KEY_LOAD_ERROR non-fatal error to
37389
- // playback fatal error if triggerFatalErrorOnResourceDenied playback
37390
- // option is set. HLSJS.ErrorTypes.KEY_SYSTEM_ERROR are fatal errors
37391
- // and therefore already handled.
37392
- if (this.options.playback.triggerFatalErrorOnResourceDenied && this._keyIsDenied(data)) {
37393
- core.Log.error('hlsjs: could not load decrypt key.', {
37394
- evt: evt,
37395
- data: data
37396
- });
37397
- formattedError = this.createError(error);
37398
- this.trigger(core.Events.PLAYBACK_ERROR, formattedError);
37399
- this.stop();
37400
- return;
37401
- }
37402
- error.level = core.PlayerError.Levels.WARN;
37403
- core.Log.warn('hlsjs: non-fatal error occurred', {
37404
- evt: evt,
37405
- data: data
37132
+ core.Log.error('hlsjs: could not recover from error after maximum number of attempts.', {
37133
+ evt,
37134
+ data
37406
37135
  });
37136
+ formattedError = this.createError(error);
37137
+ this.trigger(core.Events.PLAYBACK_ERROR, formattedError);
37138
+ this.stop();
37407
37139
  }
37140
+ } else {
37141
+ // Transforms HLSJS.ErrorDetails.KEY_LOAD_ERROR non-fatal error to
37142
+ // playback fatal error if triggerFatalErrorOnResourceDenied playback
37143
+ // option is set. HLSJS.ErrorTypes.KEY_SYSTEM_ERROR are fatal errors
37144
+ // and therefore already handled.
37145
+ if (this.options.playback.triggerFatalErrorOnResourceDenied && this._keyIsDenied(data)) {
37146
+ core.Log.error('hlsjs: could not load decrypt key.', {
37147
+ evt,
37148
+ data
37149
+ });
37150
+ formattedError = this.createError(error);
37151
+ this.trigger(core.Events.PLAYBACK_ERROR, formattedError);
37152
+ this.stop();
37153
+ return;
37154
+ }
37155
+ error.level = core.PlayerError.Levels.WARN;
37156
+ core.Log.warn('hlsjs: non-fatal error occurred', {
37157
+ evt,
37158
+ data
37159
+ });
37408
37160
  }
37409
- }, {
37410
- key: "_keyIsDenied",
37411
- value: function _keyIsDenied(data) {
37412
- return data.type === Hls.ErrorTypes.NETWORK_ERROR && data.details === Hls.ErrorDetails.KEY_LOAD_ERROR && data.response && data.response.code >= 400;
37413
- }
37414
- }, {
37415
- key: "_onTimeUpdate",
37416
- value: function _onTimeUpdate() {
37417
- var update = {
37418
- current: this.getCurrentTime(),
37419
- total: this.getDuration(),
37420
- firstFragDateTime: this.getProgramDateTime()
37421
- };
37422
- var shouldThrottle = this._shouldThrottleTimeUpdate(update);
37423
- if (shouldThrottle) return;
37424
- this._lastTimeUpdate = update;
37425
- this._lastTimeUpdateFiredTime = this._now;
37426
- this.trigger(core.Events.PLAYBACK_TIMEUPDATE, update, this.name);
37427
- }
37428
- }, {
37429
- key: "_shouldThrottleTimeUpdate",
37430
- value: function _shouldThrottleTimeUpdate(update) {
37431
- var isSameTime = Math.abs(update.current - this._lastTimeUpdate.current) < this._timeUpdateFiringRate;
37432
- var isSameDuration = Math.abs(update.total - this._lastTimeUpdate.total) < this._durationChangeMinOffset;
37433
- var isSameFirstFragDateTime = update.firstFragDateTime === this._lastTimeUpdate.firstFragDateTime;
37434
- var isSameEventPayload = isSameTime && isSameDuration && isSameFirstFragDateTime;
37435
- var isThrottled = this._now - this._lastTimeUpdateFiredTime < this._timeUpdateThrottleDelay;
37436
- return isSameEventPayload && isThrottled;
37437
- }
37438
- }, {
37439
- key: "_onDurationChange",
37440
- value: function _onDurationChange() {
37441
- var duration = this.getDuration();
37442
- var isSameDuration = Math.abs(this._lastDuration - duration) < this._durationChangeMinOffset;
37443
- if (isSameDuration) return;
37444
- this._lastDuration = duration;
37445
- _superPropGet(HlsjsPlayback, "_onDurationChange", this, 3)([]);
37161
+ }
37162
+ _keyIsDenied(data) {
37163
+ return data.type === Hls.ErrorTypes.NETWORK_ERROR && data.details === Hls.ErrorDetails.KEY_LOAD_ERROR && data.response && data.response.code >= 400;
37164
+ }
37165
+ _onTimeUpdate() {
37166
+ const update = {
37167
+ current: this.getCurrentTime(),
37168
+ total: this.getDuration(),
37169
+ firstFragDateTime: this.getProgramDateTime()
37170
+ };
37171
+ const shouldThrottle = this._shouldThrottleTimeUpdate(update);
37172
+ if (shouldThrottle) return;
37173
+ this._lastTimeUpdate = update;
37174
+ this._lastTimeUpdateFiredTime = this._now;
37175
+ this.trigger(core.Events.PLAYBACK_TIMEUPDATE, update, this.name);
37176
+ }
37177
+ _shouldThrottleTimeUpdate(update) {
37178
+ const isSameTime = Math.abs(update.current - this._lastTimeUpdate.current) < this._timeUpdateFiringRate;
37179
+ const isSameDuration = Math.abs(update.total - this._lastTimeUpdate.total) < this._durationChangeMinOffset;
37180
+ const isSameFirstFragDateTime = update.firstFragDateTime === this._lastTimeUpdate.firstFragDateTime;
37181
+ const isSameEventPayload = isSameTime && isSameDuration && isSameFirstFragDateTime;
37182
+ const isThrottled = this._now - this._lastTimeUpdateFiredTime < this._timeUpdateThrottleDelay;
37183
+ return isSameEventPayload && isThrottled;
37184
+ }
37185
+ _onDurationChange() {
37186
+ const duration = this.getDuration();
37187
+ const isSameDuration = Math.abs(this._lastDuration - duration) < this._durationChangeMinOffset;
37188
+ if (isSameDuration) return;
37189
+ this._lastDuration = duration;
37190
+ super._onDurationChange();
37191
+ }
37192
+ _onProgress() {
37193
+ if (!this.el.buffered.length) return;
37194
+ let buffered = [];
37195
+ let bufferedPos = 0;
37196
+ for (let i = 0; i < this.el.buffered.length; i++) {
37197
+ buffered = [...buffered, {
37198
+ // for a stream with sliding window dvr something that is buffered my slide off the start of the timeline
37199
+ start: Math.max(0, this.el.buffered.start(i) - this._playableRegionStartTime),
37200
+ end: Math.max(0, this.el.buffered.end(i) - this._playableRegionStartTime)
37201
+ }];
37202
+ if (this.el.currentTime >= buffered[i].start && this.el.currentTime <= buffered[i].end) {
37203
+ bufferedPos = i;
37204
+ }
37205
+ }
37206
+ const progress = {
37207
+ start: buffered[bufferedPos].start,
37208
+ current: buffered[bufferedPos].end,
37209
+ total: this.getDuration()
37210
+ };
37211
+ this.trigger(core.Events.PLAYBACK_PROGRESS, progress, buffered);
37212
+ }
37213
+ load(url) {
37214
+ this._stopTimeUpdateTimer();
37215
+ this.options.src = url;
37216
+ this._setup();
37217
+ }
37218
+ play() {
37219
+ !this._hls && this._setup();
37220
+ !this._manifestLoading && !this.options.hlsPlayback.preload && this._hls.loadSource(this.options.src);
37221
+ super.play();
37222
+ this._startTimeUpdateTimer();
37223
+ }
37224
+ pause() {
37225
+ if (!this._hls) return;
37226
+ this.el.pause();
37227
+ }
37228
+ stop() {
37229
+ this._stopTimeUpdateTimer();
37230
+ if (this._hls) super.stop();
37231
+ this._destroyHLSInstance();
37232
+ }
37233
+ destroy() {
37234
+ this._stopTimeUpdateTimer();
37235
+ this._destroyHLSInstance();
37236
+ super.destroy();
37237
+ }
37238
+ _updatePlaybackType(evt, data) {
37239
+ this._playbackType = data.details.live ? core.Playback.LIVE : core.Playback.VOD;
37240
+ this._onLevelUpdated(evt, data);
37241
+ // Live stream subtitle tracks detection hack (may not immediately available)
37242
+ if (this._ccTracksUpdated && this._playbackType === core.Playback.LIVE && this.hasClosedCaptionsTracks) {
37243
+ this._onSubtitleLoaded();
37446
37244
  }
37447
- }, {
37448
- key: "_onProgress",
37449
- value: function _onProgress() {
37450
- if (!this.el.buffered.length) return;
37451
- var buffered = [];
37452
- var bufferedPos = 0;
37453
- for (var i = 0; i < this.el.buffered.length; i++) {
37454
- buffered = [].concat(_toConsumableArray(buffered), [{
37455
- // for a stream with sliding window dvr something that is buffered my slide off the start of the timeline
37456
- start: Math.max(0, this.el.buffered.start(i) - this._playableRegionStartTime),
37457
- end: Math.max(0, this.el.buffered.end(i) - this._playableRegionStartTime)
37458
- }]);
37459
- if (this.el.currentTime >= buffered[i].start && this.el.currentTime <= buffered[i].end) {
37460
- bufferedPos = i;
37461
- }
37462
- }
37463
- var progress = {
37464
- start: buffered[bufferedPos].start,
37465
- current: buffered[bufferedPos].end,
37466
- total: this.getDuration()
37245
+ }
37246
+ _fillLevels() {
37247
+ this._levels = this._hls.levels.map((level, index) => {
37248
+ return {
37249
+ id: index,
37250
+ level: level,
37251
+ label: `${level.bitrate / 1000}Kbps`
37467
37252
  };
37468
- this.trigger(core.Events.PLAYBACK_PROGRESS, progress, buffered);
37469
- }
37470
- }, {
37471
- key: "load",
37472
- value: function load(url) {
37473
- this._stopTimeUpdateTimer();
37474
- this.options.src = url;
37475
- this._setup();
37476
- }
37477
- }, {
37478
- key: "play",
37479
- value: function play() {
37480
- !this._hls && this._setup();
37481
- !this._manifestLoading && !this.options.hlsPlayback.preload && this._hls.loadSource(this.options.src);
37482
- _superPropGet(HlsjsPlayback, "play", this, 3)([]);
37483
- this._startTimeUpdateTimer();
37484
- }
37485
- }, {
37486
- key: "pause",
37487
- value: function pause() {
37488
- if (!this._hls) return;
37489
- this.el.pause();
37490
- }
37491
- }, {
37492
- key: "stop",
37493
- value: function stop() {
37494
- this._stopTimeUpdateTimer();
37495
- if (this._hls) _superPropGet(HlsjsPlayback, "stop", this, 3)([]);
37496
- this._destroyHLSInstance();
37497
- }
37498
- }, {
37499
- key: "destroy",
37500
- value: function destroy() {
37501
- this._stopTimeUpdateTimer();
37502
- this._destroyHLSInstance();
37503
- _superPropGet(HlsjsPlayback, "destroy", this, 3)([]);
37504
- }
37505
- }, {
37506
- key: "_updatePlaybackType",
37507
- value: function _updatePlaybackType(evt, data) {
37508
- this._playbackType = data.details.live ? core.Playback.LIVE : core.Playback.VOD;
37509
- this._onLevelUpdated(evt, data);
37510
- // Live stream subtitle tracks detection hack (may not immediately available)
37511
- if (this._ccTracksUpdated && this._playbackType === core.Playback.LIVE && this.hasClosedCaptionsTracks) {
37512
- this._onSubtitleLoaded();
37513
- }
37514
- }
37515
- }, {
37516
- key: "_fillLevels",
37517
- value: function _fillLevels() {
37518
- this._levels = this._hls.levels.map(function (level, index) {
37519
- return {
37520
- id: index,
37521
- level: level,
37522
- label: "".concat(level.bitrate / 1000, "Kbps")
37253
+ });
37254
+ this.trigger(core.Events.PLAYBACK_LEVELS_AVAILABLE, this._levels);
37255
+ }
37256
+ _onLevelUpdated(evt, data) {
37257
+ this._segmentTargetDuration = data.details.targetduration;
37258
+ this._playlistType = data.details.type || null;
37259
+ let startTimeChanged = false;
37260
+ let durationChanged = false;
37261
+ const fragments = data.details.fragments;
37262
+ const previousPlayableRegionStartTime = this._playableRegionStartTime;
37263
+ const previousPlayableRegionDuration = this._playableRegionDuration;
37264
+ if (fragments.length === 0) return;
37265
+ // #EXT-X-PROGRAM-DATE-TIME
37266
+ if (fragments[0].rawProgramDateTime) {
37267
+ this._programDateTime = fragments[0].rawProgramDateTime;
37268
+ }
37269
+ if (this._playableRegionStartTime !== fragments[0].start) {
37270
+ startTimeChanged = true;
37271
+ this._playableRegionStartTime = fragments[0].start;
37272
+ }
37273
+ if (startTimeChanged) {
37274
+ if (!this._localStartTimeCorrelation) {
37275
+ // set the correlation to map to middle of the extrapolation window
37276
+ this._localStartTimeCorrelation = {
37277
+ local: this._now,
37278
+ remote: (fragments[0].start + this._extrapolatedWindowDuration / 2) * 1000
37523
37279
  };
37524
- });
37525
- this.trigger(core.Events.PLAYBACK_LEVELS_AVAILABLE, this._levels);
37526
- }
37527
- }, {
37528
- key: "_onLevelUpdated",
37529
- value: function _onLevelUpdated(evt, data) {
37530
- this._segmentTargetDuration = data.details.targetduration;
37531
- this._playlistType = data.details.type || null;
37532
- var startTimeChanged = false;
37533
- var durationChanged = false;
37534
- var fragments = data.details.fragments;
37535
- var previousPlayableRegionStartTime = this._playableRegionStartTime;
37536
- var previousPlayableRegionDuration = this._playableRegionDuration;
37537
- if (fragments.length === 0) return;
37538
- // #EXT-X-PROGRAM-DATE-TIME
37539
- if (fragments[0].rawProgramDateTime) {
37540
- this._programDateTime = fragments[0].rawProgramDateTime;
37541
- }
37542
- if (this._playableRegionStartTime !== fragments[0].start) {
37543
- startTimeChanged = true;
37544
- this._playableRegionStartTime = fragments[0].start;
37545
- }
37546
- if (startTimeChanged) {
37547
- if (!this._localStartTimeCorrelation) {
37548
- // set the correlation to map to middle of the extrapolation window
37280
+ } else {
37281
+ // check if the correlation still works
37282
+ const corr = this._localStartTimeCorrelation;
37283
+ const timePassed = this._now - corr.local;
37284
+ // this should point to a time within the extrapolation window
37285
+ const startTime = (corr.remote + timePassed) / 1000;
37286
+ if (startTime < fragments[0].start) {
37287
+ // our start time is now earlier than the first chunk
37288
+ // (maybe the chunk was removed early)
37289
+ // reset correlation so that it sits at the beginning of the first available chunk
37549
37290
  this._localStartTimeCorrelation = {
37550
37291
  local: this._now,
37551
- remote: (fragments[0].start + this._extrapolatedWindowDuration / 2) * 1000
37292
+ remote: fragments[0].start * 1000
37293
+ };
37294
+ } else if (startTime > previousPlayableRegionStartTime + this._extrapolatedWindowDuration) {
37295
+ // start time was past the end of the old extrapolation window (so would have been capped)
37296
+ // see if now that time would be inside the window, and if it would be set the correlation
37297
+ // so that it resumes from the time it was at at the end of the old window
37298
+ // update the correlation so that the time starts counting again from the value it's on now
37299
+ this._localStartTimeCorrelation = {
37300
+ local: this._now,
37301
+ remote: Math.max(fragments[0].start, previousPlayableRegionStartTime + this._extrapolatedWindowDuration) * 1000
37552
37302
  };
37553
- } else {
37554
- // check if the correlation still works
37555
- var corr = this._localStartTimeCorrelation;
37556
- var timePassed = this._now - corr.local;
37557
- // this should point to a time within the extrapolation window
37558
- var startTime = (corr.remote + timePassed) / 1000;
37559
- if (startTime < fragments[0].start) {
37560
- // our start time is now earlier than the first chunk
37561
- // (maybe the chunk was removed early)
37562
- // reset correlation so that it sits at the beginning of the first available chunk
37563
- this._localStartTimeCorrelation = {
37564
- local: this._now,
37565
- remote: fragments[0].start * 1000
37566
- };
37567
- } else if (startTime > previousPlayableRegionStartTime + this._extrapolatedWindowDuration) {
37568
- // start time was past the end of the old extrapolation window (so would have been capped)
37569
- // see if now that time would be inside the window, and if it would be set the correlation
37570
- // so that it resumes from the time it was at at the end of the old window
37571
- // update the correlation so that the time starts counting again from the value it's on now
37572
- this._localStartTimeCorrelation = {
37573
- local: this._now,
37574
- remote: Math.max(fragments[0].start, previousPlayableRegionStartTime + this._extrapolatedWindowDuration) * 1000
37575
- };
37576
- }
37577
- }
37578
- }
37579
- var newDuration = data.details.totalduration;
37580
- // if it's a live stream then shorten the duration to remove access
37581
- // to the area after hlsjs's live sync point
37582
- // seeks to areas after this point sometimes have issues
37583
- if (this._playbackType === core.Playback.LIVE) {
37584
- var fragmentTargetDuration = data.details.targetduration;
37585
- var hlsjsConfig = this.options.playback.hlsjsConfig || {};
37586
- var liveSyncDurationCount = hlsjsConfig.liveSyncDurationCount || Hls.DefaultConfig.liveSyncDurationCount;
37587
- var hiddenAreaDuration = fragmentTargetDuration * liveSyncDurationCount;
37588
- if (hiddenAreaDuration <= newDuration) {
37589
- newDuration -= hiddenAreaDuration;
37590
- this._durationExcludesAfterLiveSyncPoint = true;
37591
- } else {
37592
- this._durationExcludesAfterLiveSyncPoint = false;
37593
37303
  }
37594
37304
  }
37595
- if (newDuration !== this._playableRegionDuration) {
37596
- durationChanged = true;
37597
- this._playableRegionDuration = newDuration;
37598
- }
37599
- // Note the end time is not the playableRegionDuration
37600
- // The end time will always increase even if content is removed from the beginning
37601
- var endTime = fragments[0].start + newDuration;
37602
- var previousEndTime = previousPlayableRegionStartTime + previousPlayableRegionDuration;
37603
- var endTimeChanged = endTime !== previousEndTime;
37604
- if (endTimeChanged) {
37605
- if (!this._localEndTimeCorrelation) {
37606
- // set the correlation to map to the end
37305
+ }
37306
+ let newDuration = data.details.totalduration;
37307
+ // if it's a live stream then shorten the duration to remove access
37308
+ // to the area after hlsjs's live sync point
37309
+ // seeks to areas after this point sometimes have issues
37310
+ if (this._playbackType === core.Playback.LIVE) {
37311
+ const fragmentTargetDuration = data.details.targetduration;
37312
+ const hlsjsConfig = this.options.playback.hlsjsConfig || {};
37313
+ const liveSyncDurationCount = hlsjsConfig.liveSyncDurationCount || Hls.DefaultConfig.liveSyncDurationCount;
37314
+ const hiddenAreaDuration = fragmentTargetDuration * liveSyncDurationCount;
37315
+ if (hiddenAreaDuration <= newDuration) {
37316
+ newDuration -= hiddenAreaDuration;
37317
+ this._durationExcludesAfterLiveSyncPoint = true;
37318
+ } else {
37319
+ this._durationExcludesAfterLiveSyncPoint = false;
37320
+ }
37321
+ }
37322
+ if (newDuration !== this._playableRegionDuration) {
37323
+ durationChanged = true;
37324
+ this._playableRegionDuration = newDuration;
37325
+ }
37326
+ // Note the end time is not the playableRegionDuration
37327
+ // The end time will always increase even if content is removed from the beginning
37328
+ const endTime = fragments[0].start + newDuration;
37329
+ const previousEndTime = previousPlayableRegionStartTime + previousPlayableRegionDuration;
37330
+ const endTimeChanged = endTime !== previousEndTime;
37331
+ if (endTimeChanged) {
37332
+ if (!this._localEndTimeCorrelation) {
37333
+ // set the correlation to map to the end
37334
+ this._localEndTimeCorrelation = {
37335
+ local: this._now,
37336
+ remote: endTime * 1000
37337
+ };
37338
+ } else {
37339
+ // check if the correlation still works
37340
+ const corr = this._localEndTimeCorrelation;
37341
+ const timePassed = this._now - corr.local;
37342
+ // this should point to a time within the extrapolation window from the end
37343
+ const extrapolatedEndTime = (corr.remote + timePassed) / 1000;
37344
+ if (extrapolatedEndTime > endTime) {
37607
37345
  this._localEndTimeCorrelation = {
37608
37346
  local: this._now,
37609
37347
  remote: endTime * 1000
37610
37348
  };
37611
- } else {
37612
- // check if the correlation still works
37613
- var _corr = this._localEndTimeCorrelation;
37614
- var _timePassed = this._now - _corr.local;
37615
- // this should point to a time within the extrapolation window from the end
37616
- var extrapolatedEndTime = (_corr.remote + _timePassed) / 1000;
37617
- if (extrapolatedEndTime > endTime) {
37618
- this._localEndTimeCorrelation = {
37619
- local: this._now,
37620
- remote: endTime * 1000
37621
- };
37622
- } else if (extrapolatedEndTime < endTime - this._extrapolatedWindowDuration) {
37623
- // our extrapolated end time is now earlier than the extrapolation window from the actual end time
37624
- // (maybe a chunk became available early)
37625
- // reset correlation so that it sits at the beginning of the extrapolation window from the end time
37626
- this._localEndTimeCorrelation = {
37627
- local: this._now,
37628
- remote: (endTime - this._extrapolatedWindowDuration) * 1000
37629
- };
37630
- } else if (extrapolatedEndTime > previousEndTime) {
37631
- // end time was past the old end time (so would have been capped)
37632
- // set the correlation so that it resumes from the time it was at at the end of the old window
37633
- this._localEndTimeCorrelation = {
37634
- local: this._now,
37635
- remote: previousEndTime * 1000
37636
- };
37637
- }
37349
+ } else if (extrapolatedEndTime < endTime - this._extrapolatedWindowDuration) {
37350
+ // our extrapolated end time is now earlier than the extrapolation window from the actual end time
37351
+ // (maybe a chunk became available early)
37352
+ // reset correlation so that it sits at the beginning of the extrapolation window from the end time
37353
+ this._localEndTimeCorrelation = {
37354
+ local: this._now,
37355
+ remote: (endTime - this._extrapolatedWindowDuration) * 1000
37356
+ };
37357
+ } else if (extrapolatedEndTime > previousEndTime) {
37358
+ // end time was past the old end time (so would have been capped)
37359
+ // set the correlation so that it resumes from the time it was at at the end of the old window
37360
+ this._localEndTimeCorrelation = {
37361
+ local: this._now,
37362
+ remote: previousEndTime * 1000
37363
+ };
37638
37364
  }
37639
37365
  }
37640
-
37641
- // now that the values have been updated call any methods that use on them so they get the updated values
37642
- // immediately
37643
- durationChanged && this._onDurationChange();
37644
- startTimeChanged && this._onProgress();
37645
- }
37646
- }, {
37647
- key: "_onFragmentChanged",
37648
- value: function _onFragmentChanged(evt, data) {
37649
- this._currentFragment = data.frag;
37650
- this.trigger(core.Events.Custom.PLAYBACK_FRAGMENT_CHANGED, data);
37651
- }
37652
- }, {
37653
- key: "_onFragmentLoaded",
37654
- value: function _onFragmentLoaded(evt, data) {
37655
- this.trigger(core.Events.PLAYBACK_FRAGMENT_LOADED, data);
37656
- }
37657
- }, {
37658
- key: "_onFragmentBuffered",
37659
- value: function _onFragmentBuffered(evt, data) {
37660
- this.trigger(core.Events.PLAYBACK_FRAGMENT_BUFFERED, data);
37661
- }
37662
- }, {
37663
- key: "_onSubtitleLoaded",
37664
- value: function _onSubtitleLoaded() {
37665
- // This event may be triggered multiple times
37666
- // Setup CC only once (disable CC by default)
37667
- if (!this._ccIsSetup) {
37668
- this.trigger(core.Events.PLAYBACK_SUBTITLE_AVAILABLE);
37669
- var trackId = this._playbackType === core.Playback.LIVE ? -1 : this.closedCaptionsTrackId;
37670
- this.closedCaptionsTrackId = trackId;
37671
- this._ccIsSetup = true;
37672
- }
37673
- }
37674
- }, {
37675
- key: "_onLevelSwitch",
37676
- value: function _onLevelSwitch(evt, data) {
37677
- if (!this.levels.length) this._fillLevels();
37678
- this.trigger(core.Events.PLAYBACK_LEVEL_SWITCH_END);
37679
- this.trigger(core.Events.PLAYBACK_LEVEL_SWITCH, data);
37680
- var currentLevel = this._hls.levels[data.level];
37681
- if (currentLevel) {
37682
- // TODO should highDefinition be private and maybe have a read only accessor if it's used somewhere
37683
- this.highDefinition = currentLevel.height >= 720 || currentLevel.bitrate / 1000 >= 2000;
37684
- this.trigger(core.Events.PLAYBACK_HIGHDEFINITIONUPDATE, this.highDefinition);
37685
- this.trigger(core.Events.PLAYBACK_BITRATE, {
37686
- height: currentLevel.height,
37687
- width: currentLevel.width,
37688
- bandwidth: currentLevel.bitrate,
37689
- bitrate: currentLevel.bitrate,
37690
- level: data.level
37691
- });
37692
- }
37693
- }
37694
- }, {
37695
- key: "dvrEnabled",
37696
- get: function get() {
37697
- // enabled when:
37698
- // - the duration does not include content after hlsjs's live sync point
37699
- // - the playable region duration is longer than the configured duration to enable dvr after
37700
- // - the playback type is LIVE.
37701
- return this._durationExcludesAfterLiveSyncPoint && this._duration >= this._minDvrSize && this.getPlaybackType() === core.Playback.LIVE;
37702
37366
  }
37703
- }, {
37704
- key: "getPlaybackType",
37705
- value: function getPlaybackType() {
37706
- return this._playbackType;
37707
- }
37708
- }, {
37709
- key: "isSeekEnabled",
37710
- value: function isSeekEnabled() {
37711
- return this._playbackType === core.Playback.VOD || this.dvrEnabled;
37712
- }
37713
- }], [{
37714
- key: "HLSJS",
37715
- get: function get() {
37716
- return Hls;
37367
+
37368
+ // now that the values have been updated call any methods that use on them so they get the updated values
37369
+ // immediately
37370
+ durationChanged && this._onDurationChange();
37371
+ startTimeChanged && this._onProgress();
37372
+ }
37373
+ _onFragmentChanged(evt, data) {
37374
+ this._currentFragment = data.frag;
37375
+ this.trigger(core.Events.Custom.PLAYBACK_FRAGMENT_CHANGED, data);
37376
+ }
37377
+ _onFragmentLoaded(evt, data) {
37378
+ this.trigger(core.Events.PLAYBACK_FRAGMENT_LOADED, data);
37379
+ }
37380
+ _onFragmentBuffered(evt, data) {
37381
+ this.trigger(core.Events.PLAYBACK_FRAGMENT_BUFFERED, data);
37382
+ }
37383
+ _onSubtitleLoaded() {
37384
+ // This event may be triggered multiple times
37385
+ // Setup CC only once (disable CC by default)
37386
+ if (!this._ccIsSetup) {
37387
+ this.trigger(core.Events.PLAYBACK_SUBTITLE_AVAILABLE);
37388
+ const trackId = this._playbackType === core.Playback.LIVE ? -1 : this.closedCaptionsTrackId;
37389
+ this.closedCaptionsTrackId = trackId;
37390
+ this._ccIsSetup = true;
37391
+ }
37392
+ }
37393
+ _onLevelSwitch(evt, data) {
37394
+ if (!this.levels.length) this._fillLevels();
37395
+ this.trigger(core.Events.PLAYBACK_LEVEL_SWITCH_END);
37396
+ this.trigger(core.Events.PLAYBACK_LEVEL_SWITCH, data);
37397
+ const currentLevel = this._hls.levels[data.level];
37398
+ if (currentLevel) {
37399
+ // TODO should highDefinition be private and maybe have a read only accessor if it's used somewhere
37400
+ this.highDefinition = currentLevel.height >= 720 || currentLevel.bitrate / 1000 >= 2000;
37401
+ this.trigger(core.Events.PLAYBACK_HIGHDEFINITIONUPDATE, this.highDefinition);
37402
+ this.trigger(core.Events.PLAYBACK_BITRATE, {
37403
+ height: currentLevel.height,
37404
+ width: currentLevel.width,
37405
+ bandwidth: currentLevel.bitrate,
37406
+ bitrate: currentLevel.bitrate,
37407
+ level: data.level
37408
+ });
37717
37409
  }
37718
- }]);
37719
- }(core.HTML5Video);
37410
+ }
37411
+ get dvrEnabled() {
37412
+ // enabled when:
37413
+ // - the duration does not include content after hlsjs's live sync point
37414
+ // - the playable region duration is longer than the configured duration to enable dvr after
37415
+ // - the playback type is LIVE.
37416
+ return this._durationExcludesAfterLiveSyncPoint && this._duration >= this._minDvrSize && this.getPlaybackType() === core.Playback.LIVE;
37417
+ }
37418
+ getPlaybackType() {
37419
+ return this._playbackType;
37420
+ }
37421
+ isSeekEnabled() {
37422
+ return this._playbackType === core.Playback.VOD || this.dvrEnabled;
37423
+ }
37424
+ }
37720
37425
  HlsjsPlayback.canPlay = function (resource, mimeType) {
37721
- var resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || [];
37722
- var isHls = resourceParts.length > 1 && resourceParts[1].toLowerCase() === 'm3u8' || listContainsIgnoreCase(mimeType, ['application/vnd.apple.mpegurl', 'application/x-mpegURL']);
37426
+ const resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || [];
37427
+ const isHls = resourceParts.length > 1 && resourceParts[1].toLowerCase() === 'm3u8' || listContainsIgnoreCase(mimeType, ['application/vnd.apple.mpegurl', 'application/x-mpegURL']);
37723
37428
  return !!(Hls.isSupported() && isHls);
37724
37429
  };
37725
37430