stormcloud-video-player 0.8.48 → 0.8.50
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 +2 -2
- package/lib/index.cjs +107 -5
- package/lib/index.cjs.map +1 -1
- package/lib/index.js +107 -5
- package/lib/index.js.map +1 -1
- package/lib/player/AdTimingService.cjs +5 -5
- package/lib/player/AdTimingService.cjs.map +1 -1
- package/lib/player/AdTimingService.d.cts +6 -1
- package/lib/player/StormcloudVideoPlayer.cjs +107 -5
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/players/HlsPlayer.cjs +107 -5
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/index.cjs +107 -5
- package/lib/players/index.cjs.map +1 -1
- package/lib/sdk/hlsAdPlayer.cjs +95 -0
- package/lib/sdk/hlsAdPlayer.cjs.map +1 -1
- package/lib/types-iDjS8f_7.d.cts +133 -0
- package/lib/ui/StormcloudVideoPlayer.cjs +107 -5
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
interface AdBreak {
|
|
2
|
+
id?: string;
|
|
3
|
+
startTimeMs: number;
|
|
4
|
+
durationMs?: number;
|
|
5
|
+
vastTagUrl?: string;
|
|
6
|
+
}
|
|
7
|
+
interface StormcloudVideoPlayerConfig {
|
|
8
|
+
videoElement: HTMLVideoElement;
|
|
9
|
+
src: string;
|
|
10
|
+
autoplay?: boolean;
|
|
11
|
+
muted?: boolean;
|
|
12
|
+
allowNativeHls?: boolean;
|
|
13
|
+
lowLatencyMode?: boolean;
|
|
14
|
+
isLiveStream?: boolean;
|
|
15
|
+
pauseContentDuringAds?: boolean;
|
|
16
|
+
driftToleranceMs?: number;
|
|
17
|
+
immediateManifestAds?: boolean;
|
|
18
|
+
debugAdTiming?: boolean;
|
|
19
|
+
adFailsafeTimeoutMs?: number;
|
|
20
|
+
adBreakCheckIntervalMs?: number;
|
|
21
|
+
maxAdBreakExtensionMs?: number;
|
|
22
|
+
minRemainingMsToStartAd?: number;
|
|
23
|
+
showCustomControls?: boolean;
|
|
24
|
+
hideLoadingIndicator?: boolean;
|
|
25
|
+
onVolumeToggle?: () => void;
|
|
26
|
+
onFullscreenToggle?: () => void;
|
|
27
|
+
onControlClick?: () => void;
|
|
28
|
+
licenseKey?: string;
|
|
29
|
+
vastTagUrl?: string;
|
|
30
|
+
isVmap?: boolean;
|
|
31
|
+
vmapUrl?: string;
|
|
32
|
+
vastMode?: 'adstorm' | 'default';
|
|
33
|
+
minSegmentsBeforePlay?: number;
|
|
34
|
+
ctvAdRequest?: boolean;
|
|
35
|
+
adTest?: boolean;
|
|
36
|
+
podMaxAds?: number;
|
|
37
|
+
podMaxDurationMs?: number;
|
|
38
|
+
podExactDuration?: boolean;
|
|
39
|
+
}
|
|
40
|
+
interface Scte35Marker {
|
|
41
|
+
type: "start" | "end" | "progress";
|
|
42
|
+
ptsSeconds?: number;
|
|
43
|
+
durationSeconds?: number;
|
|
44
|
+
raw?: unknown;
|
|
45
|
+
}
|
|
46
|
+
interface Id3TagInfo {
|
|
47
|
+
key: string;
|
|
48
|
+
value: string | Uint8Array;
|
|
49
|
+
ptsSeconds?: number;
|
|
50
|
+
}
|
|
51
|
+
interface AdController {
|
|
52
|
+
initialize: () => void;
|
|
53
|
+
requestAds: (vastTagUrl: string) => Promise<void>;
|
|
54
|
+
play: () => Promise<void>;
|
|
55
|
+
pause: () => void;
|
|
56
|
+
resume: () => void;
|
|
57
|
+
stop: () => Promise<void>;
|
|
58
|
+
destroy: () => void;
|
|
59
|
+
isAdPlaying: () => boolean;
|
|
60
|
+
resize: (width: number, height: number) => void;
|
|
61
|
+
on: (event: string, listener: (payload?: any) => void) => void;
|
|
62
|
+
off: (event: string, listener: (payload?: any) => void) => void;
|
|
63
|
+
updateOriginalMutedState: (muted: boolean, volume?: number) => void;
|
|
64
|
+
getOriginalMutedState: () => boolean;
|
|
65
|
+
getOriginalVolume: () => number;
|
|
66
|
+
setAdVolume: (volume: number) => void;
|
|
67
|
+
getAdVolume: () => number;
|
|
68
|
+
showPlaceholder: () => void;
|
|
69
|
+
hidePlaceholder: () => void;
|
|
70
|
+
getLoadedAdDuration?: () => number | undefined;
|
|
71
|
+
getLoadedAdId?: () => string | undefined;
|
|
72
|
+
getPodAdCount?: () => number;
|
|
73
|
+
getPodTotalDurationMs?: () => number;
|
|
74
|
+
}
|
|
75
|
+
interface ClientInfo {
|
|
76
|
+
brand: string;
|
|
77
|
+
os: string;
|
|
78
|
+
model: string;
|
|
79
|
+
deviceType: "tv" | "mobile" | "tablet" | "desktop";
|
|
80
|
+
isSmartTV: boolean;
|
|
81
|
+
isAndroid: boolean;
|
|
82
|
+
isWebView: boolean;
|
|
83
|
+
isWebApp: boolean;
|
|
84
|
+
domain: string;
|
|
85
|
+
origin: string;
|
|
86
|
+
path: string;
|
|
87
|
+
userAgent: string;
|
|
88
|
+
vendor: string;
|
|
89
|
+
platform: string;
|
|
90
|
+
screen: {
|
|
91
|
+
width?: number;
|
|
92
|
+
height?: number;
|
|
93
|
+
availWidth?: number;
|
|
94
|
+
availHeight?: number;
|
|
95
|
+
orientation?: string;
|
|
96
|
+
pixelDepth?: number;
|
|
97
|
+
};
|
|
98
|
+
hardwareConcurrency: number;
|
|
99
|
+
deviceMemory: number | null;
|
|
100
|
+
maxTouchPoints: number;
|
|
101
|
+
language: string;
|
|
102
|
+
languages: string;
|
|
103
|
+
cookieEnabled: boolean;
|
|
104
|
+
doNotTrack: string;
|
|
105
|
+
referrer: string;
|
|
106
|
+
visibilityState: string;
|
|
107
|
+
}
|
|
108
|
+
interface PlayerAnalyticsContext {
|
|
109
|
+
inputStreamType?: string;
|
|
110
|
+
debugAdTiming?: boolean;
|
|
111
|
+
}
|
|
112
|
+
interface AdDetectInfo {
|
|
113
|
+
source: "scte35";
|
|
114
|
+
durationSeconds?: number;
|
|
115
|
+
ptsSeconds?: number;
|
|
116
|
+
detectedAtFragmentSn?: number;
|
|
117
|
+
timestamp: string;
|
|
118
|
+
}
|
|
119
|
+
interface AdLoadedInfo {
|
|
120
|
+
source: "vast" | "hls";
|
|
121
|
+
vastUrl?: string;
|
|
122
|
+
durationSeconds?: number;
|
|
123
|
+
timestamp: string;
|
|
124
|
+
}
|
|
125
|
+
interface AdImpressionInfo {
|
|
126
|
+
source: "vast" | "hls";
|
|
127
|
+
adIndex: number;
|
|
128
|
+
adUrl?: string;
|
|
129
|
+
durationSeconds?: number;
|
|
130
|
+
timestamp: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export type { AdController as A, ClientInfo as C, Id3TagInfo as I, PlayerAnalyticsContext as P, StormcloudVideoPlayerConfig as S, Scte35Marker as a, AdBreak as b, AdDetectInfo as c, AdImpressionInfo as d, AdLoadedInfo as e };
|
|
@@ -397,6 +397,7 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
397
397
|
var mainHlsInstance = options === null || options === void 0 ? void 0 : options.mainHlsInstance;
|
|
398
398
|
var AD_CONTAINER_PROP = "__stormcloudAdContainer";
|
|
399
399
|
var AD_VIDEO_PROP = "__stormcloudAdVideo";
|
|
400
|
+
var FRAME_COUNTER_RELIABLE_PROP = "__stormcloudFrameCounterReliable";
|
|
400
401
|
var adVideoElement = (_contentVideo_AD_VIDEO_PROP = contentVideo[AD_VIDEO_PROP]) !== null && _contentVideo_AD_VIDEO_PROP !== void 0 ? _contentVideo_AD_VIDEO_PROP : void 0;
|
|
401
402
|
var adHls;
|
|
402
403
|
var adContainerEl = (_contentVideo_AD_CONTAINER_PROP = contentVideo[AD_CONTAINER_PROP]) !== null && _contentVideo_AD_CONTAINER_PROP !== void 0 ? _contentVideo_AD_CONTAINER_PROP : void 0;
|
|
@@ -411,9 +412,12 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
411
412
|
var pendingTimeouts = [];
|
|
412
413
|
var STALL_TIMEOUT_MS = 4e3;
|
|
413
414
|
var STALL_CHECK_INTERVAL_MS = 1e3;
|
|
415
|
+
var BLACKFRAME_MIN_PLAYBACK_S = 1.5;
|
|
414
416
|
var stallWatchdogId;
|
|
415
417
|
var lastAdProgressTime = 0;
|
|
416
418
|
var lastAdProgressPosition = 0;
|
|
419
|
+
var sawDecodedVideoFrame = false;
|
|
420
|
+
var videoFrameCallbackHandle;
|
|
417
421
|
var trackingFired = {
|
|
418
422
|
impression: false,
|
|
419
423
|
start: false,
|
|
@@ -594,6 +598,11 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
594
598
|
console.warn("[HlsAdPlayer] MediaFile ".concat(index, ' skipped (audio-only, type="').concat(type, '") — audio ads are not played to avoid a black screen'));
|
|
595
599
|
return;
|
|
596
600
|
}
|
|
601
|
+
var hasExplicitDimensions = mf.getAttribute("width") != null && mf.getAttribute("height") != null;
|
|
602
|
+
if (!isHlsMediaFile(mediaFile) && hasExplicitDimensions && (mediaFile.width <= 1 || mediaFile.height <= 1)) {
|
|
603
|
+
console.warn("[HlsAdPlayer] MediaFile ".concat(index, " skipped (degenerate dimensions ").concat(mediaFile.width, "x").concat(mediaFile.height, ', type="').concat(type, '") — likely an audio-only placeholder rendition'));
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
597
606
|
if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
|
|
598
607
|
mediaFiles.push(mediaFile);
|
|
599
608
|
if (verbose) {
|
|
@@ -1061,6 +1070,14 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1061
1070
|
fireTrackingPixels(currentAd.trackingUrls.thirdQuartile);
|
|
1062
1071
|
}
|
|
1063
1072
|
};
|
|
1073
|
+
var onLoadedMetadata = function onLoadedMetadata() {
|
|
1074
|
+
if (!adPlaying || !adVideoElement) return;
|
|
1075
|
+
var videoWidth = adVideoElement.videoWidth, videoHeight = adVideoElement.videoHeight;
|
|
1076
|
+
if (videoWidth <= 1 || videoHeight <= 1) {
|
|
1077
|
+
console.warn("[HlsAdPlayer] Ad creative has no usable video dimensions (".concat(videoWidth, "x").concat(videoHeight, ") - skipping this creative"));
|
|
1078
|
+
handleVideoDecodeFailure();
|
|
1079
|
+
}
|
|
1080
|
+
};
|
|
1064
1081
|
var onPlaying = function onPlaying() {
|
|
1065
1082
|
startStallWatchdog();
|
|
1066
1083
|
if (!currentAd || trackingFired.start) return;
|
|
@@ -1112,6 +1129,10 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1112
1129
|
type: "timeupdate",
|
|
1113
1130
|
handler: onTimeUpdate
|
|
1114
1131
|
},
|
|
1132
|
+
{
|
|
1133
|
+
type: "loadedmetadata",
|
|
1134
|
+
handler: onLoadedMetadata
|
|
1135
|
+
},
|
|
1115
1136
|
{
|
|
1116
1137
|
type: "playing",
|
|
1117
1138
|
handler: onPlaying
|
|
@@ -1283,6 +1304,7 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1283
1304
|
clearInterval(stallWatchdogId);
|
|
1284
1305
|
stallWatchdogId = void 0;
|
|
1285
1306
|
}
|
|
1307
|
+
cancelVideoFrameProbe();
|
|
1286
1308
|
}
|
|
1287
1309
|
function noteAdProgress() {
|
|
1288
1310
|
lastAdProgressTime = Date.now();
|
|
@@ -1301,13 +1323,86 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1301
1323
|
}
|
|
1302
1324
|
handleAdError();
|
|
1303
1325
|
}
|
|
1326
|
+
function getDecodedVideoFrameCount(video) {
|
|
1327
|
+
try {
|
|
1328
|
+
if (typeof video.getVideoPlaybackQuality === "function") {
|
|
1329
|
+
var quality = video.getVideoPlaybackQuality();
|
|
1330
|
+
if (quality && typeof quality.totalVideoFrames === "number") {
|
|
1331
|
+
return quality.totalVideoFrames;
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
var legacyCount = video.webkitDecodedFrameCount;
|
|
1335
|
+
if (typeof legacyCount === "number") {
|
|
1336
|
+
return legacyCount;
|
|
1337
|
+
}
|
|
1338
|
+
} catch (error) {
|
|
1339
|
+
console.warn("[HlsAdPlayer] Unable to read decoded video frame count:", error);
|
|
1340
|
+
}
|
|
1341
|
+
return void 0;
|
|
1342
|
+
}
|
|
1343
|
+
function isFrameCounterReliable() {
|
|
1344
|
+
return contentVideo[FRAME_COUNTER_RELIABLE_PROP] === true;
|
|
1345
|
+
}
|
|
1346
|
+
function markFrameCounterReliable() {
|
|
1347
|
+
contentVideo[FRAME_COUNTER_RELIABLE_PROP] = true;
|
|
1348
|
+
}
|
|
1349
|
+
function cancelVideoFrameProbe() {
|
|
1350
|
+
if (videoFrameCallbackHandle != null && adVideoElement && typeof adVideoElement.cancelVideoFrameCallback === "function") {
|
|
1351
|
+
try {
|
|
1352
|
+
adVideoElement.cancelVideoFrameCallback(videoFrameCallbackHandle);
|
|
1353
|
+
} catch (error) {
|
|
1354
|
+
console.warn("[HlsAdPlayer] Error cancelling video frame callback:", error);
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
videoFrameCallbackHandle = void 0;
|
|
1358
|
+
}
|
|
1359
|
+
function startVideoFrameProbe() {
|
|
1360
|
+
cancelVideoFrameProbe();
|
|
1361
|
+
if (!adVideoElement) return;
|
|
1362
|
+
var rvfc = adVideoElement.requestVideoFrameCallback;
|
|
1363
|
+
if (typeof rvfc !== "function") return;
|
|
1364
|
+
var onPresentedFrame = function onPresentedFrame() {
|
|
1365
|
+
videoFrameCallbackHandle = void 0;
|
|
1366
|
+
if (!adPlaying) return;
|
|
1367
|
+
sawDecodedVideoFrame = true;
|
|
1368
|
+
};
|
|
1369
|
+
try {
|
|
1370
|
+
videoFrameCallbackHandle = rvfc.call(adVideoElement, onPresentedFrame);
|
|
1371
|
+
} catch (error) {
|
|
1372
|
+
console.warn("[HlsAdPlayer] Error requesting video frame callback:", error);
|
|
1373
|
+
videoFrameCallbackHandle = void 0;
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
function handleVideoDecodeFailure() {
|
|
1377
|
+
console.warn("[HlsAdPlayer] Ad audio is advancing but no video frames decoded (black screen) - skipping this creative");
|
|
1378
|
+
clearStallWatchdog();
|
|
1379
|
+
if (currentAd) {
|
|
1380
|
+
fireTrackingPixels(currentAd.trackingUrls.error);
|
|
1381
|
+
}
|
|
1382
|
+
if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
|
|
1383
|
+
return;
|
|
1384
|
+
}
|
|
1385
|
+
handleAdError();
|
|
1386
|
+
}
|
|
1304
1387
|
function startStallWatchdog() {
|
|
1305
1388
|
clearStallWatchdog();
|
|
1306
1389
|
noteAdProgress();
|
|
1390
|
+
sawDecodedVideoFrame = false;
|
|
1391
|
+
startVideoFrameProbe();
|
|
1307
1392
|
stallWatchdogId = window.setInterval(function() {
|
|
1308
1393
|
if (!adPlaying || !adVideoElement) {
|
|
1309
1394
|
return;
|
|
1310
1395
|
}
|
|
1396
|
+
if (!sawDecodedVideoFrame && !adVideoElement.paused) {
|
|
1397
|
+
var decodedFrames = getDecodedVideoFrameCount(adVideoElement);
|
|
1398
|
+
if (decodedFrames !== void 0 && decodedFrames > 0) {
|
|
1399
|
+
sawDecodedVideoFrame = true;
|
|
1400
|
+
markFrameCounterReliable();
|
|
1401
|
+
} else if (decodedFrames === 0 && adVideoElement.currentTime >= BLACKFRAME_MIN_PLAYBACK_S && isFrameCounterReliable()) {
|
|
1402
|
+
handleVideoDecodeFailure();
|
|
1403
|
+
return;
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1311
1406
|
if (adVideoElement.currentTime > lastAdProgressPosition + 0.05) {
|
|
1312
1407
|
noteAdProgress();
|
|
1313
1408
|
return;
|
|
@@ -6148,17 +6243,17 @@ var AdTimingService = /*#__PURE__*/ function() {
|
|
|
6148
6243
|
{
|
|
6149
6244
|
key: "logAdState",
|
|
6150
6245
|
value: function logAdState(event) {
|
|
6151
|
-
var extra = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}
|
|
6152
|
-
var _ref;
|
|
6246
|
+
var extra = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
|
|
6153
6247
|
if (!this.debug) {
|
|
6154
6248
|
return;
|
|
6155
6249
|
}
|
|
6250
|
+
var _this_callbacks_getAdState = this.callbacks.getAdState(), inAdBreak = _this_callbacks_getAdState.inAdBreak, showAds = _this_callbacks_getAdState.showAds, adPlaying = _this_callbacks_getAdState.adPlaying;
|
|
6156
6251
|
console.log("[StormcloudVideoPlayer][AdState]", _object_spread({
|
|
6157
6252
|
event: event,
|
|
6158
6253
|
timestamp: /* @__PURE__ */ new Date().toISOString(),
|
|
6159
|
-
showAds: showAds
|
|
6160
|
-
adPlaying:
|
|
6161
|
-
inAdBreak: inAdBreak
|
|
6254
|
+
showAds: showAds,
|
|
6255
|
+
adPlaying: adPlaying,
|
|
6256
|
+
inAdBreak: inAdBreak,
|
|
6162
6257
|
activeAdRequestToken: this.activeAdRequestToken
|
|
6163
6258
|
}, extra));
|
|
6164
6259
|
}
|
|
@@ -8508,6 +8603,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
|
|
|
8508
8603
|
},
|
|
8509
8604
|
onFailsafeTimeout: function onFailsafeTimeout() {
|
|
8510
8605
|
return _this.adBreak.handleAdFailure();
|
|
8606
|
+
},
|
|
8607
|
+
getAdState: function getAdState() {
|
|
8608
|
+
return {
|
|
8609
|
+
inAdBreak: _this.adBreak.inAdBreak,
|
|
8610
|
+
showAds: _this.adBreak.showAds,
|
|
8611
|
+
adPlaying: _this.adPlayer.isAdPlaying()
|
|
8612
|
+
};
|
|
8511
8613
|
}
|
|
8512
8614
|
});
|
|
8513
8615
|
this.placeholder = new PlaceholderLayer(this.video, !!this.config.debugAdTiming);
|