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.
@@ -447,6 +447,7 @@ function createHlsAdPlayer(contentVideo, options) {
447
447
  var mainHlsInstance = options === null || options === void 0 ? void 0 : options.mainHlsInstance;
448
448
  var AD_CONTAINER_PROP = "__stormcloudAdContainer";
449
449
  var AD_VIDEO_PROP = "__stormcloudAdVideo";
450
+ var FRAME_COUNTER_RELIABLE_PROP = "__stormcloudFrameCounterReliable";
450
451
  var adVideoElement = (_contentVideo_AD_VIDEO_PROP = contentVideo[AD_VIDEO_PROP]) !== null && _contentVideo_AD_VIDEO_PROP !== void 0 ? _contentVideo_AD_VIDEO_PROP : void 0;
451
452
  var adHls;
452
453
  var adContainerEl = (_contentVideo_AD_CONTAINER_PROP = contentVideo[AD_CONTAINER_PROP]) !== null && _contentVideo_AD_CONTAINER_PROP !== void 0 ? _contentVideo_AD_CONTAINER_PROP : void 0;
@@ -461,9 +462,12 @@ function createHlsAdPlayer(contentVideo, options) {
461
462
  var pendingTimeouts = [];
462
463
  var STALL_TIMEOUT_MS = 4e3;
463
464
  var STALL_CHECK_INTERVAL_MS = 1e3;
465
+ var BLACKFRAME_MIN_PLAYBACK_S = 1.5;
464
466
  var stallWatchdogId;
465
467
  var lastAdProgressTime = 0;
466
468
  var lastAdProgressPosition = 0;
469
+ var sawDecodedVideoFrame = false;
470
+ var videoFrameCallbackHandle;
467
471
  var trackingFired = {
468
472
  impression: false,
469
473
  start: false,
@@ -644,6 +648,11 @@ function createHlsAdPlayer(contentVideo, options) {
644
648
  console.warn("[HlsAdPlayer] MediaFile ".concat(index, ' skipped (audio-only, type="').concat(type, '") — audio ads are not played to avoid a black screen'));
645
649
  return;
646
650
  }
651
+ var hasExplicitDimensions = mf.getAttribute("width") != null && mf.getAttribute("height") != null;
652
+ if (!isHlsMediaFile(mediaFile) && hasExplicitDimensions && (mediaFile.width <= 1 || mediaFile.height <= 1)) {
653
+ 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'));
654
+ return;
655
+ }
647
656
  if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
648
657
  mediaFiles.push(mediaFile);
649
658
  if (verbose) {
@@ -1111,6 +1120,14 @@ function createHlsAdPlayer(contentVideo, options) {
1111
1120
  fireTrackingPixels(currentAd.trackingUrls.thirdQuartile);
1112
1121
  }
1113
1122
  };
1123
+ var onLoadedMetadata = function onLoadedMetadata() {
1124
+ if (!adPlaying || !adVideoElement) return;
1125
+ var videoWidth = adVideoElement.videoWidth, videoHeight = adVideoElement.videoHeight;
1126
+ if (videoWidth <= 1 || videoHeight <= 1) {
1127
+ console.warn("[HlsAdPlayer] Ad creative has no usable video dimensions (".concat(videoWidth, "x").concat(videoHeight, ") - skipping this creative"));
1128
+ handleVideoDecodeFailure();
1129
+ }
1130
+ };
1114
1131
  var onPlaying = function onPlaying() {
1115
1132
  startStallWatchdog();
1116
1133
  if (!currentAd || trackingFired.start) return;
@@ -1162,6 +1179,10 @@ function createHlsAdPlayer(contentVideo, options) {
1162
1179
  type: "timeupdate",
1163
1180
  handler: onTimeUpdate
1164
1181
  },
1182
+ {
1183
+ type: "loadedmetadata",
1184
+ handler: onLoadedMetadata
1185
+ },
1165
1186
  {
1166
1187
  type: "playing",
1167
1188
  handler: onPlaying
@@ -1333,6 +1354,7 @@ function createHlsAdPlayer(contentVideo, options) {
1333
1354
  clearInterval(stallWatchdogId);
1334
1355
  stallWatchdogId = void 0;
1335
1356
  }
1357
+ cancelVideoFrameProbe();
1336
1358
  }
1337
1359
  function noteAdProgress() {
1338
1360
  lastAdProgressTime = Date.now();
@@ -1351,13 +1373,86 @@ function createHlsAdPlayer(contentVideo, options) {
1351
1373
  }
1352
1374
  handleAdError();
1353
1375
  }
1376
+ function getDecodedVideoFrameCount(video) {
1377
+ try {
1378
+ if (typeof video.getVideoPlaybackQuality === "function") {
1379
+ var quality = video.getVideoPlaybackQuality();
1380
+ if (quality && typeof quality.totalVideoFrames === "number") {
1381
+ return quality.totalVideoFrames;
1382
+ }
1383
+ }
1384
+ var legacyCount = video.webkitDecodedFrameCount;
1385
+ if (typeof legacyCount === "number") {
1386
+ return legacyCount;
1387
+ }
1388
+ } catch (error) {
1389
+ console.warn("[HlsAdPlayer] Unable to read decoded video frame count:", error);
1390
+ }
1391
+ return void 0;
1392
+ }
1393
+ function isFrameCounterReliable() {
1394
+ return contentVideo[FRAME_COUNTER_RELIABLE_PROP] === true;
1395
+ }
1396
+ function markFrameCounterReliable() {
1397
+ contentVideo[FRAME_COUNTER_RELIABLE_PROP] = true;
1398
+ }
1399
+ function cancelVideoFrameProbe() {
1400
+ if (videoFrameCallbackHandle != null && adVideoElement && typeof adVideoElement.cancelVideoFrameCallback === "function") {
1401
+ try {
1402
+ adVideoElement.cancelVideoFrameCallback(videoFrameCallbackHandle);
1403
+ } catch (error) {
1404
+ console.warn("[HlsAdPlayer] Error cancelling video frame callback:", error);
1405
+ }
1406
+ }
1407
+ videoFrameCallbackHandle = void 0;
1408
+ }
1409
+ function startVideoFrameProbe() {
1410
+ cancelVideoFrameProbe();
1411
+ if (!adVideoElement) return;
1412
+ var rvfc = adVideoElement.requestVideoFrameCallback;
1413
+ if (typeof rvfc !== "function") return;
1414
+ var onPresentedFrame = function onPresentedFrame() {
1415
+ videoFrameCallbackHandle = void 0;
1416
+ if (!adPlaying) return;
1417
+ sawDecodedVideoFrame = true;
1418
+ };
1419
+ try {
1420
+ videoFrameCallbackHandle = rvfc.call(adVideoElement, onPresentedFrame);
1421
+ } catch (error) {
1422
+ console.warn("[HlsAdPlayer] Error requesting video frame callback:", error);
1423
+ videoFrameCallbackHandle = void 0;
1424
+ }
1425
+ }
1426
+ function handleVideoDecodeFailure() {
1427
+ console.warn("[HlsAdPlayer] Ad audio is advancing but no video frames decoded (black screen) - skipping this creative");
1428
+ clearStallWatchdog();
1429
+ if (currentAd) {
1430
+ fireTrackingPixels(currentAd.trackingUrls.error);
1431
+ }
1432
+ if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1433
+ return;
1434
+ }
1435
+ handleAdError();
1436
+ }
1354
1437
  function startStallWatchdog() {
1355
1438
  clearStallWatchdog();
1356
1439
  noteAdProgress();
1440
+ sawDecodedVideoFrame = false;
1441
+ startVideoFrameProbe();
1357
1442
  stallWatchdogId = window.setInterval(function() {
1358
1443
  if (!adPlaying || !adVideoElement) {
1359
1444
  return;
1360
1445
  }
1446
+ if (!sawDecodedVideoFrame && !adVideoElement.paused) {
1447
+ var decodedFrames = getDecodedVideoFrameCount(adVideoElement);
1448
+ if (decodedFrames !== void 0 && decodedFrames > 0) {
1449
+ sawDecodedVideoFrame = true;
1450
+ markFrameCounterReliable();
1451
+ } else if (decodedFrames === 0 && adVideoElement.currentTime >= BLACKFRAME_MIN_PLAYBACK_S && isFrameCounterReliable()) {
1452
+ handleVideoDecodeFailure();
1453
+ return;
1454
+ }
1455
+ }
1361
1456
  if (adVideoElement.currentTime > lastAdProgressPosition + 0.05) {
1362
1457
  noteAdProgress();
1363
1458
  return;
@@ -6198,17 +6293,17 @@ var AdTimingService = /*#__PURE__*/ function() {
6198
6293
  {
6199
6294
  key: "logAdState",
6200
6295
  value: function logAdState(event) {
6201
- 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;
6202
- var _ref;
6296
+ var extra = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
6203
6297
  if (!this.debug) {
6204
6298
  return;
6205
6299
  }
6300
+ var _this_callbacks_getAdState = this.callbacks.getAdState(), inAdBreak = _this_callbacks_getAdState.inAdBreak, showAds = _this_callbacks_getAdState.showAds, adPlaying = _this_callbacks_getAdState.adPlaying;
6206
6301
  console.log("[StormcloudVideoPlayer][AdState]", _object_spread({
6207
6302
  event: event,
6208
6303
  timestamp: /* @__PURE__ */ new Date().toISOString(),
6209
- showAds: showAds !== null && showAds !== void 0 ? showAds : false,
6210
- adPlaying: (_ref = adPlayer === null || adPlayer === void 0 ? void 0 : adPlayer.isAdPlaying()) !== null && _ref !== void 0 ? _ref : false,
6211
- inAdBreak: inAdBreak !== null && inAdBreak !== void 0 ? inAdBreak : false,
6304
+ showAds: showAds,
6305
+ adPlaying: adPlaying,
6306
+ inAdBreak: inAdBreak,
6212
6307
  activeAdRequestToken: this.activeAdRequestToken
6213
6308
  }, extra));
6214
6309
  }
@@ -8558,6 +8653,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8558
8653
  },
8559
8654
  onFailsafeTimeout: function onFailsafeTimeout() {
8560
8655
  return _this.adBreak.handleAdFailure();
8656
+ },
8657
+ getAdState: function getAdState() {
8658
+ return {
8659
+ inAdBreak: _this.adBreak.inAdBreak,
8660
+ showAds: _this.adBreak.showAds,
8661
+ adPlaying: _this.adPlayer.isAdPlaying()
8662
+ };
8561
8663
  }
8562
8664
  });
8563
8665
  this.placeholder = new PlaceholderLayer(this.video, !!this.config.debugAdTiming);