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
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
interface AdBreak {
|
|
2
|
+
id?: string;
|
|
3
|
+
startTimeMs: number;
|
|
4
|
+
durationMs?: number;
|
|
5
|
+
vastTagUrl?: string;
|
|
6
|
+
}
|
|
7
|
+
interface StormcloudVideoPlayerConfig {
|
|
8
|
+
videoElement: HTMLVideoElement;
|
|
9
|
+
src: string;
|
|
10
|
+
autoplay?: boolean;
|
|
11
|
+
muted?: boolean;
|
|
12
|
+
allowNativeHls?: boolean;
|
|
13
|
+
lowLatencyMode?: boolean;
|
|
14
|
+
isLiveStream?: boolean;
|
|
15
|
+
pauseContentDuringAds?: boolean;
|
|
16
|
+
driftToleranceMs?: number;
|
|
17
|
+
immediateManifestAds?: boolean;
|
|
18
|
+
debugAdTiming?: boolean;
|
|
19
|
+
adFailsafeTimeoutMs?: number;
|
|
20
|
+
adBreakCheckIntervalMs?: number;
|
|
21
|
+
maxAdBreakExtensionMs?: number;
|
|
22
|
+
minRemainingMsToStartAd?: number;
|
|
23
|
+
showCustomControls?: boolean;
|
|
24
|
+
hideLoadingIndicator?: boolean;
|
|
25
|
+
onVolumeToggle?: () => void;
|
|
26
|
+
onFullscreenToggle?: () => void;
|
|
27
|
+
onControlClick?: () => void;
|
|
28
|
+
licenseKey?: string;
|
|
29
|
+
vastTagUrl?: string;
|
|
30
|
+
isVmap?: boolean;
|
|
31
|
+
vmapUrl?: string;
|
|
32
|
+
vastMode?: 'adstorm' | 'default';
|
|
33
|
+
minSegmentsBeforePlay?: number;
|
|
34
|
+
ctvAdRequest?: boolean;
|
|
35
|
+
adTest?: boolean;
|
|
36
|
+
podMaxAds?: number;
|
|
37
|
+
podMaxDurationMs?: number;
|
|
38
|
+
podExactDuration?: boolean;
|
|
39
|
+
}
|
|
40
|
+
interface Scte35Marker {
|
|
41
|
+
type: "start" | "end" | "progress";
|
|
42
|
+
ptsSeconds?: number;
|
|
43
|
+
durationSeconds?: number;
|
|
44
|
+
raw?: unknown;
|
|
45
|
+
}
|
|
46
|
+
interface Id3TagInfo {
|
|
47
|
+
key: string;
|
|
48
|
+
value: string | Uint8Array;
|
|
49
|
+
ptsSeconds?: number;
|
|
50
|
+
}
|
|
51
|
+
interface AdController {
|
|
52
|
+
initialize: () => void;
|
|
53
|
+
requestAds: (vastTagUrl: string) => Promise<void>;
|
|
54
|
+
play: () => Promise<void>;
|
|
55
|
+
pause: () => void;
|
|
56
|
+
resume: () => void;
|
|
57
|
+
stop: () => Promise<void>;
|
|
58
|
+
destroy: () => void;
|
|
59
|
+
isAdPlaying: () => boolean;
|
|
60
|
+
resize: (width: number, height: number) => void;
|
|
61
|
+
on: (event: string, listener: (payload?: any) => void) => void;
|
|
62
|
+
off: (event: string, listener: (payload?: any) => void) => void;
|
|
63
|
+
updateOriginalMutedState: (muted: boolean, volume?: number) => void;
|
|
64
|
+
getOriginalMutedState: () => boolean;
|
|
65
|
+
getOriginalVolume: () => number;
|
|
66
|
+
setAdVolume: (volume: number) => void;
|
|
67
|
+
getAdVolume: () => number;
|
|
68
|
+
showPlaceholder: () => void;
|
|
69
|
+
hidePlaceholder: () => void;
|
|
70
|
+
getLoadedAdDuration?: () => number | undefined;
|
|
71
|
+
getLoadedAdId?: () => string | undefined;
|
|
72
|
+
getPodAdCount?: () => number;
|
|
73
|
+
getPodTotalDurationMs?: () => number;
|
|
74
|
+
}
|
|
75
|
+
interface ClientInfo {
|
|
76
|
+
brand: string;
|
|
77
|
+
os: string;
|
|
78
|
+
model: string;
|
|
79
|
+
deviceType: "tv" | "mobile" | "tablet" | "desktop";
|
|
80
|
+
isSmartTV: boolean;
|
|
81
|
+
isAndroid: boolean;
|
|
82
|
+
isWebView: boolean;
|
|
83
|
+
isWebApp: boolean;
|
|
84
|
+
domain: string;
|
|
85
|
+
origin: string;
|
|
86
|
+
path: string;
|
|
87
|
+
userAgent: string;
|
|
88
|
+
vendor: string;
|
|
89
|
+
platform: string;
|
|
90
|
+
screen: {
|
|
91
|
+
width?: number;
|
|
92
|
+
height?: number;
|
|
93
|
+
availWidth?: number;
|
|
94
|
+
availHeight?: number;
|
|
95
|
+
orientation?: string;
|
|
96
|
+
pixelDepth?: number;
|
|
97
|
+
};
|
|
98
|
+
hardwareConcurrency: number;
|
|
99
|
+
deviceMemory: number | null;
|
|
100
|
+
maxTouchPoints: number;
|
|
101
|
+
language: string;
|
|
102
|
+
languages: string;
|
|
103
|
+
cookieEnabled: boolean;
|
|
104
|
+
doNotTrack: string;
|
|
105
|
+
referrer: string;
|
|
106
|
+
visibilityState: string;
|
|
107
|
+
}
|
|
108
|
+
interface PlayerAnalyticsContext {
|
|
109
|
+
inputStreamType?: string;
|
|
110
|
+
debugAdTiming?: boolean;
|
|
111
|
+
}
|
|
112
|
+
interface AdDetectInfo {
|
|
113
|
+
source: "scte35";
|
|
114
|
+
durationSeconds?: number;
|
|
115
|
+
ptsSeconds?: number;
|
|
116
|
+
detectedAtFragmentSn?: number;
|
|
117
|
+
timestamp: string;
|
|
118
|
+
}
|
|
119
|
+
interface AdLoadedInfo {
|
|
120
|
+
source: "vast" | "hls";
|
|
121
|
+
vastUrl?: string;
|
|
122
|
+
durationSeconds?: number;
|
|
123
|
+
timestamp: string;
|
|
124
|
+
}
|
|
125
|
+
interface AdImpressionInfo {
|
|
126
|
+
source: "vast" | "hls";
|
|
127
|
+
adIndex: number;
|
|
128
|
+
adUrl?: string;
|
|
129
|
+
durationSeconds?: number;
|
|
130
|
+
timestamp: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export type { AdController as A, ClientInfo as C, Id3TagInfo as I, PlayerAnalyticsContext as P, StormcloudVideoPlayerConfig as S, Scte35Marker as a, AdBreak as b, AdDetectInfo as c, AdImpressionInfo as d, AdLoadedInfo as e };
|
|
@@ -411,9 +411,11 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
411
411
|
var pendingTimeouts = [];
|
|
412
412
|
var STALL_TIMEOUT_MS = 4e3;
|
|
413
413
|
var STALL_CHECK_INTERVAL_MS = 1e3;
|
|
414
|
+
var BLACKFRAME_MIN_PLAYBACK_S = 1.5;
|
|
414
415
|
var stallWatchdogId;
|
|
415
416
|
var lastAdProgressTime = 0;
|
|
416
417
|
var lastAdProgressPosition = 0;
|
|
418
|
+
var sawDecodedVideoFrame = false;
|
|
417
419
|
var trackingFired = {
|
|
418
420
|
impression: false,
|
|
419
421
|
start: false,
|
|
@@ -555,6 +557,61 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
555
557
|
var url = mediaFile.url.toLowerCase();
|
|
556
558
|
return type.startsWith("video/") || url.endsWith(".mp4") || url.includes(".mp4?");
|
|
557
559
|
}
|
|
560
|
+
function isAudioMediaFile(mediaFile) {
|
|
561
|
+
var type = mediaFile.type.toLowerCase();
|
|
562
|
+
var url = (mediaFile.url.toLowerCase().split("?")[0] || "").trim();
|
|
563
|
+
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");
|
|
564
|
+
}
|
|
565
|
+
function collectVideoMediaFiles(scope, verbose) {
|
|
566
|
+
var mediaFileElements = scope.querySelectorAll("MediaFile");
|
|
567
|
+
var mediaFiles = [];
|
|
568
|
+
if (verbose) {
|
|
569
|
+
console.log("[HlsAdPlayer] Found ".concat(mediaFileElements.length, " MediaFile element(s) in VAST XML"));
|
|
570
|
+
}
|
|
571
|
+
mediaFileElements.forEach(function(mf, index) {
|
|
572
|
+
var _mf_textContent;
|
|
573
|
+
var type = mf.getAttribute("type") || "";
|
|
574
|
+
var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
|
|
575
|
+
var width = mf.getAttribute("width") || "";
|
|
576
|
+
var height = mf.getAttribute("height") || "";
|
|
577
|
+
if (verbose) {
|
|
578
|
+
console.log("[HlsAdPlayer] MediaFile ".concat(index, ': type="').concat(type, '", url="').concat(url, '", width="').concat(width, '", height="').concat(height, '"'));
|
|
579
|
+
}
|
|
580
|
+
if (!url) {
|
|
581
|
+
if (verbose) {
|
|
582
|
+
console.warn("[HlsAdPlayer] MediaFile ".concat(index, " has empty URL"));
|
|
583
|
+
}
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
var bitrateAttr = mf.getAttribute("bitrate");
|
|
587
|
+
var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
|
|
588
|
+
var mediaFile = {
|
|
589
|
+
url: url,
|
|
590
|
+
type: type,
|
|
591
|
+
width: parseInt(width || "1920", 10),
|
|
592
|
+
height: parseInt(height || "1080", 10),
|
|
593
|
+
bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
|
|
594
|
+
};
|
|
595
|
+
if (isAudioMediaFile(mediaFile)) {
|
|
596
|
+
console.warn("[HlsAdPlayer] MediaFile ".concat(index, ' skipped (audio-only, type="').concat(type, '") — audio ads are not played to avoid a black screen'));
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
var hasExplicitDimensions = mf.getAttribute("width") != null && mf.getAttribute("height") != null;
|
|
600
|
+
if (!isHlsMediaFile(mediaFile) && hasExplicitDimensions && (mediaFile.width <= 1 || mediaFile.height <= 1)) {
|
|
601
|
+
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'));
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
|
|
605
|
+
mediaFiles.push(mediaFile);
|
|
606
|
+
if (verbose) {
|
|
607
|
+
console.log("[HlsAdPlayer] Added ".concat(isHlsMediaFile(mediaFile) ? "HLS" : "progressive", " MediaFile: ").concat(url));
|
|
608
|
+
}
|
|
609
|
+
} else if (verbose) {
|
|
610
|
+
console.log("[HlsAdPlayer] MediaFile ".concat(index, ' ignored (type="').concat(type, '" is not supported)'));
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
return mediaFiles;
|
|
614
|
+
}
|
|
558
615
|
function createEmptyTrackingUrls() {
|
|
559
616
|
return {
|
|
560
617
|
impression: [],
|
|
@@ -642,37 +699,7 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
642
699
|
var durationText = ((_xmlDoc_querySelector3 = xmlDoc.querySelector("Duration")) === null || _xmlDoc_querySelector3 === void 0 ? void 0 : _xmlDoc_querySelector3.textContent) || "00:00:30";
|
|
643
700
|
var durationParts = durationText.split(":");
|
|
644
701
|
var duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
|
|
645
|
-
var
|
|
646
|
-
var mediaFiles = [];
|
|
647
|
-
console.log("[HlsAdPlayer] Found ".concat(mediaFileElements.length, " MediaFile element(s) in VAST XML"));
|
|
648
|
-
mediaFileElements.forEach(function(mf, index) {
|
|
649
|
-
var _mf_textContent;
|
|
650
|
-
var type = mf.getAttribute("type") || "";
|
|
651
|
-
var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
|
|
652
|
-
var width = mf.getAttribute("width") || "";
|
|
653
|
-
var height = mf.getAttribute("height") || "";
|
|
654
|
-
console.log("[HlsAdPlayer] MediaFile ".concat(index, ': type="').concat(type, '", url="').concat(url, '", width="').concat(width, '", height="').concat(height, '"'));
|
|
655
|
-
var mediaFile = {
|
|
656
|
-
url: url,
|
|
657
|
-
type: type,
|
|
658
|
-
width: parseInt(width || "1920", 10),
|
|
659
|
-
height: parseInt(height || "1080", 10),
|
|
660
|
-
bitrate: void 0
|
|
661
|
-
};
|
|
662
|
-
if (!url) {
|
|
663
|
-
console.warn("[HlsAdPlayer] MediaFile ".concat(index, " has empty URL"));
|
|
664
|
-
return;
|
|
665
|
-
}
|
|
666
|
-
var bitrateAttr = mf.getAttribute("bitrate");
|
|
667
|
-
var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
|
|
668
|
-
mediaFile.bitrate = bitrateValue && bitrateValue > 0 ? bitrateValue : void 0;
|
|
669
|
-
if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
|
|
670
|
-
mediaFiles.push(mediaFile);
|
|
671
|
-
console.log("[HlsAdPlayer] Added ".concat(isHlsMediaFile(mediaFile) ? "HLS" : "progressive", " MediaFile: ").concat(url));
|
|
672
|
-
} else {
|
|
673
|
-
console.log("[HlsAdPlayer] MediaFile ".concat(index, ' ignored (type="').concat(type, '" is not supported)'));
|
|
674
|
-
}
|
|
675
|
-
});
|
|
702
|
+
var mediaFiles = collectVideoMediaFiles(xmlDoc, true);
|
|
676
703
|
if (mediaFiles.length === 0) {
|
|
677
704
|
if (isNoAdAvailable) {
|
|
678
705
|
console.warn("[HlsAdPlayer] No ads available (VAST response indicates no ads)");
|
|
@@ -720,28 +747,7 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
720
747
|
var durationText = ((_adElement_querySelector3 = adElement.querySelector("Duration")) === null || _adElement_querySelector3 === void 0 ? void 0 : _adElement_querySelector3.textContent) || "00:00:30";
|
|
721
748
|
var durationParts = durationText.split(":");
|
|
722
749
|
var duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
|
|
723
|
-
var
|
|
724
|
-
var mediaFiles = [];
|
|
725
|
-
mediaFileElements.forEach(function(mf) {
|
|
726
|
-
var _mf_textContent;
|
|
727
|
-
var type = mf.getAttribute("type") || "";
|
|
728
|
-
var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
|
|
729
|
-
var width = mf.getAttribute("width") || "";
|
|
730
|
-
var height = mf.getAttribute("height") || "";
|
|
731
|
-
if (!url) return;
|
|
732
|
-
var bitrateAttr = mf.getAttribute("bitrate");
|
|
733
|
-
var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
|
|
734
|
-
var mediaFile = {
|
|
735
|
-
url: url,
|
|
736
|
-
type: type,
|
|
737
|
-
width: parseInt(width || "1920", 10),
|
|
738
|
-
height: parseInt(height || "1080", 10),
|
|
739
|
-
bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
|
|
740
|
-
};
|
|
741
|
-
if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
|
|
742
|
-
mediaFiles.push(mediaFile);
|
|
743
|
-
}
|
|
744
|
-
});
|
|
750
|
+
var mediaFiles = collectVideoMediaFiles(adElement, false);
|
|
745
751
|
if (mediaFiles.length === 0) {
|
|
746
752
|
if (isNoAdAvailable) {
|
|
747
753
|
console.warn("[HlsAdPlayer] Pod <Ad> indicates no ad available");
|
|
@@ -1062,6 +1068,14 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1062
1068
|
fireTrackingPixels(currentAd.trackingUrls.thirdQuartile);
|
|
1063
1069
|
}
|
|
1064
1070
|
};
|
|
1071
|
+
var onLoadedMetadata = function onLoadedMetadata() {
|
|
1072
|
+
if (!adPlaying || !adVideoElement) return;
|
|
1073
|
+
var videoWidth = adVideoElement.videoWidth, videoHeight = adVideoElement.videoHeight;
|
|
1074
|
+
if (videoWidth <= 1 || videoHeight <= 1) {
|
|
1075
|
+
console.warn("[HlsAdPlayer] Ad creative has no usable video dimensions (".concat(videoWidth, "x").concat(videoHeight, ") - skipping this creative"));
|
|
1076
|
+
handleVideoDecodeFailure();
|
|
1077
|
+
}
|
|
1078
|
+
};
|
|
1065
1079
|
var onPlaying = function onPlaying() {
|
|
1066
1080
|
startStallWatchdog();
|
|
1067
1081
|
if (!currentAd || trackingFired.start) return;
|
|
@@ -1113,6 +1127,10 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1113
1127
|
type: "timeupdate",
|
|
1114
1128
|
handler: onTimeUpdate
|
|
1115
1129
|
},
|
|
1130
|
+
{
|
|
1131
|
+
type: "loadedmetadata",
|
|
1132
|
+
handler: onLoadedMetadata
|
|
1133
|
+
},
|
|
1116
1134
|
{
|
|
1117
1135
|
type: "playing",
|
|
1118
1136
|
handler: onPlaying
|
|
@@ -1302,13 +1320,51 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1302
1320
|
}
|
|
1303
1321
|
handleAdError();
|
|
1304
1322
|
}
|
|
1323
|
+
function getDecodedVideoFrameCount(video) {
|
|
1324
|
+
try {
|
|
1325
|
+
if (typeof video.getVideoPlaybackQuality === "function") {
|
|
1326
|
+
var quality = video.getVideoPlaybackQuality();
|
|
1327
|
+
if (quality && typeof quality.totalVideoFrames === "number") {
|
|
1328
|
+
return quality.totalVideoFrames;
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
var legacyCount = video.webkitDecodedFrameCount;
|
|
1332
|
+
if (typeof legacyCount === "number") {
|
|
1333
|
+
return legacyCount;
|
|
1334
|
+
}
|
|
1335
|
+
} catch (error) {
|
|
1336
|
+
console.warn("[HlsAdPlayer] Unable to read decoded video frame count:", error);
|
|
1337
|
+
}
|
|
1338
|
+
return void 0;
|
|
1339
|
+
}
|
|
1340
|
+
function handleVideoDecodeFailure() {
|
|
1341
|
+
console.warn("[HlsAdPlayer] Ad audio is advancing but no video frames decoded (black screen) - skipping this creative");
|
|
1342
|
+
clearStallWatchdog();
|
|
1343
|
+
if (currentAd) {
|
|
1344
|
+
fireTrackingPixels(currentAd.trackingUrls.error);
|
|
1345
|
+
}
|
|
1346
|
+
if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
handleAdError();
|
|
1350
|
+
}
|
|
1305
1351
|
function startStallWatchdog() {
|
|
1306
1352
|
clearStallWatchdog();
|
|
1307
1353
|
noteAdProgress();
|
|
1354
|
+
sawDecodedVideoFrame = false;
|
|
1308
1355
|
stallWatchdogId = window.setInterval(function() {
|
|
1309
1356
|
if (!adPlaying || !adVideoElement) {
|
|
1310
1357
|
return;
|
|
1311
1358
|
}
|
|
1359
|
+
if (!sawDecodedVideoFrame && !adVideoElement.paused) {
|
|
1360
|
+
var decodedFrames = getDecodedVideoFrameCount(adVideoElement);
|
|
1361
|
+
if (decodedFrames !== void 0 && decodedFrames > 0) {
|
|
1362
|
+
sawDecodedVideoFrame = true;
|
|
1363
|
+
} else if (decodedFrames === 0 && adVideoElement.currentTime >= BLACKFRAME_MIN_PLAYBACK_S) {
|
|
1364
|
+
handleVideoDecodeFailure();
|
|
1365
|
+
return;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1312
1368
|
if (adVideoElement.currentTime > lastAdProgressPosition + 0.05) {
|
|
1313
1369
|
noteAdProgress();
|
|
1314
1370
|
return;
|