stormcloud-video-player 0.8.47 → 0.8.49
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 +109 -53
- package/lib/index.cjs.map +1 -1
- package/lib/index.js +109 -53
- package/lib/index.js.map +1 -1
- package/lib/player/AdBreakOrchestrator.d.cts +1 -1
- package/lib/player/AdConfigManager.d.cts +1 -1
- package/lib/player/AdTimingService.d.cts +1 -1
- package/lib/player/HlsEngine.d.cts +1 -1
- package/lib/player/PlayerControls.d.cts +1 -1
- package/lib/player/Scte35CueManager.d.cts +1 -1
- package/lib/player/Scte35Parser.d.cts +1 -1
- package/lib/player/StormcloudVideoPlayer.cjs +109 -53
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/player/StormcloudVideoPlayer.d.cts +1 -1
- package/lib/player/playerTypes.d.cts +1 -1
- package/lib/players/HlsPlayer.cjs +109 -53
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/HlsPlayer.d.cts +1 -1
- package/lib/players/index.cjs +109 -53
- package/lib/players/index.cjs.map +1 -1
- package/lib/sdk/hlsAdPlayer.cjs +109 -53
- package/lib/sdk/hlsAdPlayer.cjs.map +1 -1
- package/lib/sdk/hlsAdPlayer.d.cts +1 -1
- package/lib/types-iDjS8f_7.d.cts +133 -0
- package/lib/ui/StormcloudVideoPlayer.cjs +109 -53
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
- package/lib/utils/tracking.d.cts +1 -1
- package/package.json +1 -1
package/lib/sdk/hlsAdPlayer.cjs
CHANGED
|
@@ -223,9 +223,11 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
223
223
|
var pendingTimeouts = [];
|
|
224
224
|
var STALL_TIMEOUT_MS = 4e3;
|
|
225
225
|
var STALL_CHECK_INTERVAL_MS = 1e3;
|
|
226
|
+
var BLACKFRAME_MIN_PLAYBACK_S = 1.5;
|
|
226
227
|
var stallWatchdogId;
|
|
227
228
|
var lastAdProgressTime = 0;
|
|
228
229
|
var lastAdProgressPosition = 0;
|
|
230
|
+
var sawDecodedVideoFrame = false;
|
|
229
231
|
var trackingFired = {
|
|
230
232
|
impression: false,
|
|
231
233
|
start: false,
|
|
@@ -367,6 +369,61 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
367
369
|
var url = mediaFile.url.toLowerCase();
|
|
368
370
|
return type.startsWith("video/") || url.endsWith(".mp4") || url.includes(".mp4?");
|
|
369
371
|
}
|
|
372
|
+
function isAudioMediaFile(mediaFile) {
|
|
373
|
+
var type = mediaFile.type.toLowerCase();
|
|
374
|
+
var url = (mediaFile.url.toLowerCase().split("?")[0] || "").trim();
|
|
375
|
+
return type.startsWith("audio/") || url.endsWith(".mp3") || url.endsWith(".aac") || url.endsWith(".m4a") || url.endsWith(".ogg") || url.endsWith(".oga") || url.endsWith(".opus") || url.endsWith(".wav") || url.endsWith(".weba");
|
|
376
|
+
}
|
|
377
|
+
function collectVideoMediaFiles(scope, verbose) {
|
|
378
|
+
var mediaFileElements = scope.querySelectorAll("MediaFile");
|
|
379
|
+
var mediaFiles = [];
|
|
380
|
+
if (verbose) {
|
|
381
|
+
console.log("[HlsAdPlayer] Found ".concat(mediaFileElements.length, " MediaFile element(s) in VAST XML"));
|
|
382
|
+
}
|
|
383
|
+
mediaFileElements.forEach(function(mf, index) {
|
|
384
|
+
var _mf_textContent;
|
|
385
|
+
var type = mf.getAttribute("type") || "";
|
|
386
|
+
var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
|
|
387
|
+
var width = mf.getAttribute("width") || "";
|
|
388
|
+
var height = mf.getAttribute("height") || "";
|
|
389
|
+
if (verbose) {
|
|
390
|
+
console.log("[HlsAdPlayer] MediaFile ".concat(index, ': type="').concat(type, '", url="').concat(url, '", width="').concat(width, '", height="').concat(height, '"'));
|
|
391
|
+
}
|
|
392
|
+
if (!url) {
|
|
393
|
+
if (verbose) {
|
|
394
|
+
console.warn("[HlsAdPlayer] MediaFile ".concat(index, " has empty URL"));
|
|
395
|
+
}
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
var bitrateAttr = mf.getAttribute("bitrate");
|
|
399
|
+
var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
|
|
400
|
+
var mediaFile = {
|
|
401
|
+
url: url,
|
|
402
|
+
type: type,
|
|
403
|
+
width: parseInt(width || "1920", 10),
|
|
404
|
+
height: parseInt(height || "1080", 10),
|
|
405
|
+
bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
|
|
406
|
+
};
|
|
407
|
+
if (isAudioMediaFile(mediaFile)) {
|
|
408
|
+
console.warn("[HlsAdPlayer] MediaFile ".concat(index, ' skipped (audio-only, type="').concat(type, '") — audio ads are not played to avoid a black screen'));
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
var hasExplicitDimensions = mf.getAttribute("width") != null && mf.getAttribute("height") != null;
|
|
412
|
+
if (!isHlsMediaFile(mediaFile) && hasExplicitDimensions && (mediaFile.width <= 1 || mediaFile.height <= 1)) {
|
|
413
|
+
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'));
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
|
|
417
|
+
mediaFiles.push(mediaFile);
|
|
418
|
+
if (verbose) {
|
|
419
|
+
console.log("[HlsAdPlayer] Added ".concat(isHlsMediaFile(mediaFile) ? "HLS" : "progressive", " MediaFile: ").concat(url));
|
|
420
|
+
}
|
|
421
|
+
} else if (verbose) {
|
|
422
|
+
console.log("[HlsAdPlayer] MediaFile ".concat(index, ' ignored (type="').concat(type, '" is not supported)'));
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
return mediaFiles;
|
|
426
|
+
}
|
|
370
427
|
function createEmptyTrackingUrls() {
|
|
371
428
|
return {
|
|
372
429
|
impression: [],
|
|
@@ -454,37 +511,7 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
454
511
|
var durationText = ((_xmlDoc_querySelector3 = xmlDoc.querySelector("Duration")) === null || _xmlDoc_querySelector3 === void 0 ? void 0 : _xmlDoc_querySelector3.textContent) || "00:00:30";
|
|
455
512
|
var durationParts = durationText.split(":");
|
|
456
513
|
var duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
|
|
457
|
-
var
|
|
458
|
-
var mediaFiles = [];
|
|
459
|
-
console.log("[HlsAdPlayer] Found ".concat(mediaFileElements.length, " MediaFile element(s) in VAST XML"));
|
|
460
|
-
mediaFileElements.forEach(function(mf, index) {
|
|
461
|
-
var _mf_textContent;
|
|
462
|
-
var type = mf.getAttribute("type") || "";
|
|
463
|
-
var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
|
|
464
|
-
var width = mf.getAttribute("width") || "";
|
|
465
|
-
var height = mf.getAttribute("height") || "";
|
|
466
|
-
console.log("[HlsAdPlayer] MediaFile ".concat(index, ': type="').concat(type, '", url="').concat(url, '", width="').concat(width, '", height="').concat(height, '"'));
|
|
467
|
-
var mediaFile = {
|
|
468
|
-
url: url,
|
|
469
|
-
type: type,
|
|
470
|
-
width: parseInt(width || "1920", 10),
|
|
471
|
-
height: parseInt(height || "1080", 10),
|
|
472
|
-
bitrate: void 0
|
|
473
|
-
};
|
|
474
|
-
if (!url) {
|
|
475
|
-
console.warn("[HlsAdPlayer] MediaFile ".concat(index, " has empty URL"));
|
|
476
|
-
return;
|
|
477
|
-
}
|
|
478
|
-
var bitrateAttr = mf.getAttribute("bitrate");
|
|
479
|
-
var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
|
|
480
|
-
mediaFile.bitrate = bitrateValue && bitrateValue > 0 ? bitrateValue : void 0;
|
|
481
|
-
if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
|
|
482
|
-
mediaFiles.push(mediaFile);
|
|
483
|
-
console.log("[HlsAdPlayer] Added ".concat(isHlsMediaFile(mediaFile) ? "HLS" : "progressive", " MediaFile: ").concat(url));
|
|
484
|
-
} else {
|
|
485
|
-
console.log("[HlsAdPlayer] MediaFile ".concat(index, ' ignored (type="').concat(type, '" is not supported)'));
|
|
486
|
-
}
|
|
487
|
-
});
|
|
514
|
+
var mediaFiles = collectVideoMediaFiles(xmlDoc, true);
|
|
488
515
|
if (mediaFiles.length === 0) {
|
|
489
516
|
if (isNoAdAvailable) {
|
|
490
517
|
console.warn("[HlsAdPlayer] No ads available (VAST response indicates no ads)");
|
|
@@ -532,28 +559,7 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
532
559
|
var durationText = ((_adElement_querySelector3 = adElement.querySelector("Duration")) === null || _adElement_querySelector3 === void 0 ? void 0 : _adElement_querySelector3.textContent) || "00:00:30";
|
|
533
560
|
var durationParts = durationText.split(":");
|
|
534
561
|
var duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
|
|
535
|
-
var
|
|
536
|
-
var mediaFiles = [];
|
|
537
|
-
mediaFileElements.forEach(function(mf) {
|
|
538
|
-
var _mf_textContent;
|
|
539
|
-
var type = mf.getAttribute("type") || "";
|
|
540
|
-
var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
|
|
541
|
-
var width = mf.getAttribute("width") || "";
|
|
542
|
-
var height = mf.getAttribute("height") || "";
|
|
543
|
-
if (!url) return;
|
|
544
|
-
var bitrateAttr = mf.getAttribute("bitrate");
|
|
545
|
-
var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
|
|
546
|
-
var mediaFile = {
|
|
547
|
-
url: url,
|
|
548
|
-
type: type,
|
|
549
|
-
width: parseInt(width || "1920", 10),
|
|
550
|
-
height: parseInt(height || "1080", 10),
|
|
551
|
-
bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
|
|
552
|
-
};
|
|
553
|
-
if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
|
|
554
|
-
mediaFiles.push(mediaFile);
|
|
555
|
-
}
|
|
556
|
-
});
|
|
562
|
+
var mediaFiles = collectVideoMediaFiles(adElement, false);
|
|
557
563
|
if (mediaFiles.length === 0) {
|
|
558
564
|
if (isNoAdAvailable) {
|
|
559
565
|
console.warn("[HlsAdPlayer] Pod <Ad> indicates no ad available");
|
|
@@ -874,6 +880,14 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
874
880
|
fireTrackingPixels(currentAd.trackingUrls.thirdQuartile);
|
|
875
881
|
}
|
|
876
882
|
};
|
|
883
|
+
var onLoadedMetadata = function onLoadedMetadata() {
|
|
884
|
+
if (!adPlaying || !adVideoElement) return;
|
|
885
|
+
var videoWidth = adVideoElement.videoWidth, videoHeight = adVideoElement.videoHeight;
|
|
886
|
+
if (videoWidth <= 1 || videoHeight <= 1) {
|
|
887
|
+
console.warn("[HlsAdPlayer] Ad creative has no usable video dimensions (".concat(videoWidth, "x").concat(videoHeight, ") - skipping this creative"));
|
|
888
|
+
handleVideoDecodeFailure();
|
|
889
|
+
}
|
|
890
|
+
};
|
|
877
891
|
var onPlaying = function onPlaying() {
|
|
878
892
|
startStallWatchdog();
|
|
879
893
|
if (!currentAd || trackingFired.start) return;
|
|
@@ -925,6 +939,10 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
925
939
|
type: "timeupdate",
|
|
926
940
|
handler: onTimeUpdate
|
|
927
941
|
},
|
|
942
|
+
{
|
|
943
|
+
type: "loadedmetadata",
|
|
944
|
+
handler: onLoadedMetadata
|
|
945
|
+
},
|
|
928
946
|
{
|
|
929
947
|
type: "playing",
|
|
930
948
|
handler: onPlaying
|
|
@@ -1114,13 +1132,51 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1114
1132
|
}
|
|
1115
1133
|
handleAdError();
|
|
1116
1134
|
}
|
|
1135
|
+
function getDecodedVideoFrameCount(video) {
|
|
1136
|
+
try {
|
|
1137
|
+
if (typeof video.getVideoPlaybackQuality === "function") {
|
|
1138
|
+
var quality = video.getVideoPlaybackQuality();
|
|
1139
|
+
if (quality && typeof quality.totalVideoFrames === "number") {
|
|
1140
|
+
return quality.totalVideoFrames;
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
var legacyCount = video.webkitDecodedFrameCount;
|
|
1144
|
+
if (typeof legacyCount === "number") {
|
|
1145
|
+
return legacyCount;
|
|
1146
|
+
}
|
|
1147
|
+
} catch (error) {
|
|
1148
|
+
console.warn("[HlsAdPlayer] Unable to read decoded video frame count:", error);
|
|
1149
|
+
}
|
|
1150
|
+
return void 0;
|
|
1151
|
+
}
|
|
1152
|
+
function handleVideoDecodeFailure() {
|
|
1153
|
+
console.warn("[HlsAdPlayer] Ad audio is advancing but no video frames decoded (black screen) - skipping this creative");
|
|
1154
|
+
clearStallWatchdog();
|
|
1155
|
+
if (currentAd) {
|
|
1156
|
+
fireTrackingPixels(currentAd.trackingUrls.error);
|
|
1157
|
+
}
|
|
1158
|
+
if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
handleAdError();
|
|
1162
|
+
}
|
|
1117
1163
|
function startStallWatchdog() {
|
|
1118
1164
|
clearStallWatchdog();
|
|
1119
1165
|
noteAdProgress();
|
|
1166
|
+
sawDecodedVideoFrame = false;
|
|
1120
1167
|
stallWatchdogId = window.setInterval(function() {
|
|
1121
1168
|
if (!adPlaying || !adVideoElement) {
|
|
1122
1169
|
return;
|
|
1123
1170
|
}
|
|
1171
|
+
if (!sawDecodedVideoFrame && !adVideoElement.paused) {
|
|
1172
|
+
var decodedFrames = getDecodedVideoFrameCount(adVideoElement);
|
|
1173
|
+
if (decodedFrames !== void 0 && decodedFrames > 0) {
|
|
1174
|
+
sawDecodedVideoFrame = true;
|
|
1175
|
+
} else if (decodedFrames === 0 && adVideoElement.currentTime >= BLACKFRAME_MIN_PLAYBACK_S) {
|
|
1176
|
+
handleVideoDecodeFailure();
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1124
1180
|
if (adVideoElement.currentTime > lastAdProgressPosition + 0.05) {
|
|
1125
1181
|
noteAdProgress();
|
|
1126
1182
|
return;
|