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
|
@@ -425,9 +425,11 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
425
425
|
var pendingTimeouts = [];
|
|
426
426
|
var STALL_TIMEOUT_MS = 4e3;
|
|
427
427
|
var STALL_CHECK_INTERVAL_MS = 1e3;
|
|
428
|
+
var BLACKFRAME_MIN_PLAYBACK_S = 1.5;
|
|
428
429
|
var stallWatchdogId;
|
|
429
430
|
var lastAdProgressTime = 0;
|
|
430
431
|
var lastAdProgressPosition = 0;
|
|
432
|
+
var sawDecodedVideoFrame = false;
|
|
431
433
|
var trackingFired = {
|
|
432
434
|
impression: false,
|
|
433
435
|
start: false,
|
|
@@ -569,6 +571,61 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
569
571
|
var url = mediaFile.url.toLowerCase();
|
|
570
572
|
return type.startsWith("video/") || url.endsWith(".mp4") || url.includes(".mp4?");
|
|
571
573
|
}
|
|
574
|
+
function isAudioMediaFile(mediaFile) {
|
|
575
|
+
var type = mediaFile.type.toLowerCase();
|
|
576
|
+
var url = (mediaFile.url.toLowerCase().split("?")[0] || "").trim();
|
|
577
|
+
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");
|
|
578
|
+
}
|
|
579
|
+
function collectVideoMediaFiles(scope, verbose) {
|
|
580
|
+
var mediaFileElements = scope.querySelectorAll("MediaFile");
|
|
581
|
+
var mediaFiles = [];
|
|
582
|
+
if (verbose) {
|
|
583
|
+
console.log("[HlsAdPlayer] Found ".concat(mediaFileElements.length, " MediaFile element(s) in VAST XML"));
|
|
584
|
+
}
|
|
585
|
+
mediaFileElements.forEach(function(mf, index) {
|
|
586
|
+
var _mf_textContent;
|
|
587
|
+
var type = mf.getAttribute("type") || "";
|
|
588
|
+
var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
|
|
589
|
+
var width = mf.getAttribute("width") || "";
|
|
590
|
+
var height = mf.getAttribute("height") || "";
|
|
591
|
+
if (verbose) {
|
|
592
|
+
console.log("[HlsAdPlayer] MediaFile ".concat(index, ': type="').concat(type, '", url="').concat(url, '", width="').concat(width, '", height="').concat(height, '"'));
|
|
593
|
+
}
|
|
594
|
+
if (!url) {
|
|
595
|
+
if (verbose) {
|
|
596
|
+
console.warn("[HlsAdPlayer] MediaFile ".concat(index, " has empty URL"));
|
|
597
|
+
}
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
var bitrateAttr = mf.getAttribute("bitrate");
|
|
601
|
+
var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
|
|
602
|
+
var mediaFile = {
|
|
603
|
+
url: url,
|
|
604
|
+
type: type,
|
|
605
|
+
width: parseInt(width || "1920", 10),
|
|
606
|
+
height: parseInt(height || "1080", 10),
|
|
607
|
+
bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
|
|
608
|
+
};
|
|
609
|
+
if (isAudioMediaFile(mediaFile)) {
|
|
610
|
+
console.warn("[HlsAdPlayer] MediaFile ".concat(index, ' skipped (audio-only, type="').concat(type, '") — audio ads are not played to avoid a black screen'));
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
var hasExplicitDimensions = mf.getAttribute("width") != null && mf.getAttribute("height") != null;
|
|
614
|
+
if (!isHlsMediaFile(mediaFile) && hasExplicitDimensions && (mediaFile.width <= 1 || mediaFile.height <= 1)) {
|
|
615
|
+
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'));
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
|
|
619
|
+
mediaFiles.push(mediaFile);
|
|
620
|
+
if (verbose) {
|
|
621
|
+
console.log("[HlsAdPlayer] Added ".concat(isHlsMediaFile(mediaFile) ? "HLS" : "progressive", " MediaFile: ").concat(url));
|
|
622
|
+
}
|
|
623
|
+
} else if (verbose) {
|
|
624
|
+
console.log("[HlsAdPlayer] MediaFile ".concat(index, ' ignored (type="').concat(type, '" is not supported)'));
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
return mediaFiles;
|
|
628
|
+
}
|
|
572
629
|
function createEmptyTrackingUrls() {
|
|
573
630
|
return {
|
|
574
631
|
impression: [],
|
|
@@ -656,37 +713,7 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
656
713
|
var durationText = ((_xmlDoc_querySelector3 = xmlDoc.querySelector("Duration")) === null || _xmlDoc_querySelector3 === void 0 ? void 0 : _xmlDoc_querySelector3.textContent) || "00:00:30";
|
|
657
714
|
var durationParts = durationText.split(":");
|
|
658
715
|
var duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
|
|
659
|
-
var
|
|
660
|
-
var mediaFiles = [];
|
|
661
|
-
console.log("[HlsAdPlayer] Found ".concat(mediaFileElements.length, " MediaFile element(s) in VAST XML"));
|
|
662
|
-
mediaFileElements.forEach(function(mf, index) {
|
|
663
|
-
var _mf_textContent;
|
|
664
|
-
var type = mf.getAttribute("type") || "";
|
|
665
|
-
var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
|
|
666
|
-
var width = mf.getAttribute("width") || "";
|
|
667
|
-
var height = mf.getAttribute("height") || "";
|
|
668
|
-
console.log("[HlsAdPlayer] MediaFile ".concat(index, ': type="').concat(type, '", url="').concat(url, '", width="').concat(width, '", height="').concat(height, '"'));
|
|
669
|
-
var mediaFile = {
|
|
670
|
-
url: url,
|
|
671
|
-
type: type,
|
|
672
|
-
width: parseInt(width || "1920", 10),
|
|
673
|
-
height: parseInt(height || "1080", 10),
|
|
674
|
-
bitrate: void 0
|
|
675
|
-
};
|
|
676
|
-
if (!url) {
|
|
677
|
-
console.warn("[HlsAdPlayer] MediaFile ".concat(index, " has empty URL"));
|
|
678
|
-
return;
|
|
679
|
-
}
|
|
680
|
-
var bitrateAttr = mf.getAttribute("bitrate");
|
|
681
|
-
var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
|
|
682
|
-
mediaFile.bitrate = bitrateValue && bitrateValue > 0 ? bitrateValue : void 0;
|
|
683
|
-
if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
|
|
684
|
-
mediaFiles.push(mediaFile);
|
|
685
|
-
console.log("[HlsAdPlayer] Added ".concat(isHlsMediaFile(mediaFile) ? "HLS" : "progressive", " MediaFile: ").concat(url));
|
|
686
|
-
} else {
|
|
687
|
-
console.log("[HlsAdPlayer] MediaFile ".concat(index, ' ignored (type="').concat(type, '" is not supported)'));
|
|
688
|
-
}
|
|
689
|
-
});
|
|
716
|
+
var mediaFiles = collectVideoMediaFiles(xmlDoc, true);
|
|
690
717
|
if (mediaFiles.length === 0) {
|
|
691
718
|
if (isNoAdAvailable) {
|
|
692
719
|
console.warn("[HlsAdPlayer] No ads available (VAST response indicates no ads)");
|
|
@@ -734,28 +761,7 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
734
761
|
var durationText = ((_adElement_querySelector3 = adElement.querySelector("Duration")) === null || _adElement_querySelector3 === void 0 ? void 0 : _adElement_querySelector3.textContent) || "00:00:30";
|
|
735
762
|
var durationParts = durationText.split(":");
|
|
736
763
|
var duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
|
|
737
|
-
var
|
|
738
|
-
var mediaFiles = [];
|
|
739
|
-
mediaFileElements.forEach(function(mf) {
|
|
740
|
-
var _mf_textContent;
|
|
741
|
-
var type = mf.getAttribute("type") || "";
|
|
742
|
-
var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
|
|
743
|
-
var width = mf.getAttribute("width") || "";
|
|
744
|
-
var height = mf.getAttribute("height") || "";
|
|
745
|
-
if (!url) return;
|
|
746
|
-
var bitrateAttr = mf.getAttribute("bitrate");
|
|
747
|
-
var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
|
|
748
|
-
var mediaFile = {
|
|
749
|
-
url: url,
|
|
750
|
-
type: type,
|
|
751
|
-
width: parseInt(width || "1920", 10),
|
|
752
|
-
height: parseInt(height || "1080", 10),
|
|
753
|
-
bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
|
|
754
|
-
};
|
|
755
|
-
if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
|
|
756
|
-
mediaFiles.push(mediaFile);
|
|
757
|
-
}
|
|
758
|
-
});
|
|
764
|
+
var mediaFiles = collectVideoMediaFiles(adElement, false);
|
|
759
765
|
if (mediaFiles.length === 0) {
|
|
760
766
|
if (isNoAdAvailable) {
|
|
761
767
|
console.warn("[HlsAdPlayer] Pod <Ad> indicates no ad available");
|
|
@@ -1076,6 +1082,14 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1076
1082
|
fireTrackingPixels(currentAd.trackingUrls.thirdQuartile);
|
|
1077
1083
|
}
|
|
1078
1084
|
};
|
|
1085
|
+
var onLoadedMetadata = function onLoadedMetadata() {
|
|
1086
|
+
if (!adPlaying || !adVideoElement) return;
|
|
1087
|
+
var videoWidth = adVideoElement.videoWidth, videoHeight = adVideoElement.videoHeight;
|
|
1088
|
+
if (videoWidth <= 1 || videoHeight <= 1) {
|
|
1089
|
+
console.warn("[HlsAdPlayer] Ad creative has no usable video dimensions (".concat(videoWidth, "x").concat(videoHeight, ") - skipping this creative"));
|
|
1090
|
+
handleVideoDecodeFailure();
|
|
1091
|
+
}
|
|
1092
|
+
};
|
|
1079
1093
|
var onPlaying = function onPlaying() {
|
|
1080
1094
|
startStallWatchdog();
|
|
1081
1095
|
if (!currentAd || trackingFired.start) return;
|
|
@@ -1127,6 +1141,10 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1127
1141
|
type: "timeupdate",
|
|
1128
1142
|
handler: onTimeUpdate
|
|
1129
1143
|
},
|
|
1144
|
+
{
|
|
1145
|
+
type: "loadedmetadata",
|
|
1146
|
+
handler: onLoadedMetadata
|
|
1147
|
+
},
|
|
1130
1148
|
{
|
|
1131
1149
|
type: "playing",
|
|
1132
1150
|
handler: onPlaying
|
|
@@ -1316,13 +1334,51 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1316
1334
|
}
|
|
1317
1335
|
handleAdError();
|
|
1318
1336
|
}
|
|
1337
|
+
function getDecodedVideoFrameCount(video) {
|
|
1338
|
+
try {
|
|
1339
|
+
if (typeof video.getVideoPlaybackQuality === "function") {
|
|
1340
|
+
var quality = video.getVideoPlaybackQuality();
|
|
1341
|
+
if (quality && typeof quality.totalVideoFrames === "number") {
|
|
1342
|
+
return quality.totalVideoFrames;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
var legacyCount = video.webkitDecodedFrameCount;
|
|
1346
|
+
if (typeof legacyCount === "number") {
|
|
1347
|
+
return legacyCount;
|
|
1348
|
+
}
|
|
1349
|
+
} catch (error) {
|
|
1350
|
+
console.warn("[HlsAdPlayer] Unable to read decoded video frame count:", error);
|
|
1351
|
+
}
|
|
1352
|
+
return void 0;
|
|
1353
|
+
}
|
|
1354
|
+
function handleVideoDecodeFailure() {
|
|
1355
|
+
console.warn("[HlsAdPlayer] Ad audio is advancing but no video frames decoded (black screen) - skipping this creative");
|
|
1356
|
+
clearStallWatchdog();
|
|
1357
|
+
if (currentAd) {
|
|
1358
|
+
fireTrackingPixels(currentAd.trackingUrls.error);
|
|
1359
|
+
}
|
|
1360
|
+
if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
|
|
1361
|
+
return;
|
|
1362
|
+
}
|
|
1363
|
+
handleAdError();
|
|
1364
|
+
}
|
|
1319
1365
|
function startStallWatchdog() {
|
|
1320
1366
|
clearStallWatchdog();
|
|
1321
1367
|
noteAdProgress();
|
|
1368
|
+
sawDecodedVideoFrame = false;
|
|
1322
1369
|
stallWatchdogId = window.setInterval(function() {
|
|
1323
1370
|
if (!adPlaying || !adVideoElement) {
|
|
1324
1371
|
return;
|
|
1325
1372
|
}
|
|
1373
|
+
if (!sawDecodedVideoFrame && !adVideoElement.paused) {
|
|
1374
|
+
var decodedFrames = getDecodedVideoFrameCount(adVideoElement);
|
|
1375
|
+
if (decodedFrames !== void 0 && decodedFrames > 0) {
|
|
1376
|
+
sawDecodedVideoFrame = true;
|
|
1377
|
+
} else if (decodedFrames === 0 && adVideoElement.currentTime >= BLACKFRAME_MIN_PLAYBACK_S) {
|
|
1378
|
+
handleVideoDecodeFailure();
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1326
1382
|
if (adVideoElement.currentTime > lastAdProgressPosition + 0.05) {
|
|
1327
1383
|
noteAdProgress();
|
|
1328
1384
|
return;
|