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.cjs
CHANGED
|
@@ -615,9 +615,11 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
615
615
|
var pendingTimeouts = [];
|
|
616
616
|
var STALL_TIMEOUT_MS = 4e3;
|
|
617
617
|
var STALL_CHECK_INTERVAL_MS = 1e3;
|
|
618
|
+
var BLACKFRAME_MIN_PLAYBACK_S = 1.5;
|
|
618
619
|
var stallWatchdogId;
|
|
619
620
|
var lastAdProgressTime = 0;
|
|
620
621
|
var lastAdProgressPosition = 0;
|
|
622
|
+
var sawDecodedVideoFrame = false;
|
|
621
623
|
var trackingFired = {
|
|
622
624
|
impression: false,
|
|
623
625
|
start: false,
|
|
@@ -759,6 +761,61 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
759
761
|
var url = mediaFile.url.toLowerCase();
|
|
760
762
|
return type.startsWith("video/") || url.endsWith(".mp4") || url.includes(".mp4?");
|
|
761
763
|
}
|
|
764
|
+
function isAudioMediaFile(mediaFile) {
|
|
765
|
+
var type = mediaFile.type.toLowerCase();
|
|
766
|
+
var url = (mediaFile.url.toLowerCase().split("?")[0] || "").trim();
|
|
767
|
+
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");
|
|
768
|
+
}
|
|
769
|
+
function collectVideoMediaFiles(scope, verbose) {
|
|
770
|
+
var mediaFileElements = scope.querySelectorAll("MediaFile");
|
|
771
|
+
var mediaFiles = [];
|
|
772
|
+
if (verbose) {
|
|
773
|
+
console.log("[HlsAdPlayer] Found ".concat(mediaFileElements.length, " MediaFile element(s) in VAST XML"));
|
|
774
|
+
}
|
|
775
|
+
mediaFileElements.forEach(function(mf, index) {
|
|
776
|
+
var _mf_textContent;
|
|
777
|
+
var type = mf.getAttribute("type") || "";
|
|
778
|
+
var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
|
|
779
|
+
var width = mf.getAttribute("width") || "";
|
|
780
|
+
var height = mf.getAttribute("height") || "";
|
|
781
|
+
if (verbose) {
|
|
782
|
+
console.log("[HlsAdPlayer] MediaFile ".concat(index, ': type="').concat(type, '", url="').concat(url, '", width="').concat(width, '", height="').concat(height, '"'));
|
|
783
|
+
}
|
|
784
|
+
if (!url) {
|
|
785
|
+
if (verbose) {
|
|
786
|
+
console.warn("[HlsAdPlayer] MediaFile ".concat(index, " has empty URL"));
|
|
787
|
+
}
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
var bitrateAttr = mf.getAttribute("bitrate");
|
|
791
|
+
var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
|
|
792
|
+
var mediaFile = {
|
|
793
|
+
url: url,
|
|
794
|
+
type: type,
|
|
795
|
+
width: parseInt(width || "1920", 10),
|
|
796
|
+
height: parseInt(height || "1080", 10),
|
|
797
|
+
bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
|
|
798
|
+
};
|
|
799
|
+
if (isAudioMediaFile(mediaFile)) {
|
|
800
|
+
console.warn("[HlsAdPlayer] MediaFile ".concat(index, ' skipped (audio-only, type="').concat(type, '") — audio ads are not played to avoid a black screen'));
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
var hasExplicitDimensions = mf.getAttribute("width") != null && mf.getAttribute("height") != null;
|
|
804
|
+
if (!isHlsMediaFile(mediaFile) && hasExplicitDimensions && (mediaFile.width <= 1 || mediaFile.height <= 1)) {
|
|
805
|
+
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'));
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
|
|
809
|
+
mediaFiles.push(mediaFile);
|
|
810
|
+
if (verbose) {
|
|
811
|
+
console.log("[HlsAdPlayer] Added ".concat(isHlsMediaFile(mediaFile) ? "HLS" : "progressive", " MediaFile: ").concat(url));
|
|
812
|
+
}
|
|
813
|
+
} else if (verbose) {
|
|
814
|
+
console.log("[HlsAdPlayer] MediaFile ".concat(index, ' ignored (type="').concat(type, '" is not supported)'));
|
|
815
|
+
}
|
|
816
|
+
});
|
|
817
|
+
return mediaFiles;
|
|
818
|
+
}
|
|
762
819
|
function createEmptyTrackingUrls() {
|
|
763
820
|
return {
|
|
764
821
|
impression: [],
|
|
@@ -846,37 +903,7 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
846
903
|
var durationText = ((_xmlDoc_querySelector3 = xmlDoc.querySelector("Duration")) === null || _xmlDoc_querySelector3 === void 0 ? void 0 : _xmlDoc_querySelector3.textContent) || "00:00:30";
|
|
847
904
|
var durationParts = durationText.split(":");
|
|
848
905
|
var duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
|
|
849
|
-
var
|
|
850
|
-
var mediaFiles = [];
|
|
851
|
-
console.log("[HlsAdPlayer] Found ".concat(mediaFileElements.length, " MediaFile element(s) in VAST XML"));
|
|
852
|
-
mediaFileElements.forEach(function(mf, index) {
|
|
853
|
-
var _mf_textContent;
|
|
854
|
-
var type = mf.getAttribute("type") || "";
|
|
855
|
-
var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
|
|
856
|
-
var width = mf.getAttribute("width") || "";
|
|
857
|
-
var height = mf.getAttribute("height") || "";
|
|
858
|
-
console.log("[HlsAdPlayer] MediaFile ".concat(index, ': type="').concat(type, '", url="').concat(url, '", width="').concat(width, '", height="').concat(height, '"'));
|
|
859
|
-
var mediaFile = {
|
|
860
|
-
url: url,
|
|
861
|
-
type: type,
|
|
862
|
-
width: parseInt(width || "1920", 10),
|
|
863
|
-
height: parseInt(height || "1080", 10),
|
|
864
|
-
bitrate: void 0
|
|
865
|
-
};
|
|
866
|
-
if (!url) {
|
|
867
|
-
console.warn("[HlsAdPlayer] MediaFile ".concat(index, " has empty URL"));
|
|
868
|
-
return;
|
|
869
|
-
}
|
|
870
|
-
var bitrateAttr = mf.getAttribute("bitrate");
|
|
871
|
-
var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
|
|
872
|
-
mediaFile.bitrate = bitrateValue && bitrateValue > 0 ? bitrateValue : void 0;
|
|
873
|
-
if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
|
|
874
|
-
mediaFiles.push(mediaFile);
|
|
875
|
-
console.log("[HlsAdPlayer] Added ".concat(isHlsMediaFile(mediaFile) ? "HLS" : "progressive", " MediaFile: ").concat(url));
|
|
876
|
-
} else {
|
|
877
|
-
console.log("[HlsAdPlayer] MediaFile ".concat(index, ' ignored (type="').concat(type, '" is not supported)'));
|
|
878
|
-
}
|
|
879
|
-
});
|
|
906
|
+
var mediaFiles = collectVideoMediaFiles(xmlDoc, true);
|
|
880
907
|
if (mediaFiles.length === 0) {
|
|
881
908
|
if (isNoAdAvailable) {
|
|
882
909
|
console.warn("[HlsAdPlayer] No ads available (VAST response indicates no ads)");
|
|
@@ -924,28 +951,7 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
924
951
|
var durationText = ((_adElement_querySelector3 = adElement.querySelector("Duration")) === null || _adElement_querySelector3 === void 0 ? void 0 : _adElement_querySelector3.textContent) || "00:00:30";
|
|
925
952
|
var durationParts = durationText.split(":");
|
|
926
953
|
var duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
|
|
927
|
-
var
|
|
928
|
-
var mediaFiles = [];
|
|
929
|
-
mediaFileElements.forEach(function(mf) {
|
|
930
|
-
var _mf_textContent;
|
|
931
|
-
var type = mf.getAttribute("type") || "";
|
|
932
|
-
var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
|
|
933
|
-
var width = mf.getAttribute("width") || "";
|
|
934
|
-
var height = mf.getAttribute("height") || "";
|
|
935
|
-
if (!url) return;
|
|
936
|
-
var bitrateAttr = mf.getAttribute("bitrate");
|
|
937
|
-
var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
|
|
938
|
-
var mediaFile = {
|
|
939
|
-
url: url,
|
|
940
|
-
type: type,
|
|
941
|
-
width: parseInt(width || "1920", 10),
|
|
942
|
-
height: parseInt(height || "1080", 10),
|
|
943
|
-
bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
|
|
944
|
-
};
|
|
945
|
-
if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
|
|
946
|
-
mediaFiles.push(mediaFile);
|
|
947
|
-
}
|
|
948
|
-
});
|
|
954
|
+
var mediaFiles = collectVideoMediaFiles(adElement, false);
|
|
949
955
|
if (mediaFiles.length === 0) {
|
|
950
956
|
if (isNoAdAvailable) {
|
|
951
957
|
console.warn("[HlsAdPlayer] Pod <Ad> indicates no ad available");
|
|
@@ -1266,6 +1272,14 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1266
1272
|
fireTrackingPixels(currentAd.trackingUrls.thirdQuartile);
|
|
1267
1273
|
}
|
|
1268
1274
|
};
|
|
1275
|
+
var onLoadedMetadata = function onLoadedMetadata() {
|
|
1276
|
+
if (!adPlaying || !adVideoElement) return;
|
|
1277
|
+
var videoWidth = adVideoElement.videoWidth, videoHeight = adVideoElement.videoHeight;
|
|
1278
|
+
if (videoWidth <= 1 || videoHeight <= 1) {
|
|
1279
|
+
console.warn("[HlsAdPlayer] Ad creative has no usable video dimensions (".concat(videoWidth, "x").concat(videoHeight, ") - skipping this creative"));
|
|
1280
|
+
handleVideoDecodeFailure();
|
|
1281
|
+
}
|
|
1282
|
+
};
|
|
1269
1283
|
var onPlaying = function onPlaying() {
|
|
1270
1284
|
startStallWatchdog();
|
|
1271
1285
|
if (!currentAd || trackingFired.start) return;
|
|
@@ -1317,6 +1331,10 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1317
1331
|
type: "timeupdate",
|
|
1318
1332
|
handler: onTimeUpdate
|
|
1319
1333
|
},
|
|
1334
|
+
{
|
|
1335
|
+
type: "loadedmetadata",
|
|
1336
|
+
handler: onLoadedMetadata
|
|
1337
|
+
},
|
|
1320
1338
|
{
|
|
1321
1339
|
type: "playing",
|
|
1322
1340
|
handler: onPlaying
|
|
@@ -1506,13 +1524,51 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1506
1524
|
}
|
|
1507
1525
|
handleAdError();
|
|
1508
1526
|
}
|
|
1527
|
+
function getDecodedVideoFrameCount(video) {
|
|
1528
|
+
try {
|
|
1529
|
+
if (typeof video.getVideoPlaybackQuality === "function") {
|
|
1530
|
+
var quality = video.getVideoPlaybackQuality();
|
|
1531
|
+
if (quality && typeof quality.totalVideoFrames === "number") {
|
|
1532
|
+
return quality.totalVideoFrames;
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
var legacyCount = video.webkitDecodedFrameCount;
|
|
1536
|
+
if (typeof legacyCount === "number") {
|
|
1537
|
+
return legacyCount;
|
|
1538
|
+
}
|
|
1539
|
+
} catch (error) {
|
|
1540
|
+
console.warn("[HlsAdPlayer] Unable to read decoded video frame count:", error);
|
|
1541
|
+
}
|
|
1542
|
+
return void 0;
|
|
1543
|
+
}
|
|
1544
|
+
function handleVideoDecodeFailure() {
|
|
1545
|
+
console.warn("[HlsAdPlayer] Ad audio is advancing but no video frames decoded (black screen) - skipping this creative");
|
|
1546
|
+
clearStallWatchdog();
|
|
1547
|
+
if (currentAd) {
|
|
1548
|
+
fireTrackingPixels(currentAd.trackingUrls.error);
|
|
1549
|
+
}
|
|
1550
|
+
if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
|
|
1551
|
+
return;
|
|
1552
|
+
}
|
|
1553
|
+
handleAdError();
|
|
1554
|
+
}
|
|
1509
1555
|
function startStallWatchdog() {
|
|
1510
1556
|
clearStallWatchdog();
|
|
1511
1557
|
noteAdProgress();
|
|
1558
|
+
sawDecodedVideoFrame = false;
|
|
1512
1559
|
stallWatchdogId = window.setInterval(function() {
|
|
1513
1560
|
if (!adPlaying || !adVideoElement) {
|
|
1514
1561
|
return;
|
|
1515
1562
|
}
|
|
1563
|
+
if (!sawDecodedVideoFrame && !adVideoElement.paused) {
|
|
1564
|
+
var decodedFrames = getDecodedVideoFrameCount(adVideoElement);
|
|
1565
|
+
if (decodedFrames !== void 0 && decodedFrames > 0) {
|
|
1566
|
+
sawDecodedVideoFrame = true;
|
|
1567
|
+
} else if (decodedFrames === 0 && adVideoElement.currentTime >= BLACKFRAME_MIN_PLAYBACK_S) {
|
|
1568
|
+
handleVideoDecodeFailure();
|
|
1569
|
+
return;
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1516
1572
|
if (adVideoElement.currentTime > lastAdProgressPosition + 0.05) {
|
|
1517
1573
|
noteAdProgress();
|
|
1518
1574
|
return;
|