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.
@@ -209,6 +209,7 @@ function createHlsAdPlayer(contentVideo, options) {
209
209
  var mainHlsInstance = options === null || options === void 0 ? void 0 : options.mainHlsInstance;
210
210
  var AD_CONTAINER_PROP = "__stormcloudAdContainer";
211
211
  var AD_VIDEO_PROP = "__stormcloudAdVideo";
212
+ var FRAME_COUNTER_RELIABLE_PROP = "__stormcloudFrameCounterReliable";
212
213
  var adVideoElement = (_contentVideo_AD_VIDEO_PROP = contentVideo[AD_VIDEO_PROP]) !== null && _contentVideo_AD_VIDEO_PROP !== void 0 ? _contentVideo_AD_VIDEO_PROP : void 0;
213
214
  var adHls;
214
215
  var adContainerEl = (_contentVideo_AD_CONTAINER_PROP = contentVideo[AD_CONTAINER_PROP]) !== null && _contentVideo_AD_CONTAINER_PROP !== void 0 ? _contentVideo_AD_CONTAINER_PROP : void 0;
@@ -223,9 +224,12 @@ function createHlsAdPlayer(contentVideo, options) {
223
224
  var pendingTimeouts = [];
224
225
  var STALL_TIMEOUT_MS = 4e3;
225
226
  var STALL_CHECK_INTERVAL_MS = 1e3;
227
+ var BLACKFRAME_MIN_PLAYBACK_S = 1.5;
226
228
  var stallWatchdogId;
227
229
  var lastAdProgressTime = 0;
228
230
  var lastAdProgressPosition = 0;
231
+ var sawDecodedVideoFrame = false;
232
+ var videoFrameCallbackHandle;
229
233
  var trackingFired = {
230
234
  impression: false,
231
235
  start: false,
@@ -406,6 +410,11 @@ function createHlsAdPlayer(contentVideo, options) {
406
410
  console.warn("[HlsAdPlayer] MediaFile ".concat(index, ' skipped (audio-only, type="').concat(type, '") — audio ads are not played to avoid a black screen'));
407
411
  return;
408
412
  }
413
+ var hasExplicitDimensions = mf.getAttribute("width") != null && mf.getAttribute("height") != null;
414
+ if (!isHlsMediaFile(mediaFile) && hasExplicitDimensions && (mediaFile.width <= 1 || mediaFile.height <= 1)) {
415
+ 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'));
416
+ return;
417
+ }
409
418
  if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
410
419
  mediaFiles.push(mediaFile);
411
420
  if (verbose) {
@@ -873,6 +882,14 @@ function createHlsAdPlayer(contentVideo, options) {
873
882
  fireTrackingPixels(currentAd.trackingUrls.thirdQuartile);
874
883
  }
875
884
  };
885
+ var onLoadedMetadata = function onLoadedMetadata() {
886
+ if (!adPlaying || !adVideoElement) return;
887
+ var videoWidth = adVideoElement.videoWidth, videoHeight = adVideoElement.videoHeight;
888
+ if (videoWidth <= 1 || videoHeight <= 1) {
889
+ console.warn("[HlsAdPlayer] Ad creative has no usable video dimensions (".concat(videoWidth, "x").concat(videoHeight, ") - skipping this creative"));
890
+ handleVideoDecodeFailure();
891
+ }
892
+ };
876
893
  var onPlaying = function onPlaying() {
877
894
  startStallWatchdog();
878
895
  if (!currentAd || trackingFired.start) return;
@@ -924,6 +941,10 @@ function createHlsAdPlayer(contentVideo, options) {
924
941
  type: "timeupdate",
925
942
  handler: onTimeUpdate
926
943
  },
944
+ {
945
+ type: "loadedmetadata",
946
+ handler: onLoadedMetadata
947
+ },
927
948
  {
928
949
  type: "playing",
929
950
  handler: onPlaying
@@ -1095,6 +1116,7 @@ function createHlsAdPlayer(contentVideo, options) {
1095
1116
  clearInterval(stallWatchdogId);
1096
1117
  stallWatchdogId = void 0;
1097
1118
  }
1119
+ cancelVideoFrameProbe();
1098
1120
  }
1099
1121
  function noteAdProgress() {
1100
1122
  lastAdProgressTime = Date.now();
@@ -1113,13 +1135,86 @@ function createHlsAdPlayer(contentVideo, options) {
1113
1135
  }
1114
1136
  handleAdError();
1115
1137
  }
1138
+ function getDecodedVideoFrameCount(video) {
1139
+ try {
1140
+ if (typeof video.getVideoPlaybackQuality === "function") {
1141
+ var quality = video.getVideoPlaybackQuality();
1142
+ if (quality && typeof quality.totalVideoFrames === "number") {
1143
+ return quality.totalVideoFrames;
1144
+ }
1145
+ }
1146
+ var legacyCount = video.webkitDecodedFrameCount;
1147
+ if (typeof legacyCount === "number") {
1148
+ return legacyCount;
1149
+ }
1150
+ } catch (error) {
1151
+ console.warn("[HlsAdPlayer] Unable to read decoded video frame count:", error);
1152
+ }
1153
+ return void 0;
1154
+ }
1155
+ function isFrameCounterReliable() {
1156
+ return contentVideo[FRAME_COUNTER_RELIABLE_PROP] === true;
1157
+ }
1158
+ function markFrameCounterReliable() {
1159
+ contentVideo[FRAME_COUNTER_RELIABLE_PROP] = true;
1160
+ }
1161
+ function cancelVideoFrameProbe() {
1162
+ if (videoFrameCallbackHandle != null && adVideoElement && typeof adVideoElement.cancelVideoFrameCallback === "function") {
1163
+ try {
1164
+ adVideoElement.cancelVideoFrameCallback(videoFrameCallbackHandle);
1165
+ } catch (error) {
1166
+ console.warn("[HlsAdPlayer] Error cancelling video frame callback:", error);
1167
+ }
1168
+ }
1169
+ videoFrameCallbackHandle = void 0;
1170
+ }
1171
+ function startVideoFrameProbe() {
1172
+ cancelVideoFrameProbe();
1173
+ if (!adVideoElement) return;
1174
+ var rvfc = adVideoElement.requestVideoFrameCallback;
1175
+ if (typeof rvfc !== "function") return;
1176
+ var onPresentedFrame = function onPresentedFrame() {
1177
+ videoFrameCallbackHandle = void 0;
1178
+ if (!adPlaying) return;
1179
+ sawDecodedVideoFrame = true;
1180
+ };
1181
+ try {
1182
+ videoFrameCallbackHandle = rvfc.call(adVideoElement, onPresentedFrame);
1183
+ } catch (error) {
1184
+ console.warn("[HlsAdPlayer] Error requesting video frame callback:", error);
1185
+ videoFrameCallbackHandle = void 0;
1186
+ }
1187
+ }
1188
+ function handleVideoDecodeFailure() {
1189
+ console.warn("[HlsAdPlayer] Ad audio is advancing but no video frames decoded (black screen) - skipping this creative");
1190
+ clearStallWatchdog();
1191
+ if (currentAd) {
1192
+ fireTrackingPixels(currentAd.trackingUrls.error);
1193
+ }
1194
+ if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1195
+ return;
1196
+ }
1197
+ handleAdError();
1198
+ }
1116
1199
  function startStallWatchdog() {
1117
1200
  clearStallWatchdog();
1118
1201
  noteAdProgress();
1202
+ sawDecodedVideoFrame = false;
1203
+ startVideoFrameProbe();
1119
1204
  stallWatchdogId = window.setInterval(function() {
1120
1205
  if (!adPlaying || !adVideoElement) {
1121
1206
  return;
1122
1207
  }
1208
+ if (!sawDecodedVideoFrame && !adVideoElement.paused) {
1209
+ var decodedFrames = getDecodedVideoFrameCount(adVideoElement);
1210
+ if (decodedFrames !== void 0 && decodedFrames > 0) {
1211
+ sawDecodedVideoFrame = true;
1212
+ markFrameCounterReliable();
1213
+ } else if (decodedFrames === 0 && adVideoElement.currentTime >= BLACKFRAME_MIN_PLAYBACK_S && isFrameCounterReliable()) {
1214
+ handleVideoDecodeFailure();
1215
+ return;
1216
+ }
1217
+ }
1123
1218
  if (adVideoElement.currentTime > lastAdProgressPosition + 0.05) {
1124
1219
  noteAdProgress();
1125
1220
  return;