stormcloud-video-player 0.8.48 → 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 +57 -0
- package/lib/index.cjs.map +1 -1
- package/lib/index.js +57 -0
- 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 +57 -0
- 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 +57 -0
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/HlsPlayer.d.cts +1 -1
- package/lib/players/index.cjs +57 -0
- package/lib/players/index.cjs.map +1 -1
- package/lib/sdk/hlsAdPlayer.cjs +57 -0
- 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 +57 -0
- 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,
|
|
@@ -594,6 +596,11 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
594
596
|
console.warn("[HlsAdPlayer] MediaFile ".concat(index, ' skipped (audio-only, type="').concat(type, '") — audio ads are not played to avoid a black screen'));
|
|
595
597
|
return;
|
|
596
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
|
+
}
|
|
597
604
|
if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
|
|
598
605
|
mediaFiles.push(mediaFile);
|
|
599
606
|
if (verbose) {
|
|
@@ -1061,6 +1068,14 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1061
1068
|
fireTrackingPixels(currentAd.trackingUrls.thirdQuartile);
|
|
1062
1069
|
}
|
|
1063
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
|
+
};
|
|
1064
1079
|
var onPlaying = function onPlaying() {
|
|
1065
1080
|
startStallWatchdog();
|
|
1066
1081
|
if (!currentAd || trackingFired.start) return;
|
|
@@ -1112,6 +1127,10 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1112
1127
|
type: "timeupdate",
|
|
1113
1128
|
handler: onTimeUpdate
|
|
1114
1129
|
},
|
|
1130
|
+
{
|
|
1131
|
+
type: "loadedmetadata",
|
|
1132
|
+
handler: onLoadedMetadata
|
|
1133
|
+
},
|
|
1115
1134
|
{
|
|
1116
1135
|
type: "playing",
|
|
1117
1136
|
handler: onPlaying
|
|
@@ -1301,13 +1320,51 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1301
1320
|
}
|
|
1302
1321
|
handleAdError();
|
|
1303
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
|
+
}
|
|
1304
1351
|
function startStallWatchdog() {
|
|
1305
1352
|
clearStallWatchdog();
|
|
1306
1353
|
noteAdProgress();
|
|
1354
|
+
sawDecodedVideoFrame = false;
|
|
1307
1355
|
stallWatchdogId = window.setInterval(function() {
|
|
1308
1356
|
if (!adPlaying || !adVideoElement) {
|
|
1309
1357
|
return;
|
|
1310
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
|
+
}
|
|
1311
1368
|
if (adVideoElement.currentTime > lastAdProgressPosition + 0.05) {
|
|
1312
1369
|
noteAdProgress();
|
|
1313
1370
|
return;
|