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/index.js
CHANGED
|
@@ -395,9 +395,11 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
395
395
|
var pendingTimeouts = [];
|
|
396
396
|
var STALL_TIMEOUT_MS = 4e3;
|
|
397
397
|
var STALL_CHECK_INTERVAL_MS = 1e3;
|
|
398
|
+
var BLACKFRAME_MIN_PLAYBACK_S = 1.5;
|
|
398
399
|
var stallWatchdogId;
|
|
399
400
|
var lastAdProgressTime = 0;
|
|
400
401
|
var lastAdProgressPosition = 0;
|
|
402
|
+
var sawDecodedVideoFrame = false;
|
|
401
403
|
var trackingFired = {
|
|
402
404
|
impression: false,
|
|
403
405
|
start: false,
|
|
@@ -539,6 +541,61 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
539
541
|
var url = mediaFile.url.toLowerCase();
|
|
540
542
|
return type.startsWith("video/") || url.endsWith(".mp4") || url.includes(".mp4?");
|
|
541
543
|
}
|
|
544
|
+
function isAudioMediaFile(mediaFile) {
|
|
545
|
+
var type = mediaFile.type.toLowerCase();
|
|
546
|
+
var url = (mediaFile.url.toLowerCase().split("?")[0] || "").trim();
|
|
547
|
+
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");
|
|
548
|
+
}
|
|
549
|
+
function collectVideoMediaFiles(scope, verbose) {
|
|
550
|
+
var mediaFileElements = scope.querySelectorAll("MediaFile");
|
|
551
|
+
var mediaFiles = [];
|
|
552
|
+
if (verbose) {
|
|
553
|
+
console.log("[HlsAdPlayer] Found ".concat(mediaFileElements.length, " MediaFile element(s) in VAST XML"));
|
|
554
|
+
}
|
|
555
|
+
mediaFileElements.forEach(function(mf, index) {
|
|
556
|
+
var _mf_textContent;
|
|
557
|
+
var type = mf.getAttribute("type") || "";
|
|
558
|
+
var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
|
|
559
|
+
var width = mf.getAttribute("width") || "";
|
|
560
|
+
var height = mf.getAttribute("height") || "";
|
|
561
|
+
if (verbose) {
|
|
562
|
+
console.log("[HlsAdPlayer] MediaFile ".concat(index, ': type="').concat(type, '", url="').concat(url, '", width="').concat(width, '", height="').concat(height, '"'));
|
|
563
|
+
}
|
|
564
|
+
if (!url) {
|
|
565
|
+
if (verbose) {
|
|
566
|
+
console.warn("[HlsAdPlayer] MediaFile ".concat(index, " has empty URL"));
|
|
567
|
+
}
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
var bitrateAttr = mf.getAttribute("bitrate");
|
|
571
|
+
var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
|
|
572
|
+
var mediaFile = {
|
|
573
|
+
url: url,
|
|
574
|
+
type: type,
|
|
575
|
+
width: parseInt(width || "1920", 10),
|
|
576
|
+
height: parseInt(height || "1080", 10),
|
|
577
|
+
bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
|
|
578
|
+
};
|
|
579
|
+
if (isAudioMediaFile(mediaFile)) {
|
|
580
|
+
console.warn("[HlsAdPlayer] MediaFile ".concat(index, ' skipped (audio-only, type="').concat(type, '") — audio ads are not played to avoid a black screen'));
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
var hasExplicitDimensions = mf.getAttribute("width") != null && mf.getAttribute("height") != null;
|
|
584
|
+
if (!isHlsMediaFile(mediaFile) && hasExplicitDimensions && (mediaFile.width <= 1 || mediaFile.height <= 1)) {
|
|
585
|
+
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'));
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
|
|
589
|
+
mediaFiles.push(mediaFile);
|
|
590
|
+
if (verbose) {
|
|
591
|
+
console.log("[HlsAdPlayer] Added ".concat(isHlsMediaFile(mediaFile) ? "HLS" : "progressive", " MediaFile: ").concat(url));
|
|
592
|
+
}
|
|
593
|
+
} else if (verbose) {
|
|
594
|
+
console.log("[HlsAdPlayer] MediaFile ".concat(index, ' ignored (type="').concat(type, '" is not supported)'));
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
return mediaFiles;
|
|
598
|
+
}
|
|
542
599
|
function createEmptyTrackingUrls() {
|
|
543
600
|
return {
|
|
544
601
|
impression: [],
|
|
@@ -626,37 +683,7 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
626
683
|
var durationText = ((_xmlDoc_querySelector3 = xmlDoc.querySelector("Duration")) === null || _xmlDoc_querySelector3 === void 0 ? void 0 : _xmlDoc_querySelector3.textContent) || "00:00:30";
|
|
627
684
|
var durationParts = durationText.split(":");
|
|
628
685
|
var duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
|
|
629
|
-
var
|
|
630
|
-
var mediaFiles = [];
|
|
631
|
-
console.log("[HlsAdPlayer] Found ".concat(mediaFileElements.length, " MediaFile element(s) in VAST XML"));
|
|
632
|
-
mediaFileElements.forEach(function(mf, index) {
|
|
633
|
-
var _mf_textContent;
|
|
634
|
-
var type = mf.getAttribute("type") || "";
|
|
635
|
-
var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
|
|
636
|
-
var width = mf.getAttribute("width") || "";
|
|
637
|
-
var height = mf.getAttribute("height") || "";
|
|
638
|
-
console.log("[HlsAdPlayer] MediaFile ".concat(index, ': type="').concat(type, '", url="').concat(url, '", width="').concat(width, '", height="').concat(height, '"'));
|
|
639
|
-
var mediaFile = {
|
|
640
|
-
url: url,
|
|
641
|
-
type: type,
|
|
642
|
-
width: parseInt(width || "1920", 10),
|
|
643
|
-
height: parseInt(height || "1080", 10),
|
|
644
|
-
bitrate: void 0
|
|
645
|
-
};
|
|
646
|
-
if (!url) {
|
|
647
|
-
console.warn("[HlsAdPlayer] MediaFile ".concat(index, " has empty URL"));
|
|
648
|
-
return;
|
|
649
|
-
}
|
|
650
|
-
var bitrateAttr = mf.getAttribute("bitrate");
|
|
651
|
-
var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
|
|
652
|
-
mediaFile.bitrate = bitrateValue && bitrateValue > 0 ? bitrateValue : void 0;
|
|
653
|
-
if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
|
|
654
|
-
mediaFiles.push(mediaFile);
|
|
655
|
-
console.log("[HlsAdPlayer] Added ".concat(isHlsMediaFile(mediaFile) ? "HLS" : "progressive", " MediaFile: ").concat(url));
|
|
656
|
-
} else {
|
|
657
|
-
console.log("[HlsAdPlayer] MediaFile ".concat(index, ' ignored (type="').concat(type, '" is not supported)'));
|
|
658
|
-
}
|
|
659
|
-
});
|
|
686
|
+
var mediaFiles = collectVideoMediaFiles(xmlDoc, true);
|
|
660
687
|
if (mediaFiles.length === 0) {
|
|
661
688
|
if (isNoAdAvailable) {
|
|
662
689
|
console.warn("[HlsAdPlayer] No ads available (VAST response indicates no ads)");
|
|
@@ -704,28 +731,7 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
704
731
|
var durationText = ((_adElement_querySelector3 = adElement.querySelector("Duration")) === null || _adElement_querySelector3 === void 0 ? void 0 : _adElement_querySelector3.textContent) || "00:00:30";
|
|
705
732
|
var durationParts = durationText.split(":");
|
|
706
733
|
var duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
|
|
707
|
-
var
|
|
708
|
-
var mediaFiles = [];
|
|
709
|
-
mediaFileElements.forEach(function(mf) {
|
|
710
|
-
var _mf_textContent;
|
|
711
|
-
var type = mf.getAttribute("type") || "";
|
|
712
|
-
var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
|
|
713
|
-
var width = mf.getAttribute("width") || "";
|
|
714
|
-
var height = mf.getAttribute("height") || "";
|
|
715
|
-
if (!url) return;
|
|
716
|
-
var bitrateAttr = mf.getAttribute("bitrate");
|
|
717
|
-
var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
|
|
718
|
-
var mediaFile = {
|
|
719
|
-
url: url,
|
|
720
|
-
type: type,
|
|
721
|
-
width: parseInt(width || "1920", 10),
|
|
722
|
-
height: parseInt(height || "1080", 10),
|
|
723
|
-
bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
|
|
724
|
-
};
|
|
725
|
-
if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
|
|
726
|
-
mediaFiles.push(mediaFile);
|
|
727
|
-
}
|
|
728
|
-
});
|
|
734
|
+
var mediaFiles = collectVideoMediaFiles(adElement, false);
|
|
729
735
|
if (mediaFiles.length === 0) {
|
|
730
736
|
if (isNoAdAvailable) {
|
|
731
737
|
console.warn("[HlsAdPlayer] Pod <Ad> indicates no ad available");
|
|
@@ -1046,6 +1052,14 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1046
1052
|
fireTrackingPixels(currentAd.trackingUrls.thirdQuartile);
|
|
1047
1053
|
}
|
|
1048
1054
|
};
|
|
1055
|
+
var onLoadedMetadata = function onLoadedMetadata() {
|
|
1056
|
+
if (!adPlaying || !adVideoElement) return;
|
|
1057
|
+
var videoWidth = adVideoElement.videoWidth, videoHeight = adVideoElement.videoHeight;
|
|
1058
|
+
if (videoWidth <= 1 || videoHeight <= 1) {
|
|
1059
|
+
console.warn("[HlsAdPlayer] Ad creative has no usable video dimensions (".concat(videoWidth, "x").concat(videoHeight, ") - skipping this creative"));
|
|
1060
|
+
handleVideoDecodeFailure();
|
|
1061
|
+
}
|
|
1062
|
+
};
|
|
1049
1063
|
var onPlaying = function onPlaying() {
|
|
1050
1064
|
startStallWatchdog();
|
|
1051
1065
|
if (!currentAd || trackingFired.start) return;
|
|
@@ -1097,6 +1111,10 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1097
1111
|
type: "timeupdate",
|
|
1098
1112
|
handler: onTimeUpdate
|
|
1099
1113
|
},
|
|
1114
|
+
{
|
|
1115
|
+
type: "loadedmetadata",
|
|
1116
|
+
handler: onLoadedMetadata
|
|
1117
|
+
},
|
|
1100
1118
|
{
|
|
1101
1119
|
type: "playing",
|
|
1102
1120
|
handler: onPlaying
|
|
@@ -1286,13 +1304,51 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1286
1304
|
}
|
|
1287
1305
|
handleAdError();
|
|
1288
1306
|
}
|
|
1307
|
+
function getDecodedVideoFrameCount(video) {
|
|
1308
|
+
try {
|
|
1309
|
+
if (typeof video.getVideoPlaybackQuality === "function") {
|
|
1310
|
+
var quality = video.getVideoPlaybackQuality();
|
|
1311
|
+
if (quality && typeof quality.totalVideoFrames === "number") {
|
|
1312
|
+
return quality.totalVideoFrames;
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
var legacyCount = video.webkitDecodedFrameCount;
|
|
1316
|
+
if (typeof legacyCount === "number") {
|
|
1317
|
+
return legacyCount;
|
|
1318
|
+
}
|
|
1319
|
+
} catch (error) {
|
|
1320
|
+
console.warn("[HlsAdPlayer] Unable to read decoded video frame count:", error);
|
|
1321
|
+
}
|
|
1322
|
+
return void 0;
|
|
1323
|
+
}
|
|
1324
|
+
function handleVideoDecodeFailure() {
|
|
1325
|
+
console.warn("[HlsAdPlayer] Ad audio is advancing but no video frames decoded (black screen) - skipping this creative");
|
|
1326
|
+
clearStallWatchdog();
|
|
1327
|
+
if (currentAd) {
|
|
1328
|
+
fireTrackingPixels(currentAd.trackingUrls.error);
|
|
1329
|
+
}
|
|
1330
|
+
if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
|
|
1331
|
+
return;
|
|
1332
|
+
}
|
|
1333
|
+
handleAdError();
|
|
1334
|
+
}
|
|
1289
1335
|
function startStallWatchdog() {
|
|
1290
1336
|
clearStallWatchdog();
|
|
1291
1337
|
noteAdProgress();
|
|
1338
|
+
sawDecodedVideoFrame = false;
|
|
1292
1339
|
stallWatchdogId = window.setInterval(function() {
|
|
1293
1340
|
if (!adPlaying || !adVideoElement) {
|
|
1294
1341
|
return;
|
|
1295
1342
|
}
|
|
1343
|
+
if (!sawDecodedVideoFrame && !adVideoElement.paused) {
|
|
1344
|
+
var decodedFrames = getDecodedVideoFrameCount(adVideoElement);
|
|
1345
|
+
if (decodedFrames !== void 0 && decodedFrames > 0) {
|
|
1346
|
+
sawDecodedVideoFrame = true;
|
|
1347
|
+
} else if (decodedFrames === 0 && adVideoElement.currentTime >= BLACKFRAME_MIN_PLAYBACK_S) {
|
|
1348
|
+
handleVideoDecodeFailure();
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1296
1352
|
if (adVideoElement.currentTime > lastAdProgressPosition + 0.05) {
|
|
1297
1353
|
noteAdProgress();
|
|
1298
1354
|
return;
|