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.
@@ -411,6 +411,7 @@ function createHlsAdPlayer(contentVideo, options) {
411
411
  var mainHlsInstance = options === null || options === void 0 ? void 0 : options.mainHlsInstance;
412
412
  var AD_CONTAINER_PROP = "__stormcloudAdContainer";
413
413
  var AD_VIDEO_PROP = "__stormcloudAdVideo";
414
+ var FRAME_COUNTER_RELIABLE_PROP = "__stormcloudFrameCounterReliable";
414
415
  var adVideoElement = (_contentVideo_AD_VIDEO_PROP = contentVideo[AD_VIDEO_PROP]) !== null && _contentVideo_AD_VIDEO_PROP !== void 0 ? _contentVideo_AD_VIDEO_PROP : void 0;
415
416
  var adHls;
416
417
  var adContainerEl = (_contentVideo_AD_CONTAINER_PROP = contentVideo[AD_CONTAINER_PROP]) !== null && _contentVideo_AD_CONTAINER_PROP !== void 0 ? _contentVideo_AD_CONTAINER_PROP : void 0;
@@ -425,9 +426,12 @@ function createHlsAdPlayer(contentVideo, options) {
425
426
  var pendingTimeouts = [];
426
427
  var STALL_TIMEOUT_MS = 4e3;
427
428
  var STALL_CHECK_INTERVAL_MS = 1e3;
429
+ var BLACKFRAME_MIN_PLAYBACK_S = 1.5;
428
430
  var stallWatchdogId;
429
431
  var lastAdProgressTime = 0;
430
432
  var lastAdProgressPosition = 0;
433
+ var sawDecodedVideoFrame = false;
434
+ var videoFrameCallbackHandle;
431
435
  var trackingFired = {
432
436
  impression: false,
433
437
  start: false,
@@ -608,6 +612,11 @@ function createHlsAdPlayer(contentVideo, options) {
608
612
  console.warn("[HlsAdPlayer] MediaFile ".concat(index, ' skipped (audio-only, type="').concat(type, '") — audio ads are not played to avoid a black screen'));
609
613
  return;
610
614
  }
615
+ var hasExplicitDimensions = mf.getAttribute("width") != null && mf.getAttribute("height") != null;
616
+ if (!isHlsMediaFile(mediaFile) && hasExplicitDimensions && (mediaFile.width <= 1 || mediaFile.height <= 1)) {
617
+ 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'));
618
+ return;
619
+ }
611
620
  if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
612
621
  mediaFiles.push(mediaFile);
613
622
  if (verbose) {
@@ -1075,6 +1084,14 @@ function createHlsAdPlayer(contentVideo, options) {
1075
1084
  fireTrackingPixels(currentAd.trackingUrls.thirdQuartile);
1076
1085
  }
1077
1086
  };
1087
+ var onLoadedMetadata = function onLoadedMetadata() {
1088
+ if (!adPlaying || !adVideoElement) return;
1089
+ var videoWidth = adVideoElement.videoWidth, videoHeight = adVideoElement.videoHeight;
1090
+ if (videoWidth <= 1 || videoHeight <= 1) {
1091
+ console.warn("[HlsAdPlayer] Ad creative has no usable video dimensions (".concat(videoWidth, "x").concat(videoHeight, ") - skipping this creative"));
1092
+ handleVideoDecodeFailure();
1093
+ }
1094
+ };
1078
1095
  var onPlaying = function onPlaying() {
1079
1096
  startStallWatchdog();
1080
1097
  if (!currentAd || trackingFired.start) return;
@@ -1126,6 +1143,10 @@ function createHlsAdPlayer(contentVideo, options) {
1126
1143
  type: "timeupdate",
1127
1144
  handler: onTimeUpdate
1128
1145
  },
1146
+ {
1147
+ type: "loadedmetadata",
1148
+ handler: onLoadedMetadata
1149
+ },
1129
1150
  {
1130
1151
  type: "playing",
1131
1152
  handler: onPlaying
@@ -1297,6 +1318,7 @@ function createHlsAdPlayer(contentVideo, options) {
1297
1318
  clearInterval(stallWatchdogId);
1298
1319
  stallWatchdogId = void 0;
1299
1320
  }
1321
+ cancelVideoFrameProbe();
1300
1322
  }
1301
1323
  function noteAdProgress() {
1302
1324
  lastAdProgressTime = Date.now();
@@ -1315,13 +1337,86 @@ function createHlsAdPlayer(contentVideo, options) {
1315
1337
  }
1316
1338
  handleAdError();
1317
1339
  }
1340
+ function getDecodedVideoFrameCount(video) {
1341
+ try {
1342
+ if (typeof video.getVideoPlaybackQuality === "function") {
1343
+ var quality = video.getVideoPlaybackQuality();
1344
+ if (quality && typeof quality.totalVideoFrames === "number") {
1345
+ return quality.totalVideoFrames;
1346
+ }
1347
+ }
1348
+ var legacyCount = video.webkitDecodedFrameCount;
1349
+ if (typeof legacyCount === "number") {
1350
+ return legacyCount;
1351
+ }
1352
+ } catch (error) {
1353
+ console.warn("[HlsAdPlayer] Unable to read decoded video frame count:", error);
1354
+ }
1355
+ return void 0;
1356
+ }
1357
+ function isFrameCounterReliable() {
1358
+ return contentVideo[FRAME_COUNTER_RELIABLE_PROP] === true;
1359
+ }
1360
+ function markFrameCounterReliable() {
1361
+ contentVideo[FRAME_COUNTER_RELIABLE_PROP] = true;
1362
+ }
1363
+ function cancelVideoFrameProbe() {
1364
+ if (videoFrameCallbackHandle != null && adVideoElement && typeof adVideoElement.cancelVideoFrameCallback === "function") {
1365
+ try {
1366
+ adVideoElement.cancelVideoFrameCallback(videoFrameCallbackHandle);
1367
+ } catch (error) {
1368
+ console.warn("[HlsAdPlayer] Error cancelling video frame callback:", error);
1369
+ }
1370
+ }
1371
+ videoFrameCallbackHandle = void 0;
1372
+ }
1373
+ function startVideoFrameProbe() {
1374
+ cancelVideoFrameProbe();
1375
+ if (!adVideoElement) return;
1376
+ var rvfc = adVideoElement.requestVideoFrameCallback;
1377
+ if (typeof rvfc !== "function") return;
1378
+ var onPresentedFrame = function onPresentedFrame() {
1379
+ videoFrameCallbackHandle = void 0;
1380
+ if (!adPlaying) return;
1381
+ sawDecodedVideoFrame = true;
1382
+ };
1383
+ try {
1384
+ videoFrameCallbackHandle = rvfc.call(adVideoElement, onPresentedFrame);
1385
+ } catch (error) {
1386
+ console.warn("[HlsAdPlayer] Error requesting video frame callback:", error);
1387
+ videoFrameCallbackHandle = void 0;
1388
+ }
1389
+ }
1390
+ function handleVideoDecodeFailure() {
1391
+ console.warn("[HlsAdPlayer] Ad audio is advancing but no video frames decoded (black screen) - skipping this creative");
1392
+ clearStallWatchdog();
1393
+ if (currentAd) {
1394
+ fireTrackingPixels(currentAd.trackingUrls.error);
1395
+ }
1396
+ if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1397
+ return;
1398
+ }
1399
+ handleAdError();
1400
+ }
1318
1401
  function startStallWatchdog() {
1319
1402
  clearStallWatchdog();
1320
1403
  noteAdProgress();
1404
+ sawDecodedVideoFrame = false;
1405
+ startVideoFrameProbe();
1321
1406
  stallWatchdogId = window.setInterval(function() {
1322
1407
  if (!adPlaying || !adVideoElement) {
1323
1408
  return;
1324
1409
  }
1410
+ if (!sawDecodedVideoFrame && !adVideoElement.paused) {
1411
+ var decodedFrames = getDecodedVideoFrameCount(adVideoElement);
1412
+ if (decodedFrames !== void 0 && decodedFrames > 0) {
1413
+ sawDecodedVideoFrame = true;
1414
+ markFrameCounterReliable();
1415
+ } else if (decodedFrames === 0 && adVideoElement.currentTime >= BLACKFRAME_MIN_PLAYBACK_S && isFrameCounterReliable()) {
1416
+ handleVideoDecodeFailure();
1417
+ return;
1418
+ }
1419
+ }
1325
1420
  if (adVideoElement.currentTime > lastAdProgressPosition + 0.05) {
1326
1421
  noteAdProgress();
1327
1422
  return;
@@ -6162,17 +6257,17 @@ var AdTimingService = /*#__PURE__*/ function() {
6162
6257
  {
6163
6258
  key: "logAdState",
6164
6259
  value: function logAdState(event) {
6165
- var extra = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}, adPlayer = arguments.length > 2 ? arguments[2] : void 0, inAdBreak = arguments.length > 3 ? arguments[3] : void 0, showAds = arguments.length > 4 ? arguments[4] : void 0;
6166
- var _ref;
6260
+ var extra = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
6167
6261
  if (!this.debug) {
6168
6262
  return;
6169
6263
  }
6264
+ var _this_callbacks_getAdState = this.callbacks.getAdState(), inAdBreak = _this_callbacks_getAdState.inAdBreak, showAds = _this_callbacks_getAdState.showAds, adPlaying = _this_callbacks_getAdState.adPlaying;
6170
6265
  console.log("[StormcloudVideoPlayer][AdState]", _object_spread({
6171
6266
  event: event,
6172
6267
  timestamp: /* @__PURE__ */ new Date().toISOString(),
6173
- showAds: showAds !== null && showAds !== void 0 ? showAds : false,
6174
- adPlaying: (_ref = adPlayer === null || adPlayer === void 0 ? void 0 : adPlayer.isAdPlaying()) !== null && _ref !== void 0 ? _ref : false,
6175
- inAdBreak: inAdBreak !== null && inAdBreak !== void 0 ? inAdBreak : false,
6268
+ showAds: showAds,
6269
+ adPlaying: adPlaying,
6270
+ inAdBreak: inAdBreak,
6176
6271
  activeAdRequestToken: this.activeAdRequestToken
6177
6272
  }, extra));
6178
6273
  }
@@ -8522,6 +8617,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8522
8617
  },
8523
8618
  onFailsafeTimeout: function onFailsafeTimeout() {
8524
8619
  return _this.adBreak.handleAdFailure();
8620
+ },
8621
+ getAdState: function getAdState() {
8622
+ return {
8623
+ inAdBreak: _this.adBreak.inAdBreak,
8624
+ showAds: _this.adBreak.showAds,
8625
+ adPlaying: _this.adPlayer.isAdPlaying()
8626
+ };
8525
8627
  }
8526
8628
  });
8527
8629
  this.placeholder = new PlaceholderLayer(this.video, !!this.config.debugAdTiming);