stormcloud-video-player 0.8.76 → 0.8.78
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/dist/stormcloud-vp.min.js +1 -1
- package/lib/Player.cjs +2 -0
- package/lib/Player.cjs.map +1 -1
- package/lib/Player.d.cts +9 -1
- package/lib/StormcloudPlayer.cjs +235 -6
- package/lib/StormcloudPlayer.cjs.map +1 -1
- package/lib/index.cjs +235 -6
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.ts +6 -0
- package/lib/index.js +236 -6
- package/lib/index.js.map +1 -1
- package/lib/player/ContentHealthWatchdog.cjs +255 -0
- package/lib/player/ContentHealthWatchdog.cjs.map +1 -0
- package/lib/player/ContentHealthWatchdog.d.cts +40 -0
- package/lib/player/StormcloudVideoPlayer.cjs +225 -6
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/player/StormcloudVideoPlayer.d.cts +1 -0
- package/lib/players/HlsPlayer.cjs +231 -6
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/index.cjs +231 -6
- package/lib/players/index.cjs.map +1 -1
- package/lib/props.cjs +2 -0
- package/lib/props.cjs.map +1 -1
- package/lib/props.d.cts +5 -1
- package/lib/sdk/hlsAdPlayer.cjs +16 -6
- package/lib/sdk/hlsAdPlayer.cjs.map +1 -1
- package/lib/types.d.cts +1 -0
- package/lib/ui/StormcloudVideoPlayer.cjs +225 -6
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/utils/browserCompat.cjs +7 -0
- package/lib/utils/browserCompat.cjs.map +1 -1
- package/lib/utils/browserCompat.d.cts +2 -1
- package/package.json +2 -2
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
function _class_call_check(instance, Constructor) {
|
|
3
|
+
if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
|
|
4
|
+
}
|
|
5
|
+
function _defineProperties(target, props) {
|
|
6
|
+
for(var i = 0; i < props.length; i++){
|
|
7
|
+
var descriptor = props[i];
|
|
8
|
+
descriptor.enumerable = descriptor.enumerable || false;
|
|
9
|
+
descriptor.configurable = true;
|
|
10
|
+
if ("value" in descriptor) descriptor.writable = true;
|
|
11
|
+
Object.defineProperty(target, descriptor.key, descriptor);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function _create_class(Constructor, protoProps, staticProps) {
|
|
15
|
+
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
|
|
16
|
+
if (staticProps) _defineProperties(Constructor, staticProps);
|
|
17
|
+
return Constructor;
|
|
18
|
+
}
|
|
19
|
+
function _type_of(obj) {
|
|
20
|
+
"@swc/helpers - typeof";
|
|
21
|
+
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
|
|
22
|
+
}
|
|
23
|
+
var __defProp = Object.defineProperty;
|
|
24
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
25
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
26
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
27
|
+
var __export = function __export(target, all) {
|
|
28
|
+
for(var name in all)__defProp(target, name, {
|
|
29
|
+
get: all[name],
|
|
30
|
+
enumerable: true
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
var __copyProps = function __copyProps(to, from, except, desc) {
|
|
34
|
+
if (from && (typeof from === "undefined" ? "undefined" : _type_of(from)) === "object" || typeof from === "function") {
|
|
35
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
36
|
+
try {
|
|
37
|
+
var _loop = function() {
|
|
38
|
+
var key = _step.value;
|
|
39
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
40
|
+
get: function get() {
|
|
41
|
+
return from[key];
|
|
42
|
+
},
|
|
43
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
for(var _iterator = __getOwnPropNames(from)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true)_loop();
|
|
47
|
+
} catch (err) {
|
|
48
|
+
_didIteratorError = true;
|
|
49
|
+
_iteratorError = err;
|
|
50
|
+
} finally{
|
|
51
|
+
try {
|
|
52
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
53
|
+
_iterator.return();
|
|
54
|
+
}
|
|
55
|
+
} finally{
|
|
56
|
+
if (_didIteratorError) {
|
|
57
|
+
throw _iteratorError;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return to;
|
|
63
|
+
};
|
|
64
|
+
var __toCommonJS = function __toCommonJS(mod) {
|
|
65
|
+
return __copyProps(__defProp({}, "__esModule", {
|
|
66
|
+
value: true
|
|
67
|
+
}), mod);
|
|
68
|
+
};
|
|
69
|
+
// src/player/ContentHealthWatchdog.ts
|
|
70
|
+
var ContentHealthWatchdog_exports = {};
|
|
71
|
+
__export(ContentHealthWatchdog_exports, {
|
|
72
|
+
ContentHealthWatchdog: function ContentHealthWatchdog1() {
|
|
73
|
+
return ContentHealthWatchdog;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
module.exports = __toCommonJS(ContentHealthWatchdog_exports);
|
|
77
|
+
var POLL_INTERVAL_MS = 2e3;
|
|
78
|
+
var STALL_THRESHOLD_MS = 12e3;
|
|
79
|
+
var POST_BREAK_GRACE_MS = 5e4;
|
|
80
|
+
var MAX_BREAK_SUPPRESSION_MS = 10 * 60 * 1e3;
|
|
81
|
+
var SELF_INFLICTED_PAUSE_WINDOW_MS = 5e3;
|
|
82
|
+
var HEALTHY_PLAYBACK_SECONDS = 5;
|
|
83
|
+
var MAX_NORMAL_ADVANCE_PER_TICK_S = 4;
|
|
84
|
+
var LIGHT_STEP_COOLDOWN_MS = 1e4;
|
|
85
|
+
var RELOAD_BASE_COOLDOWN_MS = 15e3;
|
|
86
|
+
var RELOAD_MAX_COOLDOWN_MS = 12e4;
|
|
87
|
+
var ContentHealthWatchdog = /*#__PURE__*/ function() {
|
|
88
|
+
function ContentHealthWatchdog(host) {
|
|
89
|
+
var _this = this;
|
|
90
|
+
_class_call_check(this, ContentHealthWatchdog);
|
|
91
|
+
this.host = host;
|
|
92
|
+
this.armed = false;
|
|
93
|
+
this.userPaused = false;
|
|
94
|
+
this.wasInBreak = false;
|
|
95
|
+
this.breakStartedAt = 0;
|
|
96
|
+
this.stuckBreakLogged = false;
|
|
97
|
+
this.graceUntil = 0;
|
|
98
|
+
this.lastPosition = 0;
|
|
99
|
+
this.lastProgressAt = 0;
|
|
100
|
+
this.lastRealPlaybackAt = 0;
|
|
101
|
+
this.healthySeconds = 0;
|
|
102
|
+
this.step = 0;
|
|
103
|
+
this.nextActionAt = 0;
|
|
104
|
+
this.lastActionAt = 0;
|
|
105
|
+
this.onPlaying = function() {
|
|
106
|
+
if (_this.host.isAdPlaying()) return;
|
|
107
|
+
_this.armed = true;
|
|
108
|
+
_this.userPaused = false;
|
|
109
|
+
};
|
|
110
|
+
this.onPause = function() {
|
|
111
|
+
if (_this.host.isBreakActive() || _this.host.isAdPlaying() || Date.now() - _this.lastActionAt < SELF_INFLICTED_PAUSE_WINDOW_MS) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
_this.userPaused = true;
|
|
115
|
+
};
|
|
116
|
+
this.onPlay = function() {
|
|
117
|
+
_this.userPaused = false;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
_create_class(ContentHealthWatchdog, [
|
|
121
|
+
{
|
|
122
|
+
key: "start",
|
|
123
|
+
value: function start() {
|
|
124
|
+
var _this = this;
|
|
125
|
+
if (this.intervalId !== void 0) return;
|
|
126
|
+
var video = this.host.video;
|
|
127
|
+
video.addEventListener("playing", this.onPlaying);
|
|
128
|
+
video.addEventListener("pause", this.onPause);
|
|
129
|
+
video.addEventListener("play", this.onPlay);
|
|
130
|
+
this.resetProgress(Date.now());
|
|
131
|
+
this.intervalId = window.setInterval(function() {
|
|
132
|
+
return _this.tick();
|
|
133
|
+
}, POLL_INTERVAL_MS);
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
key: "stop",
|
|
138
|
+
value: function stop() {
|
|
139
|
+
if (this.intervalId !== void 0) {
|
|
140
|
+
clearInterval(this.intervalId);
|
|
141
|
+
this.intervalId = void 0;
|
|
142
|
+
}
|
|
143
|
+
var video = this.host.video;
|
|
144
|
+
video.removeEventListener("playing", this.onPlaying);
|
|
145
|
+
video.removeEventListener("pause", this.onPause);
|
|
146
|
+
video.removeEventListener("play", this.onPlay);
|
|
147
|
+
this.armed = false;
|
|
148
|
+
this.step = 0;
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
key: "resetProgress",
|
|
153
|
+
value: function resetProgress(now) {
|
|
154
|
+
this.lastPosition = this.host.video.currentTime;
|
|
155
|
+
this.lastProgressAt = now;
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
key: "tick",
|
|
160
|
+
value: function tick() {
|
|
161
|
+
var now = Date.now();
|
|
162
|
+
var video = this.host.video;
|
|
163
|
+
var hidden = typeof document !== "undefined" && document.visibilityState === "hidden";
|
|
164
|
+
if (!this.armed || this.userPaused || hidden || video.ended || !this.host.isLiveStream() || !this.host.hasHlsInstance()) {
|
|
165
|
+
this.resetProgress(now);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
var breakActive = this.host.isBreakActive() || this.host.isAdPlaying();
|
|
169
|
+
if (breakActive && !this.wasInBreak) {
|
|
170
|
+
this.wasInBreak = true;
|
|
171
|
+
this.breakStartedAt = now;
|
|
172
|
+
this.stuckBreakLogged = false;
|
|
173
|
+
}
|
|
174
|
+
if (breakActive && now - this.breakStartedAt < MAX_BREAK_SUPPRESSION_MS) {
|
|
175
|
+
this.resetProgress(now);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (breakActive && !this.stuckBreakLogged) {
|
|
179
|
+
this.stuckBreakLogged = true;
|
|
180
|
+
console.warn("[ContentWatchdog] Ad break state active for over ".concat(MAX_BREAK_SUPPRESSION_MS / 6e4, " min — monitoring content anyway"));
|
|
181
|
+
}
|
|
182
|
+
if (!breakActive && this.wasInBreak) {
|
|
183
|
+
this.wasInBreak = false;
|
|
184
|
+
this.graceUntil = now + POST_BREAK_GRACE_MS;
|
|
185
|
+
this.resetProgress(now);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
var currentTime = video.currentTime;
|
|
189
|
+
var delta = currentTime - this.lastPosition;
|
|
190
|
+
this.lastPosition = currentTime;
|
|
191
|
+
if (delta > 0.25 && delta <= MAX_NORMAL_ADVANCE_PER_TICK_S && !video.paused) {
|
|
192
|
+
this.lastProgressAt = now;
|
|
193
|
+
this.lastRealPlaybackAt = now;
|
|
194
|
+
this.healthySeconds += delta;
|
|
195
|
+
if (this.step > 0 && this.healthySeconds >= HEALTHY_PLAYBACK_SECONDS) {
|
|
196
|
+
console.log("[ContentWatchdog] Content playback recovered after ".concat(this.step, " recovery step(s)"));
|
|
197
|
+
this.step = 0;
|
|
198
|
+
this.nextActionAt = 0;
|
|
199
|
+
}
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (now < this.graceUntil) {
|
|
203
|
+
this.lastProgressAt = now;
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (now - this.lastProgressAt < STALL_THRESHOLD_MS || now < this.nextActionAt) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
this.recover(now);
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
key: "recover",
|
|
214
|
+
value: function recover(now) {
|
|
215
|
+
var video = this.host.video;
|
|
216
|
+
var state = "t=".concat(video.currentTime.toFixed(2), " paused=").concat(video.paused, " readyState=").concat(video.readyState, " networkState=").concat(video.networkState, " error=").concat(video.error ? video.error.code : "none");
|
|
217
|
+
var stalledForS = ((now - (this.lastRealPlaybackAt || this.lastProgressAt)) / 1e3).toFixed(0);
|
|
218
|
+
var fullReloadOnly = this.host.requiresMediaPipelineResetAfterAds();
|
|
219
|
+
var action = fullReloadOnly || this.step >= 2 ? "reload" : this.step === 0 ? "recover_media_error" : "restart_load";
|
|
220
|
+
console.warn("[ContentWatchdog] Content stalled for ".concat(stalledForS, "s — ").concat(action, " (step ").concat(this.step + 1, ") — ").concat(state));
|
|
221
|
+
this.lastActionAt = now;
|
|
222
|
+
this.healthySeconds = 0;
|
|
223
|
+
if (action === "recover_media_error") {
|
|
224
|
+
this.host.recoverMediaError();
|
|
225
|
+
this.nextActionAt = now + LIGHT_STEP_COOLDOWN_MS;
|
|
226
|
+
} else if (action === "restart_load") {
|
|
227
|
+
var liveSyncPos = this.host.getLiveSyncPosition();
|
|
228
|
+
var position = liveSyncPos != null && liveSyncPos > video.currentTime ? liveSyncPos : video.currentTime;
|
|
229
|
+
if (position !== video.currentTime) {
|
|
230
|
+
video.currentTime = position;
|
|
231
|
+
}
|
|
232
|
+
this.host.restartHlsLoad(position);
|
|
233
|
+
this.nextActionAt = now + LIGHT_STEP_COOLDOWN_MS;
|
|
234
|
+
} else {
|
|
235
|
+
var reloadIndex = fullReloadOnly ? this.step : this.step - 2;
|
|
236
|
+
var cooldown = Math.min(RELOAD_BASE_COOLDOWN_MS * Math.pow(2, reloadIndex), RELOAD_MAX_COOLDOWN_MS);
|
|
237
|
+
this.host.reloadLiveStream();
|
|
238
|
+
this.nextActionAt = now + cooldown;
|
|
239
|
+
}
|
|
240
|
+
this.step++;
|
|
241
|
+
this.resetProgress(now);
|
|
242
|
+
if (video.paused) {
|
|
243
|
+
var _video_play;
|
|
244
|
+
(_video_play = video.play()) === null || _video_play === void 0 ? void 0 : _video_play.catch(function() {});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
]);
|
|
249
|
+
return ContentHealthWatchdog;
|
|
250
|
+
}();
|
|
251
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
252
|
+
0 && (module.exports = {
|
|
253
|
+
ContentHealthWatchdog: ContentHealthWatchdog
|
|
254
|
+
});
|
|
255
|
+
//# sourceMappingURL=ContentHealthWatchdog.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["d:\\project\\stormcloud-vp\\lib\\player\\ContentHealthWatchdog.cjs"],"names":["__defProp","Object","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","getOwnPropertyNames","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","__copyProps","to","from","except","desc","key","call","__toCommonJS","mod","value","ContentHealthWatchdog_exports","ContentHealthWatchdog","module","exports","POLL_INTERVAL_MS","STALL_THRESHOLD_MS","POST_BREAK_GRACE_MS","MAX_BREAK_SUPPRESSION_MS","SELF_INFLICTED_PAUSE_WINDOW_MS","HEALTHY_PLAYBACK_SECONDS","MAX_NORMAL_ADVANCE_PER_TICK_S","LIGHT_STEP_COOLDOWN_MS","RELOAD_BASE_COOLDOWN_MS","RELOAD_MAX_COOLDOWN_MS","host","armed","userPaused","wasInBreak","breakStartedAt","stuckBreakLogged","graceUntil","lastPosition","lastProgressAt","lastRealPlaybackAt","healthySeconds","step","nextActionAt","lastActionAt","onPlaying","isAdPlaying","onPause","isBreakActive","Date","now","onPlay","start","intervalId","video","addEventListener","resetProgress","window","setInterval","tick","stop","clearInterval","removeEventListener","currentTime","hidden","document","visibilityState","ended","isLiveStream","hasHlsInstance","breakActive","console","warn","delta","paused","log","recover","state","toFixed","readyState","networkState","error","code","stalledForS","fullReloadOnly","requiresMediaPipelineResetAfterAds","action","recoverMediaError","liveSyncPos","getLiveSyncPosition","position","restartHlsLoad","reloadIndex","cooldown","Math","min","pow","reloadLiveStream","play","catch"],"mappings":"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","sourcesContent":["\"use strict\";\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/player/ContentHealthWatchdog.ts\nvar ContentHealthWatchdog_exports = {};\n__export(ContentHealthWatchdog_exports, {\n ContentHealthWatchdog: () => ContentHealthWatchdog\n});\nmodule.exports = __toCommonJS(ContentHealthWatchdog_exports);\nvar POLL_INTERVAL_MS = 2e3;\nvar STALL_THRESHOLD_MS = 12e3;\nvar POST_BREAK_GRACE_MS = 5e4;\nvar MAX_BREAK_SUPPRESSION_MS = 10 * 60 * 1e3;\nvar SELF_INFLICTED_PAUSE_WINDOW_MS = 5e3;\nvar HEALTHY_PLAYBACK_SECONDS = 5;\nvar MAX_NORMAL_ADVANCE_PER_TICK_S = 4;\nvar LIGHT_STEP_COOLDOWN_MS = 1e4;\nvar RELOAD_BASE_COOLDOWN_MS = 15e3;\nvar RELOAD_MAX_COOLDOWN_MS = 12e4;\nvar ContentHealthWatchdog = class {\n constructor(host) {\n this.host = host;\n this.armed = false;\n this.userPaused = false;\n this.wasInBreak = false;\n this.breakStartedAt = 0;\n this.stuckBreakLogged = false;\n this.graceUntil = 0;\n this.lastPosition = 0;\n this.lastProgressAt = 0;\n this.lastRealPlaybackAt = 0;\n this.healthySeconds = 0;\n this.step = 0;\n this.nextActionAt = 0;\n this.lastActionAt = 0;\n this.onPlaying = () => {\n if (this.host.isAdPlaying()) return;\n this.armed = true;\n this.userPaused = false;\n };\n this.onPause = () => {\n if (this.host.isBreakActive() || this.host.isAdPlaying() || Date.now() - this.lastActionAt < SELF_INFLICTED_PAUSE_WINDOW_MS) {\n return;\n }\n this.userPaused = true;\n };\n this.onPlay = () => {\n this.userPaused = false;\n };\n }\n start() {\n if (this.intervalId !== void 0) return;\n const video = this.host.video;\n video.addEventListener(\"playing\", this.onPlaying);\n video.addEventListener(\"pause\", this.onPause);\n video.addEventListener(\"play\", this.onPlay);\n this.resetProgress(Date.now());\n this.intervalId = window.setInterval(() => this.tick(), POLL_INTERVAL_MS);\n }\n stop() {\n if (this.intervalId !== void 0) {\n clearInterval(this.intervalId);\n this.intervalId = void 0;\n }\n const video = this.host.video;\n video.removeEventListener(\"playing\", this.onPlaying);\n video.removeEventListener(\"pause\", this.onPause);\n video.removeEventListener(\"play\", this.onPlay);\n this.armed = false;\n this.step = 0;\n }\n resetProgress(now) {\n this.lastPosition = this.host.video.currentTime;\n this.lastProgressAt = now;\n }\n tick() {\n const now = Date.now();\n const video = this.host.video;\n const hidden = typeof document !== \"undefined\" && document.visibilityState === \"hidden\";\n if (!this.armed || this.userPaused || hidden || video.ended || !this.host.isLiveStream() || !this.host.hasHlsInstance()) {\n this.resetProgress(now);\n return;\n }\n const breakActive = this.host.isBreakActive() || this.host.isAdPlaying();\n if (breakActive && !this.wasInBreak) {\n this.wasInBreak = true;\n this.breakStartedAt = now;\n this.stuckBreakLogged = false;\n }\n if (breakActive && now - this.breakStartedAt < MAX_BREAK_SUPPRESSION_MS) {\n this.resetProgress(now);\n return;\n }\n if (breakActive && !this.stuckBreakLogged) {\n this.stuckBreakLogged = true;\n console.warn(\n `[ContentWatchdog] Ad break state active for over ${MAX_BREAK_SUPPRESSION_MS / 6e4} min \\u2014 monitoring content anyway`\n );\n }\n if (!breakActive && this.wasInBreak) {\n this.wasInBreak = false;\n this.graceUntil = now + POST_BREAK_GRACE_MS;\n this.resetProgress(now);\n return;\n }\n const currentTime = video.currentTime;\n const delta = currentTime - this.lastPosition;\n this.lastPosition = currentTime;\n if (delta > 0.25 && delta <= MAX_NORMAL_ADVANCE_PER_TICK_S && !video.paused) {\n this.lastProgressAt = now;\n this.lastRealPlaybackAt = now;\n this.healthySeconds += delta;\n if (this.step > 0 && this.healthySeconds >= HEALTHY_PLAYBACK_SECONDS) {\n console.log(\n `[ContentWatchdog] Content playback recovered after ${this.step} recovery step(s)`\n );\n this.step = 0;\n this.nextActionAt = 0;\n }\n return;\n }\n if (now < this.graceUntil) {\n this.lastProgressAt = now;\n return;\n }\n if (now - this.lastProgressAt < STALL_THRESHOLD_MS || now < this.nextActionAt) {\n return;\n }\n this.recover(now);\n }\n recover(now) {\n const video = this.host.video;\n const state = `t=${video.currentTime.toFixed(2)} paused=${video.paused} readyState=${video.readyState} networkState=${video.networkState} error=${video.error ? video.error.code : \"none\"}`;\n const stalledForS = ((now - (this.lastRealPlaybackAt || this.lastProgressAt)) / 1e3).toFixed(0);\n const fullReloadOnly = this.host.requiresMediaPipelineResetAfterAds();\n const action = fullReloadOnly || this.step >= 2 ? \"reload\" : this.step === 0 ? \"recover_media_error\" : \"restart_load\";\n console.warn(\n `[ContentWatchdog] Content stalled for ${stalledForS}s \\u2014 ${action} (step ${this.step + 1}) \\u2014 ${state}`\n );\n this.lastActionAt = now;\n this.healthySeconds = 0;\n if (action === \"recover_media_error\") {\n this.host.recoverMediaError();\n this.nextActionAt = now + LIGHT_STEP_COOLDOWN_MS;\n } else if (action === \"restart_load\") {\n const liveSyncPos = this.host.getLiveSyncPosition();\n const position = liveSyncPos != null && liveSyncPos > video.currentTime ? liveSyncPos : video.currentTime;\n if (position !== video.currentTime) {\n video.currentTime = position;\n }\n this.host.restartHlsLoad(position);\n this.nextActionAt = now + LIGHT_STEP_COOLDOWN_MS;\n } else {\n const reloadIndex = fullReloadOnly ? this.step : this.step - 2;\n const cooldown = Math.min(\n RELOAD_BASE_COOLDOWN_MS * Math.pow(2, reloadIndex),\n RELOAD_MAX_COOLDOWN_MS\n );\n this.host.reloadLiveStream();\n this.nextActionAt = now + cooldown;\n }\n this.step++;\n this.resetProgress(now);\n if (video.paused) {\n video.play()?.catch(() => {\n });\n }\n }\n};\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n ContentHealthWatchdog\n});\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
interface ContentHealthWatchdogHost {
|
|
2
|
+
video: HTMLVideoElement;
|
|
3
|
+
isLiveStream(): boolean;
|
|
4
|
+
hasHlsInstance(): boolean;
|
|
5
|
+
isBreakActive(): boolean;
|
|
6
|
+
isAdPlaying(): boolean;
|
|
7
|
+
requiresMediaPipelineResetAfterAds(): boolean;
|
|
8
|
+
getLiveSyncPosition(): number | undefined;
|
|
9
|
+
recoverMediaError(): void;
|
|
10
|
+
restartHlsLoad(position: number): void;
|
|
11
|
+
reloadLiveStream(): void;
|
|
12
|
+
}
|
|
13
|
+
declare class ContentHealthWatchdog {
|
|
14
|
+
private readonly host;
|
|
15
|
+
private intervalId;
|
|
16
|
+
private armed;
|
|
17
|
+
private userPaused;
|
|
18
|
+
private wasInBreak;
|
|
19
|
+
private breakStartedAt;
|
|
20
|
+
private stuckBreakLogged;
|
|
21
|
+
private graceUntil;
|
|
22
|
+
private lastPosition;
|
|
23
|
+
private lastProgressAt;
|
|
24
|
+
private lastRealPlaybackAt;
|
|
25
|
+
private healthySeconds;
|
|
26
|
+
private step;
|
|
27
|
+
private nextActionAt;
|
|
28
|
+
private lastActionAt;
|
|
29
|
+
private readonly onPlaying;
|
|
30
|
+
private readonly onPause;
|
|
31
|
+
private readonly onPlay;
|
|
32
|
+
constructor(host: ContentHealthWatchdogHost);
|
|
33
|
+
start(): void;
|
|
34
|
+
stop(): void;
|
|
35
|
+
private resetProgress;
|
|
36
|
+
private tick;
|
|
37
|
+
private recover;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export { ContentHealthWatchdog, type ContentHealthWatchdogHost };
|
|
@@ -2252,6 +2252,9 @@ function requiresMediaPipelineResetAfterAds() {
|
|
|
2252
2252
|
var browser = detectBrowser();
|
|
2253
2253
|
return browser.name === "Samsung Tizen" && ((_browser_tizenVersion = browser.tizenVersion) !== null && _browser_tizenVersion !== void 0 ? _browser_tizenVersion : 0) >= 5;
|
|
2254
2254
|
}
|
|
2255
|
+
function prefersProgressiveAdMedia() {
|
|
2256
|
+
return detectBrowser().isSmartTV;
|
|
2257
|
+
}
|
|
2255
2258
|
function getBrowserConfigOverrides() {
|
|
2256
2259
|
var browser = detectBrowser();
|
|
2257
2260
|
var overrides = {};
|
|
@@ -3095,6 +3098,7 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
3095
3098
|
var probe = adVideoElement !== null && adVideoElement !== void 0 ? adVideoElement : document.createElement("video");
|
|
3096
3099
|
var pageIsHttps = typeof location !== "undefined" && location.protocol === "https:";
|
|
3097
3100
|
var target = getMediaTargetQuality();
|
|
3101
|
+
var deprioritizeHls = prefersProgressiveAdMedia();
|
|
3098
3102
|
var accepted = [];
|
|
3099
3103
|
mediaFiles.forEach(function(file, index) {
|
|
3100
3104
|
var isHls = isHlsMediaFile(file);
|
|
@@ -3125,19 +3129,24 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
3125
3129
|
var bitrateDiff = Math.abs((file.bitrate || 5e3) * 1e3 - target.bitrate);
|
|
3126
3130
|
score = resolutionDiff * 2 + bitrateDiff / 1e3;
|
|
3127
3131
|
}
|
|
3132
|
+
var deprioritizedHls = deprioritizeHls && isHls;
|
|
3128
3133
|
accepted.push({
|
|
3129
3134
|
file: file,
|
|
3130
3135
|
score: score,
|
|
3131
|
-
insecure: insecure
|
|
3136
|
+
insecure: insecure,
|
|
3137
|
+
deprioritizedHls: deprioritizedHls
|
|
3132
3138
|
});
|
|
3133
3139
|
logFunnel("media_candidate", _object_spread_props(_object_spread({}, details), {
|
|
3134
3140
|
reason: "accepted",
|
|
3135
|
-
why: insecure ? "insecure_http_deprioritized" : "playable",
|
|
3141
|
+
why: insecure ? "insecure_http_deprioritized" : deprioritizedHls ? "hls_deprioritized_on_tv" : "playable",
|
|
3136
3142
|
score: score
|
|
3137
3143
|
}));
|
|
3138
3144
|
});
|
|
3139
3145
|
accepted.sort(function(a, b) {
|
|
3140
3146
|
if (a.insecure !== b.insecure) return a.insecure ? 1 : -1;
|
|
3147
|
+
if (a.deprioritizedHls !== b.deprioritizedHls) {
|
|
3148
|
+
return a.deprioritizedHls ? 1 : -1;
|
|
3149
|
+
}
|
|
3141
3150
|
return a.score - b.score;
|
|
3142
3151
|
});
|
|
3143
3152
|
return accepted.map(function(entry) {
|
|
@@ -4810,8 +4819,8 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
4810
4819
|
console.warn("[HlsAdPlayer] Error preparing ad video for new source:", error);
|
|
4811
4820
|
}
|
|
4812
4821
|
}
|
|
4813
|
-
function recreateAdVideoElement() {
|
|
4814
|
-
console.log("[HlsAdPlayer] Recreating ad video element (
|
|
4822
|
+
function recreateAdVideoElement(reason) {
|
|
4823
|
+
console.log("[HlsAdPlayer] Recreating ad video element (".concat(reason, ")"));
|
|
4815
4824
|
teardownAdEventListeners();
|
|
4816
4825
|
prepareVideoElementForNewSource();
|
|
4817
4826
|
if (adVideoElement) {
|
|
@@ -4855,8 +4864,9 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
4855
4864
|
}
|
|
4856
4865
|
function resetVideoElementForNextSource() {
|
|
4857
4866
|
clearStallWatchdog();
|
|
4858
|
-
|
|
4859
|
-
|
|
4867
|
+
var previousSourceUsedHlsJs = adHls !== void 0;
|
|
4868
|
+
if (requiresMediaPipelineResetAfterAds() || previousSourceUsedHlsJs) {
|
|
4869
|
+
recreateAdVideoElement(previousSourceUsedHlsJs ? "after hls.js source" : "single-decoder platform");
|
|
4860
4870
|
setupAdEventListeners();
|
|
4861
4871
|
} else {
|
|
4862
4872
|
prepareVideoElementForNewSource();
|
|
@@ -12338,6 +12348,181 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
|
|
|
12338
12348
|
]);
|
|
12339
12349
|
return AdBreakOrchestrator;
|
|
12340
12350
|
}();
|
|
12351
|
+
// src/player/ContentHealthWatchdog.ts
|
|
12352
|
+
var POLL_INTERVAL_MS = 2e3;
|
|
12353
|
+
var STALL_THRESHOLD_MS = 12e3;
|
|
12354
|
+
var POST_BREAK_GRACE_MS = 5e4;
|
|
12355
|
+
var MAX_BREAK_SUPPRESSION_MS = 10 * 60 * 1e3;
|
|
12356
|
+
var SELF_INFLICTED_PAUSE_WINDOW_MS = 5e3;
|
|
12357
|
+
var HEALTHY_PLAYBACK_SECONDS = 5;
|
|
12358
|
+
var MAX_NORMAL_ADVANCE_PER_TICK_S = 4;
|
|
12359
|
+
var LIGHT_STEP_COOLDOWN_MS = 1e4;
|
|
12360
|
+
var RELOAD_BASE_COOLDOWN_MS = 15e3;
|
|
12361
|
+
var RELOAD_MAX_COOLDOWN_MS = 12e4;
|
|
12362
|
+
var ContentHealthWatchdog = /*#__PURE__*/ function() {
|
|
12363
|
+
function ContentHealthWatchdog(host) {
|
|
12364
|
+
var _this = this;
|
|
12365
|
+
_class_call_check(this, ContentHealthWatchdog);
|
|
12366
|
+
this.host = host;
|
|
12367
|
+
this.armed = false;
|
|
12368
|
+
this.userPaused = false;
|
|
12369
|
+
this.wasInBreak = false;
|
|
12370
|
+
this.breakStartedAt = 0;
|
|
12371
|
+
this.stuckBreakLogged = false;
|
|
12372
|
+
this.graceUntil = 0;
|
|
12373
|
+
this.lastPosition = 0;
|
|
12374
|
+
this.lastProgressAt = 0;
|
|
12375
|
+
this.lastRealPlaybackAt = 0;
|
|
12376
|
+
this.healthySeconds = 0;
|
|
12377
|
+
this.step = 0;
|
|
12378
|
+
this.nextActionAt = 0;
|
|
12379
|
+
this.lastActionAt = 0;
|
|
12380
|
+
this.onPlaying = function() {
|
|
12381
|
+
if (_this.host.isAdPlaying()) return;
|
|
12382
|
+
_this.armed = true;
|
|
12383
|
+
_this.userPaused = false;
|
|
12384
|
+
};
|
|
12385
|
+
this.onPause = function() {
|
|
12386
|
+
if (_this.host.isBreakActive() || _this.host.isAdPlaying() || Date.now() - _this.lastActionAt < SELF_INFLICTED_PAUSE_WINDOW_MS) {
|
|
12387
|
+
return;
|
|
12388
|
+
}
|
|
12389
|
+
_this.userPaused = true;
|
|
12390
|
+
};
|
|
12391
|
+
this.onPlay = function() {
|
|
12392
|
+
_this.userPaused = false;
|
|
12393
|
+
};
|
|
12394
|
+
}
|
|
12395
|
+
_create_class(ContentHealthWatchdog, [
|
|
12396
|
+
{
|
|
12397
|
+
key: "start",
|
|
12398
|
+
value: function start() {
|
|
12399
|
+
var _this = this;
|
|
12400
|
+
if (this.intervalId !== void 0) return;
|
|
12401
|
+
var video = this.host.video;
|
|
12402
|
+
video.addEventListener("playing", this.onPlaying);
|
|
12403
|
+
video.addEventListener("pause", this.onPause);
|
|
12404
|
+
video.addEventListener("play", this.onPlay);
|
|
12405
|
+
this.resetProgress(Date.now());
|
|
12406
|
+
this.intervalId = window.setInterval(function() {
|
|
12407
|
+
return _this.tick();
|
|
12408
|
+
}, POLL_INTERVAL_MS);
|
|
12409
|
+
}
|
|
12410
|
+
},
|
|
12411
|
+
{
|
|
12412
|
+
key: "stop",
|
|
12413
|
+
value: function stop() {
|
|
12414
|
+
if (this.intervalId !== void 0) {
|
|
12415
|
+
clearInterval(this.intervalId);
|
|
12416
|
+
this.intervalId = void 0;
|
|
12417
|
+
}
|
|
12418
|
+
var video = this.host.video;
|
|
12419
|
+
video.removeEventListener("playing", this.onPlaying);
|
|
12420
|
+
video.removeEventListener("pause", this.onPause);
|
|
12421
|
+
video.removeEventListener("play", this.onPlay);
|
|
12422
|
+
this.armed = false;
|
|
12423
|
+
this.step = 0;
|
|
12424
|
+
}
|
|
12425
|
+
},
|
|
12426
|
+
{
|
|
12427
|
+
key: "resetProgress",
|
|
12428
|
+
value: function resetProgress(now) {
|
|
12429
|
+
this.lastPosition = this.host.video.currentTime;
|
|
12430
|
+
this.lastProgressAt = now;
|
|
12431
|
+
}
|
|
12432
|
+
},
|
|
12433
|
+
{
|
|
12434
|
+
key: "tick",
|
|
12435
|
+
value: function tick() {
|
|
12436
|
+
var now = Date.now();
|
|
12437
|
+
var video = this.host.video;
|
|
12438
|
+
var hidden = typeof document !== "undefined" && document.visibilityState === "hidden";
|
|
12439
|
+
if (!this.armed || this.userPaused || hidden || video.ended || !this.host.isLiveStream() || !this.host.hasHlsInstance()) {
|
|
12440
|
+
this.resetProgress(now);
|
|
12441
|
+
return;
|
|
12442
|
+
}
|
|
12443
|
+
var breakActive = this.host.isBreakActive() || this.host.isAdPlaying();
|
|
12444
|
+
if (breakActive && !this.wasInBreak) {
|
|
12445
|
+
this.wasInBreak = true;
|
|
12446
|
+
this.breakStartedAt = now;
|
|
12447
|
+
this.stuckBreakLogged = false;
|
|
12448
|
+
}
|
|
12449
|
+
if (breakActive && now - this.breakStartedAt < MAX_BREAK_SUPPRESSION_MS) {
|
|
12450
|
+
this.resetProgress(now);
|
|
12451
|
+
return;
|
|
12452
|
+
}
|
|
12453
|
+
if (breakActive && !this.stuckBreakLogged) {
|
|
12454
|
+
this.stuckBreakLogged = true;
|
|
12455
|
+
console.warn("[ContentWatchdog] Ad break state active for over ".concat(MAX_BREAK_SUPPRESSION_MS / 6e4, " min — monitoring content anyway"));
|
|
12456
|
+
}
|
|
12457
|
+
if (!breakActive && this.wasInBreak) {
|
|
12458
|
+
this.wasInBreak = false;
|
|
12459
|
+
this.graceUntil = now + POST_BREAK_GRACE_MS;
|
|
12460
|
+
this.resetProgress(now);
|
|
12461
|
+
return;
|
|
12462
|
+
}
|
|
12463
|
+
var currentTime = video.currentTime;
|
|
12464
|
+
var delta = currentTime - this.lastPosition;
|
|
12465
|
+
this.lastPosition = currentTime;
|
|
12466
|
+
if (delta > 0.25 && delta <= MAX_NORMAL_ADVANCE_PER_TICK_S && !video.paused) {
|
|
12467
|
+
this.lastProgressAt = now;
|
|
12468
|
+
this.lastRealPlaybackAt = now;
|
|
12469
|
+
this.healthySeconds += delta;
|
|
12470
|
+
if (this.step > 0 && this.healthySeconds >= HEALTHY_PLAYBACK_SECONDS) {
|
|
12471
|
+
console.log("[ContentWatchdog] Content playback recovered after ".concat(this.step, " recovery step(s)"));
|
|
12472
|
+
this.step = 0;
|
|
12473
|
+
this.nextActionAt = 0;
|
|
12474
|
+
}
|
|
12475
|
+
return;
|
|
12476
|
+
}
|
|
12477
|
+
if (now < this.graceUntil) {
|
|
12478
|
+
this.lastProgressAt = now;
|
|
12479
|
+
return;
|
|
12480
|
+
}
|
|
12481
|
+
if (now - this.lastProgressAt < STALL_THRESHOLD_MS || now < this.nextActionAt) {
|
|
12482
|
+
return;
|
|
12483
|
+
}
|
|
12484
|
+
this.recover(now);
|
|
12485
|
+
}
|
|
12486
|
+
},
|
|
12487
|
+
{
|
|
12488
|
+
key: "recover",
|
|
12489
|
+
value: function recover(now) {
|
|
12490
|
+
var video = this.host.video;
|
|
12491
|
+
var state = "t=".concat(video.currentTime.toFixed(2), " paused=").concat(video.paused, " readyState=").concat(video.readyState, " networkState=").concat(video.networkState, " error=").concat(video.error ? video.error.code : "none");
|
|
12492
|
+
var stalledForS = ((now - (this.lastRealPlaybackAt || this.lastProgressAt)) / 1e3).toFixed(0);
|
|
12493
|
+
var fullReloadOnly = this.host.requiresMediaPipelineResetAfterAds();
|
|
12494
|
+
var action = fullReloadOnly || this.step >= 2 ? "reload" : this.step === 0 ? "recover_media_error" : "restart_load";
|
|
12495
|
+
console.warn("[ContentWatchdog] Content stalled for ".concat(stalledForS, "s — ").concat(action, " (step ").concat(this.step + 1, ") — ").concat(state));
|
|
12496
|
+
this.lastActionAt = now;
|
|
12497
|
+
this.healthySeconds = 0;
|
|
12498
|
+
if (action === "recover_media_error") {
|
|
12499
|
+
this.host.recoverMediaError();
|
|
12500
|
+
this.nextActionAt = now + LIGHT_STEP_COOLDOWN_MS;
|
|
12501
|
+
} else if (action === "restart_load") {
|
|
12502
|
+
var liveSyncPos = this.host.getLiveSyncPosition();
|
|
12503
|
+
var position = liveSyncPos != null && liveSyncPos > video.currentTime ? liveSyncPos : video.currentTime;
|
|
12504
|
+
if (position !== video.currentTime) {
|
|
12505
|
+
video.currentTime = position;
|
|
12506
|
+
}
|
|
12507
|
+
this.host.restartHlsLoad(position);
|
|
12508
|
+
this.nextActionAt = now + LIGHT_STEP_COOLDOWN_MS;
|
|
12509
|
+
} else {
|
|
12510
|
+
var reloadIndex = fullReloadOnly ? this.step : this.step - 2;
|
|
12511
|
+
var cooldown = Math.min(RELOAD_BASE_COOLDOWN_MS * Math.pow(2, reloadIndex), RELOAD_MAX_COOLDOWN_MS);
|
|
12512
|
+
this.host.reloadLiveStream();
|
|
12513
|
+
this.nextActionAt = now + cooldown;
|
|
12514
|
+
}
|
|
12515
|
+
this.step++;
|
|
12516
|
+
this.resetProgress(now);
|
|
12517
|
+
if (video.paused) {
|
|
12518
|
+
var _video_play;
|
|
12519
|
+
(_video_play = video.play()) === null || _video_play === void 0 ? void 0 : _video_play.catch(function() {});
|
|
12520
|
+
}
|
|
12521
|
+
}
|
|
12522
|
+
}
|
|
12523
|
+
]);
|
|
12524
|
+
return ContentHealthWatchdog;
|
|
12525
|
+
}();
|
|
12341
12526
|
// src/player/StormcloudVideoPlayer.ts
|
|
12342
12527
|
var StormcloudVideoPlayer = /*#__PURE__*/ function() {
|
|
12343
12528
|
function StormcloudVideoPlayer(config) {
|
|
@@ -12468,6 +12653,36 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
|
|
|
12468
12653
|
return _this.palNonce.sendAdTouch(event);
|
|
12469
12654
|
}
|
|
12470
12655
|
}, this.timing, this.adConfig, this.cueManager, this.placeholder);
|
|
12656
|
+
this.contentWatchdog = new ContentHealthWatchdog({
|
|
12657
|
+
video: this.video,
|
|
12658
|
+
isLiveStream: function isLiveStream() {
|
|
12659
|
+
return _this.hlsEngine.isLiveStream;
|
|
12660
|
+
},
|
|
12661
|
+
hasHlsInstance: function hasHlsInstance() {
|
|
12662
|
+
return !!_this.hlsEngine.hls;
|
|
12663
|
+
},
|
|
12664
|
+
isBreakActive: function isBreakActive() {
|
|
12665
|
+
return _this.adBreak.inAdBreak || _this.adBreak.isBreakActive();
|
|
12666
|
+
},
|
|
12667
|
+
isAdPlaying: function isAdPlaying() {
|
|
12668
|
+
return _this.adPlayer.isAdPlaying();
|
|
12669
|
+
},
|
|
12670
|
+
requiresMediaPipelineResetAfterAds: function requiresMediaPipelineResetAfterAds1() {
|
|
12671
|
+
return requiresMediaPipelineResetAfterAds();
|
|
12672
|
+
},
|
|
12673
|
+
getLiveSyncPosition: function getLiveSyncPosition() {
|
|
12674
|
+
return _this.hlsEngine.getLiveSyncPosition();
|
|
12675
|
+
},
|
|
12676
|
+
recoverMediaError: function recoverMediaError() {
|
|
12677
|
+
return _this.hlsEngine.recoverMediaError();
|
|
12678
|
+
},
|
|
12679
|
+
restartHlsLoad: function restartHlsLoad(position) {
|
|
12680
|
+
return _this.hlsEngine.restartLoadAt(position);
|
|
12681
|
+
},
|
|
12682
|
+
reloadLiveStream: function reloadLiveStream() {
|
|
12683
|
+
return _this.hlsEngine.reloadLiveStream();
|
|
12684
|
+
}
|
|
12685
|
+
});
|
|
12471
12686
|
}
|
|
12472
12687
|
_create_class(StormcloudVideoPlayer, [
|
|
12473
12688
|
{
|
|
@@ -12788,6 +13003,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
|
|
|
12788
13003
|
}
|
|
12789
13004
|
};
|
|
12790
13005
|
this.video.addEventListener("emptied", this.emptiedHandler);
|
|
13006
|
+
if (this.config.contentStallWatchdog !== false) {
|
|
13007
|
+
this.contentWatchdog.start();
|
|
13008
|
+
}
|
|
12791
13009
|
}
|
|
12792
13010
|
},
|
|
12793
13011
|
{
|
|
@@ -13272,6 +13490,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
|
|
|
13272
13490
|
key: "destroy",
|
|
13273
13491
|
value: function destroy() {
|
|
13274
13492
|
var _this_adPlayer;
|
|
13493
|
+
this.contentWatchdog.stop();
|
|
13275
13494
|
this.adBreak.cancelScheduledRequest();
|
|
13276
13495
|
this.timing.destroy();
|
|
13277
13496
|
this.adBreak.clearPendingAdBreak();
|