@dolby-optiview/ads-sdk-core 1.0.0 → 1.1.1
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/README.md +68 -0
- package/dist/OptiViewAds.d.ts +1 -0
- package/dist/OptiViewAds.js +1 -1
- package/dist/diagnostics/recordEventTimeline.d.ts +3 -0
- package/dist/diagnostics/recordEventTimeline.js +1 -0
- package/dist/generated/diagnosticCodes.d.ts +142 -2
- package/dist/generated/diagnosticCodes.js +1 -1
- package/dist/generated/version.d.ts +1 -1
- package/dist/generated/version.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/interfaces/PlayerAdapter.d.ts +33 -0
- package/dist/internal/index.d.ts +1 -1
- package/dist/internal/index.js +1 -1
- package/dist/services/AdPlayerController.d.ts +9 -1
- package/dist/services/AdPlayerController.js +1 -1
- package/dist/services/BreakScheduler.d.ts +5 -2
- package/dist/services/BreakScheduler.js +1 -1
- package/dist/services/CmcdAdReporter.d.ts +10 -0
- package/dist/services/CmcdAdReporter.js +1 -1
- package/dist/services/GamPodAdTracker.d.ts +12 -1
- package/dist/services/GamPodAdTracker.js +1 -1
- package/dist/services/LensReporter.d.ts +3 -1
- package/dist/services/LensReporter.js +1 -1
- package/dist/services/LiveStartPushbackController.d.ts +36 -0
- package/dist/services/LiveStartPushbackController.js +1 -0
- package/dist/services/MetadataCueTracker.d.ts +42 -1
- package/dist/services/MetadataCueTracker.js +1 -1
- package/dist/services/VastAdManager.js +1 -1
- package/dist/services/adPlayer/AdPlayerState.d.ts +11 -0
- package/dist/services/adPlayer/AdPlayerState.js +1 -1
- package/dist/services/adPlayer/AdPreloader.d.ts +9 -1
- package/dist/services/adPlayer/AdPreloader.js +1 -1
- package/dist/services/adPlayer/AdStage.d.ts +5 -0
- package/dist/services/adPlayer/AdStage.js +1 -1
- package/dist/services/adPlayer/AssetPlayback.d.ts +15 -1
- package/dist/services/adPlayer/AssetPlayback.js +1 -1
- package/dist/services/adPlayer/ContentRecovery.js +1 -1
- package/dist/services/adPlayer/ContentResumeCoordinator.d.ts +81 -2
- package/dist/services/adPlayer/ContentResumeCoordinator.js +1 -1
- package/dist/services/adPlayer/GamPodBridge.d.ts +7 -0
- package/dist/services/adPlayer/GamPodBridge.js +1 -1
- package/dist/services/adPlayer/PreparedPresentation.d.ts +32 -0
- package/dist/services/adPlayer/PreparedPresentation.js +1 -0
- package/dist/services/adPlayer/SharedElementBreakPlayer.d.ts +8 -0
- package/dist/services/adPlayer/SharedElementBreakPlayer.js +1 -1
- package/dist/services/adPlayer/classifyMediaError.d.ts +9 -0
- package/dist/services/adPlayer/classifyMediaError.js +1 -0
- package/dist/services/adPlayer/liveEdge.d.ts +29 -0
- package/dist/services/adPlayer/liveEdge.js +1 -1
- package/dist/services/adPlayer/streamClockMapping.d.ts +65 -0
- package/dist/services/adPlayer/streamClockMapping.js +1 -0
- package/dist/services/adPlayer/streamClockResume.d.ts +94 -0
- package/dist/services/adPlayer/streamClockResume.js +1 -0
- package/dist/services/anvatoCueRuntime.d.ts +19 -0
- package/dist/services/anvatoCueRuntime.js +1 -1
- package/dist/services/breakTiming.d.ts +7 -0
- package/dist/services/breakTiming.js +1 -1
- package/dist/services/classifyImaAdError.d.ts +6 -0
- package/dist/services/classifyImaAdError.js +1 -0
- package/dist/services/html5ImaErrorNames.d.ts +2 -0
- package/dist/services/html5ImaErrorNames.js +1 -0
- package/dist/services/lensBackend.d.ts +1 -1
- package/dist/services/resolveLiveStartPushback.d.ts +1 -0
- package/dist/services/resolveLiveStartPushback.js +1 -0
- package/dist/session/AdBreakStatusTracker.js +1 -1
- package/dist/session/BreakLifecycle.js +1 -1
- package/dist/session/SessionStarter.d.ts +1 -0
- package/dist/session/SessionStarter.js +1 -1
- package/dist/types/AdError.d.ts +12 -0
- package/dist/types/AdError.js +1 -0
- package/dist/types/OptiViewAdsConfig.d.ts +17 -0
- package/dist/types/VendorAdError.d.ts +6 -5
- package/dist/types/VendorAdError.js +1 -1
- package/dist/vendor/lens-sdk/THIRD-PARTY-NOTICES.txt +1 -1
- package/dist/vendor/lens-sdk/index.d.ts +1 -1
- package/dist/vendor/lens-sdk/index.js +1 -1
- package/dist/vendor/lens-sdk/lens-core-uGZfZpQy.js +1 -0
- package/dist/vendor/lens-sdk/lens.d.ts +2 -0
- package/dist/vendor/lens-sdk/teardown.d.ts +13 -0
- package/dist/vendor/lens-sdk/transport.d.ts +11 -2
- package/dist/vendor/lens-sdk/types.d.ts +56 -5
- package/dist/vendor/lens-sdk/validation.d.ts +4 -1
- package/package.json +1 -1
- package/dist/vendor/lens-sdk/lens-core-Q5lbQGMC.js +0 -1
|
@@ -37,6 +37,13 @@ export interface TimedMetadataCue {
|
|
|
37
37
|
* parsed cue as `presented` can cause an ad break to start before its scheduled time.
|
|
38
38
|
*/
|
|
39
39
|
phase?: 'parsed' | 'presented' | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Player media time minus the stream's PTS clock, in seconds, when the adapter knows it
|
|
42
|
+
* (DASH: `Period@start - presentationTimeOffset / timescale`). A DASH emsg whose
|
|
43
|
+
* `presentation_time_delta` is 0 reports its carrying segment's start as `startTime`; with
|
|
44
|
+
* this offset the core places an in-band break cue at its own `pts` instead.
|
|
45
|
+
*/
|
|
46
|
+
ptsOffset?: number | undefined;
|
|
40
47
|
}
|
|
41
48
|
/** For `timedmetadata` the payload is a {@link TimedMetadataCue}; other events are event-specific (often undefined, or a media error for `error`). @category Player Integration */
|
|
42
49
|
export type PlayerAdapterEventHandler = (event?: unknown) => void;
|
|
@@ -146,6 +153,27 @@ export interface PlayerAdapter {
|
|
|
146
153
|
* and rejects sentinel values.
|
|
147
154
|
*/
|
|
148
155
|
readonly seekableEnd?: number | null;
|
|
156
|
+
/** Start of the same seekable range as seekableEnd; null/omitted when unknown. */
|
|
157
|
+
readonly seekableStart?: number | null;
|
|
158
|
+
/**
|
|
159
|
+
* End of the newest published media segment in seconds (the live edge), or `null` when unknown.
|
|
160
|
+
* Report this when the engine's seekable range stops short of the published playlist end;
|
|
161
|
+
* AVFoundation, for example, ends `seekableTimeRanges` one hold-back before the edge. If omitted,
|
|
162
|
+
* the live start positioning uses {@link seekableEnd} as the edge.
|
|
163
|
+
*/
|
|
164
|
+
readonly liveEdge?: number | null;
|
|
165
|
+
/** Declared HLS EXT-X-TARGETDURATION in seconds, not average segment length; null when unknown. */
|
|
166
|
+
readonly targetDuration?: number | null;
|
|
167
|
+
/** False while play expresses intent but the content timeline is not yet ready for a startup seek. */
|
|
168
|
+
readonly playbackReady?: boolean;
|
|
169
|
+
/**
|
|
170
|
+
* Ask the engine to begin the upcoming live playback `targetDurations` declared target
|
|
171
|
+
* durations behind the live edge, before the first frame, instead of the SDK seeking after it.
|
|
172
|
+
* `fallbackTargetDuration` (seconds) applies when the engine cannot read the playlist's.
|
|
173
|
+
* `null` withdraws the request. Return `false` when the engine cannot honour it for the
|
|
174
|
+
* upcoming start; the SDK then seeks after the first frame. Omit when unsupported.
|
|
175
|
+
*/
|
|
176
|
+
requestLiveStartOffset?(targetDurations: number | null, fallbackTargetDuration: number | null): boolean;
|
|
149
177
|
/**
|
|
150
178
|
* Playback rate (`1` = normal). Used to correct drift while content continues behind an
|
|
151
179
|
* ad with `continueContentDuringBreak`. Report the engine's current rate, not just the last
|
|
@@ -160,6 +188,11 @@ export interface PlayerAdapter {
|
|
|
160
188
|
* Resolve only when the element is free; do not destroy the adapter. The SDK restores
|
|
161
189
|
* content with `load()` when the break ends. Omit this method if your player supports
|
|
162
190
|
* sharing the element without an explicit release, as HLS.js does.
|
|
191
|
+
*
|
|
192
|
+
* The same method is called on the ad adapter that `createAdAdapter(container, video)` built
|
|
193
|
+
* for the content element, right before content is reloaded at break end. Leave the ad media
|
|
194
|
+
* on the element there: the content engine's `load()` replaces it in one step, and an element
|
|
195
|
+
* without a resource closes Safari's picture-in-picture window.
|
|
163
196
|
*/
|
|
164
197
|
releaseMediaElement?(): void | Promise<void>;
|
|
165
198
|
/**
|
package/dist/internal/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {};
|
|
1
|
+
export { preparePresentation, getPreparedPresentation, disposePreparedPresentation } from '../services/adPlayer/PreparedPresentation';
|
package/dist/internal/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{};
|
|
1
|
+
export{preparePresentation,getPreparedPresentation,disposePreparedPresentation}from"../services/adPlayer/PreparedPresentation";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PlayerAdapter } from '../interfaces/PlayerAdapter';
|
|
1
|
+
import { PlayerAdapter, type TimedMetadataCue } from '../interfaces/PlayerAdapter';
|
|
2
2
|
import { Asset, Break, BreakFormat, Timebase } from '../types/BreakManifest';
|
|
3
3
|
import { AdInsertionType, CreateAdAdapter, DoubleBoxAudio, GamConfig } from '../types/OptiViewAdsConfig';
|
|
4
4
|
import { AssetParameterMacros } from './assetParameterMacros';
|
|
@@ -11,6 +11,7 @@ import { type NoPlayableAssetReason } from './AdBreakSequencer';
|
|
|
11
11
|
import type { PauseAdResolution } from './PauseAdController';
|
|
12
12
|
import { type Clock } from './BreakScheduler';
|
|
13
13
|
import { PauseAdPresenter } from './adPlayer/PauseAdPresenter';
|
|
14
|
+
import type { BreakEndOnStreamClock } from './adPlayer/streamClockResume';
|
|
14
15
|
/** Creative identity IMA reports for an ad of a GAM pod; absent for other asset kinds. */
|
|
15
16
|
export interface AdCreativeIdentity {
|
|
16
17
|
adId?: string | undefined;
|
|
@@ -99,6 +100,7 @@ export declare class AdPlayerController {
|
|
|
99
100
|
private readonly assetParameters;
|
|
100
101
|
private readonly gamPodBridge;
|
|
101
102
|
private readonly adStage;
|
|
103
|
+
private destroyed;
|
|
102
104
|
private readonly preloader;
|
|
103
105
|
private readonly assetPlayback;
|
|
104
106
|
private readonly breakSequenceDriver;
|
|
@@ -232,6 +234,12 @@ export declare class AdPlayerController {
|
|
|
232
234
|
private clearContentGate;
|
|
233
235
|
/** `null` disables chaining: breaks then always hard-end regardless of how close the next one is. */
|
|
234
236
|
setChainResolver(resolver: ((currentBreakId: string) => Break | null) | null): void;
|
|
237
|
+
/** {@link ContentResumeCoordinator.setCueAnchorResolver}. */
|
|
238
|
+
setResumeCueAnchorResolver(resolver: ((breakInfo: Break) => number | null) | null): void;
|
|
239
|
+
/** {@link ContentResumeCoordinator.setCueEndResolver}. */
|
|
240
|
+
setResumeCueEndResolver(resolver: ((breakInfo: Break) => BreakEndOnStreamClock | null) | null): void;
|
|
241
|
+
/** {@link ContentResumeCoordinator.ingestStreamClockCue}. */
|
|
242
|
+
ingestStreamClockCue(cue: TimedMetadataCue): void;
|
|
235
243
|
/** Only wired by the SDK in debug mode, so production reports keep their shape. */
|
|
236
244
|
setTransitionPhaseReporter(reporter: ((name: string) => void) | null): void;
|
|
237
245
|
/** No-op when no reporter is wired or no transition is in flight. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{BreakFormat as e,Timebase as t,isPostRollBreak as r}from"../types/BreakManifest";import{DEFAULT_AD_BREAK_CUT_SAFETY_MARGIN_SEC as s,DEFAULT_DOUBLE_BOX_AUDIO as a}from"../types/OptiViewAdsConfig";import{pipOverridesFormat as i,pipSuppressesBackdrop as n}from"./pictureInPicture";import{AdBreakSequencer as o,assetsOfVariant as d,resolveBreakFormat as l}from"./AdBreakSequencer";import{selectVariantDetailed as h}from"./selectVariant";import{debugLog as u}from"../diagnostics/debugLog";import{systemClock as c}from"./BreakScheduler";import{createAdPlayerState as m,invalidateMediaPreload as g}from"./adPlayer/AdPlayerState";import{PauseAdPresenter as P}from"./adPlayer/PauseAdPresenter";import{AssetParameterResolver as y}from"./adPlayer/AssetParameterResolver";import{GamPodBridge as p}from"./adPlayer/GamPodBridge";import{AdStage as A}from"./adPlayer/AdStage";import{AdPreloader as v}from"./adPlayer/AdPreloader";import{PresentationModeTracker as C}from"./adPlayer/PresentationModeTracker";import{SharedElementBreakPlayer as k}from"./adPlayer/SharedElementBreakPlayer";import{ContentResumeCoordinator as f}from"./adPlayer/ContentResumeCoordinator";import{resumeContentAfterBreak as S}from"./adPlayer/ContentRecovery";import{AssetPlayback as B}from"./adPlayer/AssetPlayback";import{BreakSequenceDriver as b}from"./adPlayer/BreakSequenceDriver";import{VariantSelector as E}from"./adPlayer/VariantSelector";import{AdPlayerEvents as F}from"./adPlayer/AdPlayerEvents";import{armPlaybackCutoff as T,clearPlaybackCutoff as M}from"./adPlayer/PlaybackCutoff";export class AdPlayerController{get contentPaused(){return this.state.contentPaused}set contentPaused(e){this.state.contentPaused=e}get firstFrameGateActive(){return this.state.firstFrameGateActive}set firstFrameGateActive(e){this.state.firstFrameGateActive=e}get firstFrameGateShown(){return this.state.firstFrameGateShown}set firstFrameGateShown(e){this.state.firstFrameGateShown=e}get firstFrameRejector(){return this.state.firstFrameRejector}set firstFrameRejector(e){this.state.firstFrameRejector=e}get gamEventCallbacks(){return this.gamPodBridge.eventCallbacks}get vastAdManager(){return this.state.vastAdManager}set vastAdManager(e){this.state.vastAdManager=e}get currentBreak(){return this.state.currentBreak}set currentBreak(e){this.state.currentBreak=e}get currentAsset(){return this.state.currentAsset}set currentAsset(e){this.state.currentAsset=e}get callbacks(){return this.state.callbacks}set callbacks(e){this.state.callbacks=e}get isPlaying(){return this.state.isPlaying}set isPlaying(e){this.state.isPlaying=e}get effectiveBreakDurationSec(){return this.state.effectiveBreakDurationSec}set effectiveBreakDurationSec(e){this.state.effectiveBreakDurationSec=e}get adBreakCutSafetyMarginSec(){return this.state.adBreakCutSafetyMarginSec}set adBreakCutSafetyMarginSec(e){this.state.adBreakCutSafetyMarginSec=e}get durationTimer(){return this.state.durationTimer}set durationTimer(e){this.state.durationTimer=e}get adInsertionType(){return this.state.adInsertionType}set adInsertionType(e){this.state.adInsertionType=e}get breakTimebase(){return this.state.breakTimebase}set breakTimebase(e){this.state.breakTimebase=e}constructor(e,t,r,i,n=!1,o,d="parallel",l="overlay",h=s,u=a,S=!1,T=c,M="none"){this.playerContainerCreatedBySDK=!1,this.adMediaContainer=null,this.adPlayer=null,this.adContainer=null,this.companionElement=null,this.staticQuartilesFired=new Set,this.sequencer=null,this.pendingAdError=null,this.preRollHoldActive=!1,this.preRollHoldHandler=null,this.breakContentGateHandler=null,this.abortingBreak=!1,this.overlayImageEl=null,this.imageAssetTimer=null,this.chainResolver=null,this.transitionPhaseReporter=null,this.chaining=!1,this.diagnoseHandler=null,this.playerContainerOriginalStyle="",this.companionRemoveTimer=null,this.adFadeTimer=null,this.syncing=!1,this.contentVolumeHandler=null,this.adVolumeChangeHandler=null,this.volumeChangeCallback=null,this.stage=null,this.resizeObserver=null,this.contentVideoElement=null,this.contentVideoGeometryHandler=null,this.contentVideoMutationObserver=null,this.resumeGeneration=0,this.singleSurfaceDevice=!1,this.currentSelectedFormat=void 0,this.audioFocus=null,this.state=m(),this.state.playerContainer=r??null,this.clock=T,this.singleSurfaceDevice=S,this.contentPlayer=e,this.adBreakCutSafetyMarginSec=h,this.doubleBoxAudio=u,this.container=t,r&&(this.playerContainer=r),this.createAdAdapter=i,this.debug=n,this.state.gamConfig=o,this.preloadMode=d,this.insertionMode=l,this.pauseAdPresenter=new P({contentPlayer:this.contentPlayer,getStage:()=>this.stage,resolveUri:e=>this.resolveUri(e),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.assetParameters=new y(this.state,{contentPlayer:this.contentPlayer,container:this.container,getGamStreamManager:()=>this.state.gamStreamManager,getCurrentBreak:()=>this.currentBreak,getEffectiveBreakDuration:()=>this.effectiveBreakDurationSec,diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.gamPodBridge=new p(this.state,{contentPlayer:this.contentPlayer,debug:this.debug,getAdPlayer:()=>this.adPlayer,getAdContainer:()=>this.adContainer,isSharedElement:()=>"shared-element"===this.effectiveInsertionMode(),getCurrentBreak:()=>this.currentBreak,getCurrentAsset:()=>this.currentAsset,getEffectiveBreakDuration:()=>this.effectiveBreakDurationSec,getAdPlayerStreamRemainingSec:()=>this.adPlayerStreamRemainingSec(),emitStep:e=>this.emitStep(e),emitAdStart:(e,t,r,s,a)=>this.callbacks?.onAdStart(e,t,r,s,a),emitAdEnd:(e,t,r,s,a)=>this.callbacks?.onAdEnd(e,t,r,s,a),onAdEnded:()=>this.onAdEnded(),pushAssetParameters:(e,t)=>this.pushAssetParameters(e,t),resolveSessionAdTagParameters:()=>this.assetParameters.resolveSessionAdTagParameters(),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.adStage=new A({contentPlayer:this.contentPlayer,container:this.container,getPlayerContainer:()=>this.playerContainer,setPlayerContainer:e=>{this.playerContainer=e},setPlayerContainerOriginalStyle:e=>{this.playerContainerOriginalStyle=e},onPlayerContainerCreated:()=>{this.playerContainerCreatedBySDK=!0},getStage:()=>this.stage,setStage:e=>{this.stage=e},getContentVideo:()=>this.contentVideoElement,setContentVideo:e=>{this.contentVideoElement=e},getAdContainer:()=>this.adContainer,setAdContainer:e=>{this.adContainer=e},getAdMediaContainer:()=>this.adMediaContainer,setAdMediaContainer:e=>{this.adMediaContainer=e},setAdPlayer:e=>{g(this.state),this.adPlayer=e},createAdAdapter:e=>this.createAdAdapter(e),setupEventListeners:()=>this.setupEventListeners(),resizeVast:()=>this.vastAdManager?.resize(),getVariant:e=>this.getVariant(e),getCurrentBreak:()=>this.currentBreak,resolveUri:e=>this.resolveUri(e),getCompanion:()=>this.companionElement,setCompanion:e=>{this.companionElement=e},getOverlayImage:()=>this.overlayImageEl,setOverlayImage:e=>{this.overlayImageEl=e},getImageAssetTimer:()=>this.imageAssetTimer,setImageAssetTimer:e=>{this.imageAssetTimer=e},isChaining:()=>this.chaining,log:(...e)=>this.log(...e)}),this.preloader=new v({state:this.state,clock:this.clock,adStartBudgetMs:AdPlayerController.AD_START_BUDGET_MS,gamPodAdStartBudgetMs:AdPlayerController.GAM_POD_AD_START_BUDGET_MS,minAdStartBudgetMs:AdPlayerController.MIN_AD_START_BUDGET_MS,debug:this.debug,isPlaying:()=>this.isPlaying,preloadMode:this.preloadMode,effectiveInsertionMode:()=>this.effectiveInsertionMode(),getVariant:e=>this.getVariant(e),getAssets:e=>this.getAssets(e),resolveUri:e=>this.resolveUri(e),resolveAssetUri:(e,t)=>this.resolveAssetUri(e,t),getCompanionVariant:e=>this.getVariant(e),getAdPlayer:()=>this.adPlayer,getAdContainer:()=>this.adContainer,buildVastTagUrl:(e,t)=>this.buildVastTagUrl(e,t),resolveChainedSuccessor:e=>this.resolveChainedSuccessor(e),show:()=>this.show(),reapplyAdContainerLayout:e=>this.reapplyAdContainerLayout(e),getCurrentBreak:()=>this.currentBreak,onBreakVisible:e=>this.callbacks?.onBreakVisible?.(e),onAdError:e=>this.onAdError(e),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.presentationModes=new C(this.state,{contentPlayer:this.contentPlayer,getAdPlayer:()=>this.adPlayer,insertionMode:this.insertionMode,getCurrentBreak:()=>this.currentBreak,getCurrentFormat:()=>this.currentSelectedFormat,isSharedElementActive:()=>this.state.sharedAdClaimActive,getVariant:e=>this.getVariant(e),isAdPlaying:()=>this.isAdPlaying(),hasCurrentAsset:()=>null!==this.currentAsset,contentSourceIsReloadable:()=>null!==this.sharedElementPlayer.restorableContentSource(),adoptPictureInPictureMidBreak:()=>this.adoptPictureInPictureMidBreak(),restoreLayout:e=>this.restoreLayout(e),applyLayout:e=>this.applyLayout(e),show:()=>this.show(),playAdThroughContentElement:()=>this.sharedElementPlayer.playAdThroughContentElement(),getContentPositionAtBreakStart:()=>this.state.contentPositionAtBreakStart,setContentPositionAtBreakStart:e=>{this.state.contentPositionAtBreakStart=e},gateContentForBreak:()=>this.gateContentForBreak(),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.resumeCoordinator=new f(this.state,{contentPlayer:this.contentPlayer,clock:this.clock,getInsertionMode:()=>this.effectiveInsertionMode(),gateContentForBreak:()=>this.gateContentForBreak(),clearContentGate:e=>this.clearContentGate(e),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.recoveryDeps={contentPlayer:e,getCallbacks:()=>this.callbacks,preloadMode:d,contentRecovery:M,resumeCoordinator:this.resumeCoordinator,diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)},this.sharedElementPlayer=new k(this.state,{contentPlayer:this.contentPlayer,getAdPlayer:()=>this.adPlayer,resolveAssetUri:(e,t)=>this.resolveAssetUri(e,t),liveEdgeDistanceSec:()=>this.resumeCoordinator.liveEdgeDistanceSec(),rebindContentElementIfSwapped:()=>this.rebindContentElementIfSwapped(),createAdAdapter:(e,t)=>this.createAdAdapter(e,t),reportAdProgress:e=>this.reportAdProgress(e),processTimedMetadata:e=>this.gamPodBridge.processTimedMetadata(e),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e),clearContentGate:()=>this.clearContentGate(),hide:()=>this.hide(),waitForLiveEdge:e=>this.resumeCoordinator.waitForLiveEdge(e),getVariant:e=>this.getVariant(e),getAssets:e=>this.getAssets(e)}),this.variantSelector=new E(this.state,{getEffectiveInsertionMode:()=>this.effectiveInsertionMode(),singleSurfaceDevice:this.singleSurfaceDevice,preloadMode:this.preloadMode,getIsContentPictureInPicture:()=>this.isContentPictureInPicture(),getIsGamReady:()=>this.isGamReady(),getSupportsParallelBuffering:()=>this.contentPlayer.supportsParallelBuffering,diagnose:(e,t,r)=>this.diagnose(e,t,r)}),this.assetPlayback=new B(this.state,{getAdPlayer:()=>this.adPlayer,getAdContainer:()=>this.adContainer,getAdMediaContainer:()=>this.adMediaContainer,clearStaticQuartiles:()=>this.staticQuartilesFired.clear(),getVariant:e=>this.getVariant(e),getGamEventCallbacks:()=>this.gamEventCallbacks,getSequencer:()=>this.sequencer,debug:this.debug,resolveUri:e=>this.resolveUri(e),buildGamUrl:(e,t)=>this.buildGamUrl(e,t),buildVastTagUrl:(e,t)=>this.buildVastTagUrl(e,t),onAdError:e=>this.onAdError(e),onAdEnded:()=>this.onAdEnded(),drive:e=>this.drive(e),clearOverlayImage:()=>this.clearOverlayImage(),revealFirstFrame:()=>this.preloader.revealFirstFrame(),transferPictureInPictureToAd:()=>this.presentationModes.transferPictureInPictureToAd(),isGamPodAsset:e=>this.isGamPodAsset(e),beginGamPodTracking:e=>this.gamPodBridge.beginGamPodTracking(e),recordAdProgress:e=>this.adPlayerEvents.recordAdProgress(e),reportPhase:e=>this.reportPhase(e),armAdStartBudget:e=>this.armAdStartBudget(e),remainingAdStartBudgetMs:()=>this.remainingAdStartBudgetMs(),loadWithinBudget:e=>this.loadWithinBudget(e),waitForFirstFrame:()=>this.waitForFirstFrame(),getOverlayImage:()=>this.overlayImageEl,setOverlayImage:e=>{this.overlayImageEl=e},getImageAssetTimer:()=>this.imageAssetTimer,setImageAssetTimer:e=>{this.imageAssetTimer=e},getVastPodPosition:()=>this.vastPodPosition,setVastPodPosition:e=>{this.vastPodPosition=e},diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.breakSequenceDriver=new b(this.state,{getChainResolver:()=>this.chainResolver,getEffectiveInsertionMode:()=>this.effectiveInsertionMode(),getVastPodPosition:()=>this.vastPodPosition,setVastPodPosition:e=>{this.vastPodPosition=e},getPendingAdError:()=>this.pendingAdError,setPendingAdError:e=>{this.pendingAdError=e},getChaining:()=>this.chaining,setChaining:e=>{this.chaining=e},isExpandingPodAsset:e=>this.isExpandingPodAsset(e),fallBackToPodLevelReporting:()=>this.fallBackToPodLevelReporting(),resetGamPodTracking:()=>this.resetGamPodTracking(),emitGamPodSteps:e=>this.gamPodBridge.emitGamPodSteps(e),playAsset:(e,t,r)=>this.playAsset(e,t,r),endBreak:()=>this.endBreak(),playBreak:(e,t,r)=>this.playBreak(e,t,r),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.adPlayerEvents=new F(this.state,{getAdPlayer:()=>this.adPlayer,getContentPlayer:()=>this.contentPlayer,getGamEventCallbacks:()=>this.gamEventCallbacks,getSequencer:()=>this.sequencer,getAudioFocus:()=>this.audioFocus,getSyncing:()=>this.syncing,setSyncing:e=>{this.syncing=e},setAdVolumeChangeHandler:e=>{this.adVolumeChangeHandler=e},setContentVolumeHandler:e=>{this.contentVolumeHandler=e},notifyVolumeChange:()=>this.notifyVolumeChange(),onAdEnded:()=>this.onAdEnded(),processTimedMetadata:e=>this.gamPodBridge.processTimedMetadata(e),toAdError:e=>this.toAdError(e),revealFirstFrame:()=>this.revealFirstFrame(),clearFirstFrameResolver:()=>this.preloader.clearFirstFrameResolver(),drive:e=>this.drive(e),setPendingAdError:e=>{this.pendingAdError=e},log:(...e)=>this.log(...e),maybePrepositionContentForResume:()=>"single-decoder"!==this.preloadMode&&this.resumeCoordinator.maybePrepositionContentForResume(),maybeCatchUpContentForResume:e=>"single-decoder"!==this.preloadMode&&this.resumeCoordinator.maybeCatchUpContentForResume(e),getStaticQuartilesFired:()=>this.staticQuartilesFired,onGamPodAdProgress:e=>this.gamPodBridge.onGamPodAdProgress(e)})}setGamEventCallbacks(e){this.gamPodBridge.setEventCallbacks(e)}setDiagnoseHandler(e){this.diagnoseHandler=e}diagnose(e,t,r){this.diagnoseHandler?.(e,t,r)}holdContentForPreRoll(e){if(this.preRollHoldActive)return;const t=this.getVariant(e);t&&this.shouldPauseContent(t.format)&&(this.preRollHoldActive=!0,this.contentPlayer.pause(),this.preRollHoldHandler=()=>{this.preRollHoldActive&&this.contentPlayer.pause()},this.contentPlayer.on("playing",this.preRollHoldHandler),this.diagnose("DA-PREROLL-CONTENT-HELD","Content held at session start until the pending pre-roll begins.",{breakId:e.id}))}releaseContentHold(){this.preRollHoldActive&&(this.preRollHoldActive=!1,this.preRollHoldHandler&&(this.contentPlayer.off("playing",this.preRollHoldHandler),this.preRollHoldHandler=null))}gateContentForBreak(){this.contentPlayer.pause(),this.breakContentGateHandler||(this.breakContentGateHandler=()=>{this.contentPaused&&!this.state.resumeCatchupActive&&this.contentPlayer.pause()},this.contentPlayer.on("playing",this.breakContentGateHandler))}clearContentGate(e=!1){this.resumeCoordinator.endResumeCatchup(e),this.breakContentGateHandler&&(this.contentPlayer.off("playing",this.breakContentGateHandler),this.breakContentGateHandler=null)}setChainResolver(e){this.chainResolver=e}setTransitionPhaseReporter(e){this.transitionPhaseReporter=e}reportPhase(e){this.transitionPhaseReporter?.(e)}setBreakResumePolicy(e,t,r=!1){this.adInsertionType=e,this.breakTimebase=t,this.state.continueContentDuringBreak=r,this.state.continueRefusalReported=!1}initialize(){this.adContainer||(this.setupOverlay(),this.presentationModes.setupPictureInPictureTracking(),"adaptive"===this.insertionMode&&this.presentationModes.setupFullscreenTracking())}async startGamSession(e,t){await this.gamPodBridge.startGamSession(e,t)}endGamSession(){this.gamPodBridge.endGamSession()}setSessionAssetParameters(e){this.assetParameters.setSessionAssetParameters(e)}setManifestAssetParameters(e){this.assetParameters.setManifestAssetParameters(e)}updateAssetParameters(e){this.assetParameters.updateAssetParameters(e)}updateAssetParameterMacros(e){this.assetParameters.updateAssetParameterMacros(e)}get updatedAssetParameters(){return this.state.updatedAssetParameters}setSessionAssetParameterMacros(e){this.assetParameters.setSessionAssetParameterMacros(e)}get sessionAssetParameterMacros(){return this.assetParameters.getSessionAssetParameterMacros()}buildVastTagUrl(e,t){return this.assetParameters.buildVastTagUrl(e,t)}pushAssetParameters(e,t){this.assetParameters.pushAssetParameters(e,t)}setupOverlay(){this.adStage.setupOverlay()}isGamPodAsset(e){return this.gamPodBridge.isGamPodAsset(e)}isExpandingPodAsset(e){return this.gamPodBridge.isExpandingPodAsset(e)}fallBackToPodLevelReporting(){this.gamPodBridge.fallBackToPodLevelReporting()}resetGamPodTracking(){this.gamPodBridge.resetGamPodTracking()}emitGamPodSteps(e){this.gamPodBridge.emitGamPodSteps(e)}isGamEnabled(){return this.gamPodBridge.isGamEnabled()}isGamReady(){return this.gamPodBridge.isGamReady()}isOverlayInsertion(){return"overlay"===this.effectiveInsertionMode()}settleAssetProgress(){this.adPlayerEvents.settleAssetProgress()}reportAdProgress(e){this.adPlayerEvents.reportAdProgress(e)}setupEventListeners(){this.adPlayerEvents.setupEventListeners()}async preload(e){return this.preloader.preload(e)}renderLshapeContentBackdrop(e){this.adStage.renderLshapeContentBackdrop(e)}currentInsertionMode(){return this.presentationModes.currentInsertionMode()}effectiveInsertionMode(){return this.presentationModes.effectiveInsertionMode()}isContentPictureInPicture(){return this.presentationModes.isPictureInPicture()}rebindContentElementIfSwapped(){this.presentationModes.rebindContentElementIfSwapped()}teardownFullscreenTracking(){this.presentationModes.teardownFullscreenTracking()}teardownPictureInPictureTracking(){this.presentationModes.teardownPictureInPictureTracking()}isPictureInPicture(){return this.presentationModes.isPictureInPicture()}pictureInPictureOwner(){return this.presentationModes.pictureInPictureOwner()}isShowingAd(){if(!this.isAdPlaying())return!1;const e=this.currentDeclaredFormat();return!n(e,this.isContentPictureInPicture())}currentDeclaredFormat(){return this.currentSelectedFormat??(this.currentBreak?l(this.currentBreak):void 0)}async playBreak(t,s,a){if(this.state.preloadGeneration++,this.resumeGeneration++,!this.sharedElementPlayer.beginSharedActivity())return;this.releaseContentHold(),this.rebindContentElementIfSwapped();const n=a?.effectiveDurationSec??null;this.effectiveBreakDurationSec=null!==n&&n>0?n:null;const d=this.effectiveBreakDurationSec??t.duration;if("shared-element"===this.effectiveInsertionMode())return g(this.state),void await this.sharedElementPlayer.playSharedElementBreak(t,s);if(!this.adPlayer)return void this.log("playBreak called before initialize()");const l=this.getVariant(t);if(!l)return void this.log("No valid variant found for break:",t.id);this.currentBreak=t,this.callbacks=s,this.isPlaying=!0,this.firstFrameGateActive=a?.firstFrameGate??!1,this.firstFrameGateShown=!1;const h=this.continueContentDuringBreakReason(l.format),u=r(t),c=this.shouldPauseContent(l.format)&&"ok"===h&&!u;c||u||this.state.continueRefusalReported||!this.shouldPauseContent(l.format)||(this.state.continueRefusalReported=!0,this.diagnose("DA-CONTINUE-PLAYOUT-REFUSED",`Content will be paused for breaks this session: ${h}`,{reason:h,format:l.format})),this.audioFocus=l.format===e.DOUBLE?this.doubleBoxAudio:c?"ad":null,this.adPlayer&&("content"===this.audioFocus?(this.adPlayer.muted=!0,this.adPlayer.volume=0):(this.adPlayer.muted=this.contentPlayer.muted,this.adPlayer.volume=this.contentPlayer.volume,"ad"===this.audioFocus&&(this.contentPlayer.muted=!0)));const m=this.declaredFormatOf(t);if(this.currentSelectedFormat=m,this.state.pipAdoptedForBreak=i(m,this.isContentPictureInPicture()),this.state.pipAdoptedForBreak&&this.diagnose("DA-PIP-FORMAT-OVERRIDE",`Picture-in-picture: ${m} break plays as single`,{declared:m,effective:e.SINGLE,reason:"picture-in-picture",midBreak:!1}),this.shouldPauseContent(l.format)?(this.state.contentPositionAtBreakStart=this.contentPlayer.currentTime,this.resumeCoordinator.resetBreakResumeState(),c?(this.contentPaused=!1,this.resumeCoordinator.beginContinueContentPlayout()):(this.contentPaused=!0,this.gateContentForBreak())):this.contentPaused=!1,this.applyLayout(l),l.format===e.LSHAPE_CONTENT)return this.renderLshapeContentBackdrop(l),void(d&&d>0?(this.log("Starting duration timer:",d,"seconds"),this.durationTimer=setTimeout(()=>{this.log("Duration timer expired, ending break"),this.endBreak()},1e3*d)):this.endBreak());this.firstFrameGateActive||(this.show(),this.reapplyAdContainerLayout(l));if(0===this.getAssets(t).length)return this.log("No assets in break, ending immediately"),void this.endBreak();{const e=Math.max(0,d||0)+this.adBreakCutSafetyMarginSec;T(this.state,e,()=>{this.log("Break-cut timer expired, ending break"),this.firstFrameRejector?this.onAdError(new Error("Break cut before first frame rendered")):this.sequencer&&this.currentBreak&&this.isPlaying?(this.callbacks?.onBreakCut?.(this.currentBreak),this.drive(this.sequencer.onOutcome({kind:"break-elapsed"}))):this.endBreak()},()=>this.onAdError(new Error("Ad playback startup timed out")))}this.sequencer=new o("overlay",()=>null,this.selectionInputs()),await this.drive(this.sequencer.beginBreak(t)),this.preloadChainedSuccessor(t.id)}get resumeHandoverInMotion(){return this.state.resumeCatchupActive}reapplyAdContainerLayout(e){this.adStage.reapplyAdContainerLayout(e)}adoptPictureInPictureMidBreak(){this.presentationModes.adoptPictureInPictureMidBreak()}async restorePictureInPictureToContent(){await this.presentationModes.restorePictureInPictureToContent()}continueContentDuringBreakReason(e){return this.variantSelector.continueContentDuringBreakReason(e)}shouldPauseContent(t){return t===e.SINGLE||t===e.LSHAPE_AD}selectionInputs(){return this.variantSelector.selectionInputs()}breakNothingToPlayReason(e){return this.variantSelector.breakNothingToPlayReason(e)}unknownVariantFormatsOf(e){return this.variantSelector.unknownVariantFormatsOf(e)}diagnoseUnknownVariantFormats(e){this.variantSelector.diagnoseUnknownVariantFormats(e)}declaredFormatOf(e){const{selected:t,deviceQualified:r}=h(e,this.selectionInputs());return(t??r)?.format??l(e)}getVariant(e){return this.variantSelector.getVariant(e)}applyLayout(e){this.adStage.applyLayout(e)}restoreLayout(e){this.adStage.restoreLayout(e)}getAssets(e){return d(this.getVariant(e))}async playAsset(e,t,r){return this.assetPlayback.playAsset(e,t,r)}clearOverlayImage(){this.adStage.clearOverlayImage()}async showPauseImage(e){return this.pauseAdPresenter.showPauseImage(e)}hidePauseImage(){this.pauseAdPresenter.hidePauseImage()}async drive(e){return this.breakSequenceDriver.drive(e)}emitStep(e){this.breakSequenceDriver.emitStep(e)}async resolveAssetUri(e,t){return this.assetPlayback.resolveAssetUri(e,t)}async buildGamUrl(e,t){return this.gamPodBridge.buildGamUrl(e,t)}resolveUri(e){return this.assetParameters.resolveUri(e)}adPlayerStreamRemainingSec(){return this.assetPlayback.adPlayerStreamRemainingSec()}onAdEnded(){this.currentBreak&&this.isPlaying&&this.sequencer&&(this.settleAssetProgress(),this.drive(this.sequencer.onOutcome({kind:"asset-ended"})))}toAdError(e){return this.assetPlayback.toAdError(e)}onAdError(e){this.adPlayerEvents.onAdError(e)}endBreak(){if(!this.currentBreak)return;this.log("Break ended:",this.currentBreak.id),M(this.state),this.state.gamPodTracker&&this.emitGamPodSteps(this.state.gamPodTracker.finish()),this.resetGamPodTracking(),this.pushAssetParameters(void 0);const e=this.currentBreak,s=this.getVariant(e)?.format;let a=this.abortingBreak?null:this.resolveChainedSuccessor(e.id);if(a){const e=this.breakNothingToPlayReason(a);this.diagnoseUnknownVariantFormats(a),e&&(this.diagnose("DA-BREAK-NO-PLAYABLE-ASSET",`chained break ${a.id} has no playable variant (${e}); it will not begin.`,{breakId:a.id,reason:e}),this.callbacks?.onAdError(a,void 0,new Error(`Break ${a.id} has no playable variant (${e})`)),this.callbacks?.onChainedBreakSkipped?.(a),a=null)}if(a)return void this.advanceToChainedBreak(e,a);if(this.currentSelectedFormat=void 0,this.resumeCoordinator.endContinueContentPlayout(),this.restorePictureInPictureToContent(),this.isPlaying=!1,this.currentBreak=null,this.resumeGeneration++,this.effectiveBreakDurationSec=null,g(this.state),this.preloader.clearWarmedBreaks(),this.firstFrameGateActive=!1,this.firstFrameGateShown=!1,this.clearFirstFrame("Break ended before first frame"),"ad"===this.audioFocus&&this.adPlayer?(this.contentPlayer.muted=this.adPlayer.muted,this.contentPlayer.volume=this.adPlayer.volume):"content"===this.audioFocus&&this.adPlayer&&(this.adPlayer.muted=this.contentPlayer.muted,this.adPlayer.volume=this.contentPlayer.volume),this.audioFocus=null,this.clearOverlayImage(),this.vastAdManager?.reset(),this.hide(),this.restoreLayout(s),this.adPlayer?.pause(),this.state.sharedAdClaimActive){const t=this.callbacks;return this.sharedElementPlayer.detachSharedAdListeners(),this.abortingBreak=!1,this.state.earlyReturnResumeOffsetSec=null,this.sharedElementPlayer.restoreSharedElementContent(e),void t?.onBreakEnd(e)}const i="recreate"===this.recoveryDeps.contentRecovery&&"function"==typeof this.contentPlayer.recreateMediaPipeline,n="single-decoder"===this.preloadMode||i?this.adPlayer?.unload?.():void 0;if(this.contentPaused&&r(e))this.contentPaused=!1,this.clearContentGate();else if(this.contentPaused){this.contentPaused=!1,this.clearContentGate(this.resumeHandoverInMotion);const r=this.resumeGeneration;S(this.recoveryDeps,{breakInfo:e,earlyReturnOffsetSec:this.state.earlyReturnResumeOffsetSec,isLiveResume:this.breakTimebase===t.WALLCLOCK,released:n,isCurrent:()=>r===this.resumeGeneration})}this.abortingBreak=!1,this.state.earlyReturnResumeOffsetSec=null,this.callbacks?.onBreakEnd(e)}currentAdAsset(){return this.isPlaying?this.currentAsset:null}skipBreak(){this.isPlaying&&(this.log("Skipping break:",this.currentBreak?.id),M(this.state),this.sequencer&&this.currentBreak?this.drive(this.sequencer.onOutcome({kind:"break-elapsed"})):this.endBreak())}abortBreak(e){this.isPlaying&&(this.log("Early return — break removed from manifest:",this.currentBreak?.id),this.state.earlyReturnResumeOffsetSec=Math.max(0,e),this.abortingBreak=!0,M(this.state),this.sequencer&&this.currentBreak?this.drive(this.sequencer.onOutcome({kind:"break-elapsed"})):this.endBreak())}stopActiveBreak(){g(this.state),this.sharedElementPlayer.cancelSharedRestore()&&this.isPlaying&&(this.log("Aborting active break on session end:",this.currentBreak?.id),M(this.state),this.sharedElementPlayer.detachSharedAdListeners(),this.sharedElementPlayer.releaseContentElementAdPlayer(),this.adPlayer?.pause(),this.clearOverlayImage(),this.vastAdManager?.reset(),this.hide(),this.restoreLayout(this.currentBreak?this.getVariant(this.currentBreak)?.format??void 0:void 0),this.isPlaying=!1,this.currentBreak=null,this.currentAsset=null,this.contentPaused=!1,this.clearContentGate(),this.resumeCoordinator.endContinueContentPlayout(),this.effectiveBreakDurationSec=null,this.firstFrameGateActive=!1,this.firstFrameGateShown=!1,this.clearFirstFrame("Break aborted before first frame"),this.audioFocus=null)}resolveChainedSuccessor(e){return this.breakSequenceDriver.resolveChainedSuccessor(e)}advanceToChainedBreak(e,t){this.breakSequenceDriver.advanceToChainedBreak(e,t)}preloadChainedSuccessor(e){this.preloader.preloadChainedSuccessor(e)}revealFirstFrame(){this.assetPlayback.revealFirstFrame()}armAdStartBudget(e){this.preloader.armAdStartBudget(e)}remainingAdStartBudgetMs(){return this.preloader.remainingAdStartBudgetMs()}loadWithinBudget(e){return this.preloader.loadWithinBudget(e)}async waitForFirstFrame(){return this.preloader.waitForFirstFrame()}clearFirstFrame(e="Break ended before first frame"){this.preloader.clearFirstFrame(e)}show(){this.adStage.show()}hide(){this.adStage.hide()}isAdPlaying(){return this.isPlaying}get muted(){return"content"===this.audioFocus?this.contentPlayer.muted:"ad"===this.audioFocus?this.adPlayer?this.adPlayer.muted:this.contentPlayer.muted:this.isPlaying&&this.adPlayer?this.adPlayer.muted:this.contentPlayer.muted}set muted(e){if("content"===this.audioFocus)this.contentPlayer.muted=e;else if("ad"===this.audioFocus)this.adPlayer&&(this.adPlayer.muted=e);else{(this.isPlaying&&this.adPlayer?this.adPlayer:this.contentPlayer).muted=e}this.vastAdManager?.setMuted(e),this.notifyVolumeChange()}get volume(){return"content"===this.audioFocus?this.contentPlayer.volume:"ad"===this.audioFocus?this.adPlayer?this.adPlayer.volume:this.contentPlayer.volume:this.isPlaying&&this.adPlayer?this.adPlayer.volume:this.contentPlayer.volume}set volume(e){if("content"===this.audioFocus)this.contentPlayer.volume=e;else if("ad"===this.audioFocus)this.adPlayer&&(this.adPlayer.volume=e);else{(this.isPlaying&&this.adPlayer?this.adPlayer:this.contentPlayer).volume=e}this.vastAdManager?.setVolume(e),this.notifyVolumeChange()}setVolumeChangeCallback(e){this.volumeChangeCallback=e}notifyVolumeChange(){this.volumeChangeCallback?.(this.muted,this.volume)}destroy(){if(this.resumeGeneration++,g(this.state),this.sharedElementPlayer.beginSharedActivity()){if(this.adStage.destroy(),this.resumeCoordinator.cancelResumeLandedProbe(),this.resumeCoordinator.endResumeCatchup(),this.resumeCoordinator.endContinueContentPlayout(),this.state.resumePrepositionSettle&&(clearTimeout(this.state.resumePrepositionSettle),this.state.resumePrepositionSettle=null),this.releaseContentHold(),this.clearContentGate(),this.clearFirstFrame("Controller destroyed before first frame"),this.firstFrameGateActive=!1,this.firstFrameGateShown=!1,this.resetGamPodTracking(),M(this.state),this.companionRemoveTimer&&(clearTimeout(this.companionRemoveTimer),this.companionRemoveTimer=null),this.adFadeTimer&&(clearTimeout(this.adFadeTimer),this.adFadeTimer=null),this.adPlayer?.pause(),this.clearOverlayImage(),this.pauseAdPresenter.destroy(),this.contentVolumeHandler&&(this.contentPlayer.off("volumechange",this.contentVolumeHandler),this.contentVolumeHandler=null),this.sharedElementPlayer.detachSharedAdListeners(),this.sharedElementPlayer.releaseContentElementAdPlayer(),this.state.pipTransferredToAd=!1,this.teardownFullscreenTracking(),this.teardownPictureInPictureTracking(),this.adVolumeChangeHandler&&this.adPlayer&&this.adPlayer.off("volumechange",this.adVolumeChangeHandler),this.adVolumeChangeHandler=null,this.hide(),this.resizeObserver?.disconnect(),this.resizeObserver=null,this.contentVideoMutationObserver?.disconnect(),this.contentVideoMutationObserver=null,this.contentVideoElement&&this.contentVideoGeometryHandler&&(this.contentVideoElement.removeEventListener("loadedmetadata",this.contentVideoGeometryHandler),this.contentVideoElement.removeEventListener("resize",this.contentVideoGeometryHandler)),this.contentVideoElement=null,this.contentVideoGeometryHandler=null,this.playerContainerCreatedBySDK)for(;this.playerContainer.firstChild;)this.container.appendChild(this.playerContainer.firstChild);else this.container.appendChild(this.playerContainer),this.playerContainer.style.cssText=this.playerContainerOriginalStyle;this.companionElement&&this.companionElement.parentElement&&(this.companionElement.parentElement.removeChild(this.companionElement),this.companionElement=null),this.isPlaying=!1,this.currentBreak=null,this.adContainer&&this.adContainer.parentElement&&this.adContainer.parentElement.removeChild(this.adContainer),this.stage&&this.stage.parentElement&&this.stage.parentElement.removeChild(this.stage),this.stage=null,this.adContainer=null,this.adMediaContainer=null;try{this.adPlayer?.destroy()}catch(e){this.log("Ad player adapter destroy failed (ignored):",e)}this.adPlayer=null,this.callbacks=null,this.gamPodBridge.destroy(),this.vastAdManager?.destroy(),this.vastAdManager=null,this.log("Ad player destroyed")}}log(...e){this.debug&&u("[OptiViewAds:AdPlayerController]",...e)}}AdPlayerController.AD_START_BUDGET_MS=5e3,AdPlayerController.GAM_POD_AD_START_BUDGET_MS=1e4,AdPlayerController.MIN_AD_START_BUDGET_MS=3e3;
|
|
1
|
+
import{BreakFormat as e,Timebase as t,isPostRollBreak as r}from"../types/BreakManifest";import{DEFAULT_AD_BREAK_CUT_SAFETY_MARGIN_SEC as s,DEFAULT_DOUBLE_BOX_AUDIO as a}from"../types/OptiViewAdsConfig";import{pipOverridesFormat as i,pipSuppressesBackdrop as n}from"./pictureInPicture";import{AdBreakSequencer as o,assetsOfVariant as d,resolveBreakFormat as l}from"./AdBreakSequencer";import{selectVariantDetailed as h}from"./selectVariant";import{debugLog as u}from"../diagnostics/debugLog";import{systemClock as c}from"./BreakScheduler";import{createAdPlayerState as m,invalidateMediaPreload as g}from"./adPlayer/AdPlayerState";import{PauseAdPresenter as P}from"./adPlayer/PauseAdPresenter";import{AssetParameterResolver as y}from"./adPlayer/AssetParameterResolver";import{GamPodBridge as p}from"./adPlayer/GamPodBridge";import{AdStage as A}from"./adPlayer/AdStage";import{AdPreloader as v}from"./adPlayer/AdPreloader";import{PresentationModeTracker as C}from"./adPlayer/PresentationModeTracker";import{SharedElementBreakPlayer as k}from"./adPlayer/SharedElementBreakPlayer";import{ContentResumeCoordinator as f}from"./adPlayer/ContentResumeCoordinator";import{resumeContentAfterBreak as S}from"./adPlayer/ContentRecovery";import{AssetPlayback as B}from"./adPlayer/AssetPlayback";import{BreakSequenceDriver as E}from"./adPlayer/BreakSequenceDriver";import{VariantSelector as b}from"./adPlayer/VariantSelector";import{AdPlayerEvents as F}from"./adPlayer/AdPlayerEvents";import{armPlaybackCutoff as T,clearPlaybackCutoff as M}from"./adPlayer/PlaybackCutoff";import{AdError as R}from"../types/AdError";export class AdPlayerController{get contentPaused(){return this.state.contentPaused}set contentPaused(e){this.state.contentPaused=e}get firstFrameGateActive(){return this.state.firstFrameGateActive}set firstFrameGateActive(e){this.state.firstFrameGateActive=e}get firstFrameGateShown(){return this.state.firstFrameGateShown}set firstFrameGateShown(e){this.state.firstFrameGateShown=e}get firstFrameRejector(){return this.state.firstFrameRejector}set firstFrameRejector(e){this.state.firstFrameRejector=e}get gamEventCallbacks(){return this.gamPodBridge.eventCallbacks}get vastAdManager(){return this.state.vastAdManager}set vastAdManager(e){this.state.vastAdManager=e}get currentBreak(){return this.state.currentBreak}set currentBreak(e){this.state.currentBreak=e}get currentAsset(){return this.state.currentAsset}set currentAsset(e){this.state.currentAsset=e}get callbacks(){return this.state.callbacks}set callbacks(e){this.state.callbacks=e}get isPlaying(){return this.state.isPlaying}set isPlaying(e){this.state.isPlaying=e}get effectiveBreakDurationSec(){return this.state.effectiveBreakDurationSec}set effectiveBreakDurationSec(e){this.state.effectiveBreakDurationSec=e}get adBreakCutSafetyMarginSec(){return this.state.adBreakCutSafetyMarginSec}set adBreakCutSafetyMarginSec(e){this.state.adBreakCutSafetyMarginSec=e}get durationTimer(){return this.state.durationTimer}set durationTimer(e){this.state.durationTimer=e}get adInsertionType(){return this.state.adInsertionType}set adInsertionType(e){this.state.adInsertionType=e}get breakTimebase(){return this.state.breakTimebase}set breakTimebase(e){this.state.breakTimebase=e}constructor(e,t,r,i,n=!1,o,d="parallel",l="overlay",h=s,u=a,S=!1,T=c,M="none"){this.playerContainerCreatedBySDK=!1,this.adMediaContainer=null,this.adPlayer=null,this.adContainer=null,this.companionElement=null,this.staticQuartilesFired=new Set,this.sequencer=null,this.pendingAdError=null,this.preRollHoldActive=!1,this.preRollHoldHandler=null,this.breakContentGateHandler=null,this.destroyed=!1,this.abortingBreak=!1,this.overlayImageEl=null,this.imageAssetTimer=null,this.chainResolver=null,this.transitionPhaseReporter=null,this.chaining=!1,this.diagnoseHandler=null,this.playerContainerOriginalStyle="",this.companionRemoveTimer=null,this.adFadeTimer=null,this.syncing=!1,this.contentVolumeHandler=null,this.adVolumeChangeHandler=null,this.volumeChangeCallback=null,this.stage=null,this.resizeObserver=null,this.contentVideoElement=null,this.contentVideoGeometryHandler=null,this.contentVideoMutationObserver=null,this.resumeGeneration=0,this.singleSurfaceDevice=!1,this.currentSelectedFormat=void 0,this.audioFocus=null,this.state=m(),this.state.playerContainer=r??null,this.clock=T,this.singleSurfaceDevice=S,this.contentPlayer=e,this.adBreakCutSafetyMarginSec=h,this.doubleBoxAudio=u,this.container=t,r&&(this.playerContainer=r),this.createAdAdapter=i,this.debug=n,this.state.gamConfig=o,this.preloadMode=d,this.insertionMode=l,this.pauseAdPresenter=new P({contentPlayer:this.contentPlayer,getStage:()=>this.stage,resolveUri:e=>this.resolveUri(e),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.assetParameters=new y(this.state,{contentPlayer:this.contentPlayer,container:this.container,getGamStreamManager:()=>this.state.gamStreamManager,getCurrentBreak:()=>this.currentBreak,getEffectiveBreakDuration:()=>this.effectiveBreakDurationSec,diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.gamPodBridge=new p(this.state,{contentPlayer:this.contentPlayer,debug:this.debug,getAdPlayer:()=>this.adPlayer,getAdContainer:()=>this.adContainer,isSharedElement:()=>"shared-element"===this.effectiveInsertionMode(),getCurrentBreak:()=>this.currentBreak,getCurrentAsset:()=>this.currentAsset,getEffectiveBreakDuration:()=>this.effectiveBreakDurationSec,getAdPlayerStreamRemainingSec:()=>this.adPlayerStreamRemainingSec(),emitStep:e=>this.emitStep(e),emitAdStart:(e,t,r,s,a)=>this.callbacks?.onAdStart(e,t,r,s,a),emitAdEnd:(e,t,r,s,a)=>this.callbacks?.onAdEnd(e,t,r,s,a),onAdEnded:()=>this.onAdEnded(),pushAssetParameters:(e,t)=>this.pushAssetParameters(e,t),resolveSessionAdTagParameters:()=>this.assetParameters.resolveSessionAdTagParameters(),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.adStage=new A({contentPlayer:this.contentPlayer,container:this.container,getPlayerContainer:()=>this.playerContainer,setPlayerContainer:e=>{this.playerContainer=e},setPlayerContainerOriginalStyle:e=>{this.playerContainerOriginalStyle=e},onPlayerContainerCreated:()=>{this.playerContainerCreatedBySDK=!0},getStage:()=>this.stage,setStage:e=>{this.stage=e},getContentVideo:()=>this.contentVideoElement,setContentVideo:e=>{this.contentVideoElement=e},getAdContainer:()=>this.adContainer,setAdContainer:e=>{this.adContainer=e},getAdMediaContainer:()=>this.adMediaContainer,setAdMediaContainer:e=>{this.adMediaContainer=e},setAdPlayer:e=>{g(this.state),this.adPlayer=e},createAdAdapter:e=>this.createAdAdapter(e),setupEventListeners:()=>this.setupEventListeners(),resizeVast:()=>this.vastAdManager?.resize(),getVariant:e=>this.getVariant(e),getCurrentBreak:()=>this.currentBreak,resolveUri:e=>this.resolveUri(e),getCompanion:()=>this.companionElement,setCompanion:e=>{this.companionElement=e},getOverlayImage:()=>this.overlayImageEl,setOverlayImage:e=>{this.overlayImageEl=e},getImageAssetTimer:()=>this.imageAssetTimer,setImageAssetTimer:e=>{this.imageAssetTimer=e},isChaining:()=>this.chaining,log:(...e)=>this.log(...e)}),this.preloader=new v({state:this.state,clock:this.clock,adStartBudgetMs:AdPlayerController.AD_START_BUDGET_MS,gamPodAdStartBudgetMs:AdPlayerController.GAM_POD_AD_START_BUDGET_MS,minAdStartBudgetMs:AdPlayerController.MIN_AD_START_BUDGET_MS,debug:this.debug,isPlaying:()=>this.isPlaying,preloadMode:this.preloadMode,effectiveInsertionMode:()=>this.effectiveInsertionMode(),getVariant:e=>this.getVariant(e),getAssets:e=>this.getAssets(e),resolveUri:e=>this.resolveUri(e),resolveAssetUri:(e,t)=>this.resolveAssetUri(e,t),getCompanionVariant:e=>this.getVariant(e),getAdPlayer:()=>this.adPlayer,getAdContainer:()=>this.adContainer,buildVastTagUrl:(e,t)=>this.buildVastTagUrl(e,t),resolveChainedSuccessor:e=>this.resolveChainedSuccessor(e),show:()=>this.show(),reapplyAdContainerLayout:e=>this.reapplyAdContainerLayout(e),getCurrentBreak:()=>this.currentBreak,onBreakVisible:e=>this.callbacks?.onBreakVisible?.(e),onAdError:e=>this.onAdError(e),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.presentationModes=new C(this.state,{contentPlayer:this.contentPlayer,getAdPlayer:()=>this.adPlayer,insertionMode:this.insertionMode,getCurrentBreak:()=>this.currentBreak,getCurrentFormat:()=>this.currentSelectedFormat,isSharedElementActive:()=>this.state.sharedAdClaimActive,getVariant:e=>this.getVariant(e),isAdPlaying:()=>this.isAdPlaying(),hasCurrentAsset:()=>null!==this.currentAsset,contentSourceIsReloadable:()=>null!==this.sharedElementPlayer.restorableContentSource(),adoptPictureInPictureMidBreak:()=>this.adoptPictureInPictureMidBreak(),restoreLayout:e=>this.restoreLayout(e),applyLayout:e=>this.applyLayout(e),show:()=>this.show(),playAdThroughContentElement:()=>this.sharedElementPlayer.playAdThroughContentElement(),getContentPositionAtBreakStart:()=>this.state.contentPositionAtBreakStart,setContentPositionAtBreakStart:e=>this.resumeCoordinator.reanchorMidBreak(e),gateContentForBreak:()=>this.gateContentForBreak(),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.resumeCoordinator=new f(this.state,{contentPlayer:this.contentPlayer,clock:this.clock,getInsertionMode:()=>this.effectiveInsertionMode(),gateContentForBreak:()=>this.gateContentForBreak(),clearContentGate:e=>this.clearContentGate(e),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.recoveryDeps={contentPlayer:e,getCallbacks:()=>this.callbacks,preloadMode:d,contentRecovery:M,resumeCoordinator:this.resumeCoordinator,diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)},this.sharedElementPlayer=new k(this.state,{contentPlayer:this.contentPlayer,getAdPlayer:()=>this.adPlayer,resolveAssetUri:(e,t)=>this.resolveAssetUri(e,t),liveEdgeDistanceSec:()=>this.resumeCoordinator.liveEdgeDistanceSec(),rebindContentElementIfSwapped:()=>this.rebindContentElementIfSwapped(),createAdAdapter:(e,t)=>this.createAdAdapter(e,t),reportAdProgress:e=>this.reportAdProgress(e),processTimedMetadata:e=>this.gamPodBridge.processTimedMetadata(e),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e),clearContentGate:()=>this.clearContentGate(),hide:()=>this.hide(),waitForLiveEdge:e=>this.resumeCoordinator.waitForLiveEdge(e),getVariant:e=>this.getVariant(e),getAssets:e=>this.getAssets(e)}),this.variantSelector=new b(this.state,{getEffectiveInsertionMode:()=>this.effectiveInsertionMode(),singleSurfaceDevice:this.singleSurfaceDevice,preloadMode:this.preloadMode,getIsContentPictureInPicture:()=>this.isContentPictureInPicture(),getIsGamReady:()=>this.isGamReady(),getSupportsParallelBuffering:()=>this.contentPlayer.supportsParallelBuffering,diagnose:(e,t,r)=>this.diagnose(e,t,r)}),this.assetPlayback=new B(this.state,{getAdPlayer:()=>this.adPlayer,getAdContainer:()=>this.adContainer,getAdMediaContainer:()=>this.adMediaContainer,clearStaticQuartiles:()=>this.staticQuartilesFired.clear(),getVariant:e=>this.getVariant(e),getGamEventCallbacks:()=>this.gamEventCallbacks,getSequencer:()=>this.sequencer,debug:this.debug,resolveUri:e=>this.resolveUri(e),buildGamUrl:(e,t)=>this.buildGamUrl(e,t),buildVastTagUrl:(e,t)=>this.buildVastTagUrl(e,t),onAdError:e=>this.onAdError(e),onAdEnded:()=>this.onAdEnded(),drive:e=>this.drive(e),clearOverlayImage:()=>this.clearOverlayImage(),revealFirstFrame:()=>this.preloader.revealFirstFrame(),transferPictureInPictureToAd:()=>this.presentationModes.transferPictureInPictureToAd(),isGamPodAsset:e=>this.isGamPodAsset(e),hasGamIdentityMismatch:e=>this.gamPodBridge.hasIdentityMismatch(e),beginGamPodTracking:e=>this.gamPodBridge.beginGamPodTracking(e),recordAdProgress:e=>this.adPlayerEvents.recordAdProgress(e),reportPhase:e=>this.reportPhase(e),armAdStartBudget:e=>this.armAdStartBudget(e),remainingAdStartBudgetMs:()=>this.remainingAdStartBudgetMs(),loadWithinBudget:(e,t)=>this.loadWithinBudget(e,t),waitForFirstFrame:()=>this.waitForFirstFrame(),getOverlayImage:()=>this.overlayImageEl,setOverlayImage:e=>{this.overlayImageEl=e},getImageAssetTimer:()=>this.imageAssetTimer,setImageAssetTimer:e=>{this.imageAssetTimer=e},getVastPodPosition:()=>this.vastPodPosition,setVastPodPosition:e=>{this.vastPodPosition=e},diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.breakSequenceDriver=new E(this.state,{getChainResolver:()=>this.chainResolver,getEffectiveInsertionMode:()=>this.effectiveInsertionMode(),getVastPodPosition:()=>this.vastPodPosition,setVastPodPosition:e=>{this.vastPodPosition=e},getPendingAdError:()=>this.pendingAdError,setPendingAdError:e=>{this.pendingAdError=e},getChaining:()=>this.chaining,setChaining:e=>{this.chaining=e},isExpandingPodAsset:e=>this.isExpandingPodAsset(e),fallBackToPodLevelReporting:()=>this.fallBackToPodLevelReporting(),resetGamPodTracking:()=>this.resetGamPodTracking(),emitGamPodSteps:e=>this.gamPodBridge.emitGamPodSteps(e),playAsset:(e,t,r)=>this.playAsset(e,t,r),endBreak:()=>this.endBreak(),playBreak:(e,t,r)=>this.playBreak(e,t,r),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.adPlayerEvents=new F(this.state,{getAdPlayer:()=>this.adPlayer,getContentPlayer:()=>this.contentPlayer,getGamEventCallbacks:()=>this.gamEventCallbacks,getSequencer:()=>this.sequencer,getAudioFocus:()=>this.audioFocus,getSyncing:()=>this.syncing,setSyncing:e=>{this.syncing=e},setAdVolumeChangeHandler:e=>{this.adVolumeChangeHandler=e},setContentVolumeHandler:e=>{this.contentVolumeHandler=e},notifyVolumeChange:()=>this.notifyVolumeChange(),onAdEnded:()=>this.onAdEnded(),processTimedMetadata:e=>this.gamPodBridge.processTimedMetadata(e),toAdError:e=>this.toAdError(e),revealFirstFrame:()=>this.revealFirstFrame(),clearFirstFrameResolver:()=>this.preloader.clearFirstFrameResolver(),drive:e=>this.drive(e),setPendingAdError:e=>{this.pendingAdError=e},log:(...e)=>this.log(...e),maybePrepositionContentForResume:()=>"single-decoder"!==this.preloadMode&&this.resumeCoordinator.maybePrepositionContentForResume(),maybeCatchUpContentForResume:e=>"single-decoder"!==this.preloadMode&&this.resumeCoordinator.maybeCatchUpContentForResume(e),getStaticQuartilesFired:()=>this.staticQuartilesFired,onGamPodAdProgress:e=>this.gamPodBridge.onGamPodAdProgress(e)})}setGamEventCallbacks(e){this.gamPodBridge.setEventCallbacks(e)}setDiagnoseHandler(e){this.diagnoseHandler=e}diagnose(e,t,r){this.diagnoseHandler?.(e,t,r)}holdContentForPreRoll(e){if(this.preRollHoldActive)return;const t=this.getVariant(e);t&&this.shouldPauseContent(t.format)&&(this.preRollHoldActive=!0,this.contentPlayer.pause(),this.preRollHoldHandler=()=>{this.preRollHoldActive&&this.contentPlayer.pause()},this.contentPlayer.on("playing",this.preRollHoldHandler),this.diagnose("DA-PREROLL-CONTENT-HELD","Content held at session start until the pending pre-roll begins.",{breakId:e.id}))}releaseContentHold(){this.preRollHoldActive&&(this.preRollHoldActive=!1,this.preRollHoldHandler&&(this.contentPlayer.off("playing",this.preRollHoldHandler),this.preRollHoldHandler=null))}gateContentForBreak(){this.contentPlayer.pause(),this.breakContentGateHandler||(this.breakContentGateHandler=()=>{this.contentPaused&&!this.state.resumeCatchupActive&&this.contentPlayer.pause()},this.contentPlayer.on("playing",this.breakContentGateHandler))}clearContentGate(e=!1){this.resumeCoordinator.endResumeCatchup(e),this.breakContentGateHandler&&(this.contentPlayer.off("playing",this.breakContentGateHandler),this.breakContentGateHandler=null)}setChainResolver(e){this.chainResolver=e}setResumeCueAnchorResolver(e){this.resumeCoordinator.setCueAnchorResolver(e)}setResumeCueEndResolver(e){this.resumeCoordinator.setCueEndResolver(e)}ingestStreamClockCue(e){this.resumeCoordinator.ingestStreamClockCue(e)}setTransitionPhaseReporter(e){this.transitionPhaseReporter=e}reportPhase(e){this.transitionPhaseReporter?.(e)}setBreakResumePolicy(e,t,r=!1){this.adInsertionType=e,this.breakTimebase=t,this.state.continueContentDuringBreak=r,this.state.continueRefusalReported=!1}initialize(){this.adContainer||(this.setupOverlay(),this.presentationModes.setupPictureInPictureTracking(),"adaptive"===this.insertionMode&&this.presentationModes.setupFullscreenTracking())}async startGamSession(e,t){await this.gamPodBridge.startGamSession(e,t)}endGamSession(){this.gamPodBridge.endGamSession()}setSessionAssetParameters(e){this.assetParameters.setSessionAssetParameters(e)}setManifestAssetParameters(e){this.assetParameters.setManifestAssetParameters(e)}updateAssetParameters(e){this.assetParameters.updateAssetParameters(e)}updateAssetParameterMacros(e){this.assetParameters.updateAssetParameterMacros(e)}get updatedAssetParameters(){return this.state.updatedAssetParameters}setSessionAssetParameterMacros(e){this.assetParameters.setSessionAssetParameterMacros(e)}get sessionAssetParameterMacros(){return this.assetParameters.getSessionAssetParameterMacros()}buildVastTagUrl(e,t){return this.assetParameters.buildVastTagUrl(e,t)}pushAssetParameters(e,t){this.assetParameters.pushAssetParameters(e,t)}setupOverlay(){this.adStage.setupOverlay()}isGamPodAsset(e){return this.gamPodBridge.isGamPodAsset(e)}isExpandingPodAsset(e){return this.gamPodBridge.isExpandingPodAsset(e)}fallBackToPodLevelReporting(){this.gamPodBridge.fallBackToPodLevelReporting()}resetGamPodTracking(){this.gamPodBridge.resetGamPodTracking()}emitGamPodSteps(e){this.gamPodBridge.emitGamPodSteps(e)}isGamEnabled(){return this.gamPodBridge.isGamEnabled()}isGamReady(){return this.gamPodBridge.isGamReady()}isOverlayInsertion(){return"overlay"===this.effectiveInsertionMode()}settleAssetProgress(){this.adPlayerEvents.settleAssetProgress()}reportAdProgress(e){this.adPlayerEvents.reportAdProgress(e)}setupEventListeners(){this.adPlayerEvents.setupEventListeners()}async preload(e){return this.preloader.preload(e)}renderLshapeContentBackdrop(e){this.adStage.renderLshapeContentBackdrop(e)}currentInsertionMode(){return this.presentationModes.currentInsertionMode()}effectiveInsertionMode(){return this.presentationModes.effectiveInsertionMode()}isContentPictureInPicture(){return this.presentationModes.isPictureInPicture()}rebindContentElementIfSwapped(){this.presentationModes.rebindContentElementIfSwapped()}teardownFullscreenTracking(){this.presentationModes.teardownFullscreenTracking()}teardownPictureInPictureTracking(){this.presentationModes.teardownPictureInPictureTracking()}isPictureInPicture(){return this.presentationModes.isPictureInPicture()}pictureInPictureOwner(){return this.presentationModes.pictureInPictureOwner()}isShowingAd(){if(!this.isAdPlaying())return!1;const e=this.currentDeclaredFormat();return!n(e,this.isContentPictureInPicture())}currentDeclaredFormat(){return this.currentSelectedFormat??(this.currentBreak?l(this.currentBreak):void 0)}async playBreak(t,s,a){if(this.state.preloadGeneration++,this.resumeGeneration++,!this.sharedElementPlayer.beginSharedActivity())return;this.releaseContentHold(),this.rebindContentElementIfSwapped();const n=a?.effectiveDurationSec??null;this.effectiveBreakDurationSec=null!==n&&n>0?n:null;const d=this.effectiveBreakDurationSec??t.duration;if("shared-element"===this.effectiveInsertionMode())return g(this.state),void await this.sharedElementPlayer.playSharedElementBreak(t,s);if(!this.adPlayer)return void this.log("playBreak called before initialize()");const l=this.getVariant(t);if(!l)return void this.log("No valid variant found for break:",t.id);this.currentBreak=t,this.callbacks=s,this.isPlaying=!0,this.firstFrameGateActive=a?.firstFrameGate??!1,this.firstFrameGateShown=!1;const h=this.continueContentDuringBreakReason(l.format),u=r(t),c=this.shouldPauseContent(l.format)&&"ok"===h&&!u;c||u||this.state.continueRefusalReported||!this.shouldPauseContent(l.format)||(this.state.continueRefusalReported=!0,this.diagnose("DA-CONTINUE-PLAYOUT-REFUSED",`Content will be paused for breaks this session: ${h}`,{reason:h,format:l.format})),this.audioFocus=l.format===e.DOUBLE?this.doubleBoxAudio:c?"ad":null,this.adPlayer&&("content"===this.audioFocus?(this.adPlayer.muted=!0,this.adPlayer.volume=0):(this.adPlayer.muted=this.contentPlayer.muted,this.adPlayer.volume=this.contentPlayer.volume,"ad"===this.audioFocus&&(this.contentPlayer.muted=!0)));const m=this.declaredFormatOf(t);if(this.currentSelectedFormat=m,this.state.pipAdoptedForBreak=i(m,this.isContentPictureInPicture()),this.state.pipAdoptedForBreak&&this.diagnose("DA-PIP-FORMAT-OVERRIDE",`Picture-in-picture: ${m} break plays as single`,{declared:m,effective:e.SINGLE,reason:"picture-in-picture",midBreak:!1}),this.shouldPauseContent(l.format)?(this.resumeCoordinator.captureBreakAnchor(t),c?(this.contentPaused=!1,this.resumeCoordinator.beginContinueContentPlayout()):(this.contentPaused=!0,this.gateContentForBreak())):this.contentPaused=!1,this.applyLayout(l),l.format===e.LSHAPE_CONTENT)return this.renderLshapeContentBackdrop(l),void(d&&d>0?(this.log("Starting duration timer:",d,"seconds"),this.durationTimer=setTimeout(()=>{this.log("Duration timer expired, ending break"),this.endBreak()},1e3*d)):this.endBreak());this.firstFrameGateActive||(this.show(),this.reapplyAdContainerLayout(l));if(0===this.getAssets(t).length)return this.log("No assets in break, ending immediately"),void this.endBreak();{const e=Math.max(0,d||0)+this.adBreakCutSafetyMarginSec;T(this.state,e,()=>{this.log("Break-cut timer expired, ending break"),this.firstFrameRejector?this.onAdError(new R("Break cut before first frame rendered","DA-AD-START-TIMEOUT")):this.sequencer&&this.currentBreak&&this.isPlaying?(this.callbacks?.onBreakCut?.(this.currentBreak),this.drive(this.sequencer.onOutcome({kind:"break-elapsed"}))):this.endBreak()},()=>this.onAdError(new R("Ad playback startup timed out","DA-AD-START-TIMEOUT")))}this.sequencer=new o("overlay",()=>null,this.selectionInputs()),await this.drive(this.sequencer.beginBreak(t)),this.preloadChainedSuccessor(t.id)}get resumeHandoverInMotion(){return this.state.resumeCatchupActive}reapplyAdContainerLayout(e){this.adStage.reapplyAdContainerLayout(e)}adoptPictureInPictureMidBreak(){this.presentationModes.adoptPictureInPictureMidBreak()}async restorePictureInPictureToContent(){await this.presentationModes.restorePictureInPictureToContent()}continueContentDuringBreakReason(e){return this.variantSelector.continueContentDuringBreakReason(e)}shouldPauseContent(t){return t===e.SINGLE||t===e.LSHAPE_AD}selectionInputs(){return this.variantSelector.selectionInputs()}breakNothingToPlayReason(e){return this.variantSelector.breakNothingToPlayReason(e)}unknownVariantFormatsOf(e){return this.variantSelector.unknownVariantFormatsOf(e)}diagnoseUnknownVariantFormats(e){this.variantSelector.diagnoseUnknownVariantFormats(e)}declaredFormatOf(e){const{selected:t,deviceQualified:r}=h(e,this.selectionInputs());return(t??r)?.format??l(e)}getVariant(e){return this.variantSelector.getVariant(e)}applyLayout(e){this.adStage.applyLayout(e)}restoreLayout(e){this.adStage.restoreLayout(e)}getAssets(e){return d(this.getVariant(e))}async playAsset(e,t,r){return this.assetPlayback.playAsset(e,t,r)}clearOverlayImage(){this.adStage.clearOverlayImage()}async showPauseImage(e){return this.pauseAdPresenter.showPauseImage(e)}hidePauseImage(){this.pauseAdPresenter.hidePauseImage()}async drive(e){return this.breakSequenceDriver.drive(e)}emitStep(e){this.breakSequenceDriver.emitStep(e)}async resolveAssetUri(e,t){return this.assetPlayback.resolveAssetUri(e,t)}async buildGamUrl(e,t){return this.gamPodBridge.buildGamUrl(e,t)}resolveUri(e){return this.assetParameters.resolveUri(e)}adPlayerStreamRemainingSec(){return this.assetPlayback.adPlayerStreamRemainingSec()}onAdEnded(){this.currentBreak&&this.isPlaying&&this.sequencer&&(this.settleAssetProgress(),this.drive(this.sequencer.onOutcome({kind:"asset-ended"})))}toAdError(e){return this.assetPlayback.toAdError(e)}onAdError(e){this.adPlayerEvents.onAdError(e)}endBreak(){if(!this.currentBreak)return;this.log("Break ended:",this.currentBreak.id),M(this.state),this.state.gamPodTracker&&this.emitGamPodSteps(this.state.gamPodTracker.finish()),this.resetGamPodTracking(),this.pushAssetParameters(void 0);const e=this.currentBreak,s=this.getVariant(e)?.format;let a=this.abortingBreak?null:this.resolveChainedSuccessor(e.id);if(a){const e=this.breakNothingToPlayReason(a);this.diagnoseUnknownVariantFormats(a),e&&(this.diagnose("DA-BREAK-NO-PLAYABLE-ASSET",`chained break ${a.id} has no playable variant (${e}); it will not begin.`,{breakId:a.id,reason:e}),this.callbacks?.onAdError(a,void 0,new R(`Break ${a.id} has no playable variant (${e})`,"DA-BREAK-NO-PLAYABLE-ASSET")),this.callbacks?.onChainedBreakSkipped?.(a),a=null)}if(a)return void this.advanceToChainedBreak(e,a);if(this.currentSelectedFormat=void 0,this.resumeCoordinator.endContinueContentPlayout(),this.restorePictureInPictureToContent(),this.isPlaying=!1,this.currentBreak=null,this.resumeGeneration++,this.effectiveBreakDurationSec=null,g(this.state),this.preloader.clearWarmedBreaks(),this.firstFrameGateActive=!1,this.firstFrameGateShown=!1,this.clearFirstFrame("Break ended before first frame"),"ad"===this.audioFocus&&this.adPlayer?(this.contentPlayer.muted=this.adPlayer.muted,this.contentPlayer.volume=this.adPlayer.volume):"content"===this.audioFocus&&this.adPlayer&&(this.adPlayer.muted=this.contentPlayer.muted,this.adPlayer.volume=this.contentPlayer.volume),this.audioFocus=null,this.clearOverlayImage(),this.vastAdManager?.reset(),this.hide(),this.restoreLayout(s),this.adPlayer?.pause(),this.state.sharedAdClaimActive){const t=this.callbacks;return this.sharedElementPlayer.detachSharedAdListeners(),this.abortingBreak=!1,this.state.earlyReturnResumeOffsetSec=null,this.sharedElementPlayer.restoreSharedElementContent(e),void t?.onBreakEnd(e)}const i="recreate"===this.recoveryDeps.contentRecovery&&"function"==typeof this.contentPlayer.recreateMediaPipeline,n="single-decoder"===this.preloadMode||i?this.adPlayer?.unload?.():void 0;if(this.contentPaused&&r(e))this.contentPaused=!1,this.clearContentGate();else if(this.contentPaused){this.contentPaused=!1,this.clearContentGate(this.resumeHandoverInMotion);const r=this.resumeGeneration;S(this.recoveryDeps,{breakInfo:e,earlyReturnOffsetSec:this.state.earlyReturnResumeOffsetSec,isLiveResume:this.breakTimebase===t.WALLCLOCK,released:n,isCurrent:()=>r===this.resumeGeneration})}this.abortingBreak=!1,this.state.earlyReturnResumeOffsetSec=null,this.callbacks?.onBreakEnd(e)}currentAdAsset(){return this.isPlaying?this.currentAsset:null}skipBreak(){this.isPlaying&&(this.log("Skipping break:",this.currentBreak?.id),M(this.state),this.sequencer&&this.currentBreak?this.drive(this.sequencer.onOutcome({kind:"break-elapsed"})):this.endBreak())}abortBreak(e){this.isPlaying&&(this.log("Early return — break removed from manifest:",this.currentBreak?.id),this.state.earlyReturnResumeOffsetSec=Math.max(0,e),this.abortingBreak=!0,M(this.state),this.sequencer&&this.currentBreak?this.drive(this.sequencer.onOutcome({kind:"break-elapsed"})):this.endBreak())}stopActiveBreak(){g(this.state),this.sharedElementPlayer.cancelSharedRestore()&&this.isPlaying&&(this.log("Aborting active break on session end:",this.currentBreak?.id),M(this.state),this.sharedElementPlayer.detachSharedAdListeners(),this.sharedElementPlayer.releaseContentElementAdPlayer(),this.adPlayer?.pause(),this.clearOverlayImage(),this.vastAdManager?.reset(),this.hide(),this.restoreLayout(this.currentBreak?this.getVariant(this.currentBreak)?.format??void 0:void 0),this.isPlaying=!1,this.currentBreak=null,this.currentAsset=null,this.contentPaused=!1,this.clearContentGate(),this.resumeCoordinator.endContinueContentPlayout(),this.effectiveBreakDurationSec=null,this.firstFrameGateActive=!1,this.firstFrameGateShown=!1,this.clearFirstFrame("Break aborted before first frame"),this.audioFocus=null)}resolveChainedSuccessor(e){return this.breakSequenceDriver.resolveChainedSuccessor(e)}advanceToChainedBreak(e,t){this.breakSequenceDriver.advanceToChainedBreak(e,t)}preloadChainedSuccessor(e){this.preloader.preloadChainedSuccessor(e)}revealFirstFrame(){this.assetPlayback.revealFirstFrame()}armAdStartBudget(e){this.preloader.armAdStartBudget(e)}remainingAdStartBudgetMs(){return this.preloader.remainingAdStartBudgetMs()}loadWithinBudget(e,t){return this.preloader.loadWithinBudget(e,t)}async waitForFirstFrame(){return this.preloader.waitForFirstFrame()}clearFirstFrame(e="Break ended before first frame"){this.preloader.clearFirstFrame(e)}show(){this.adStage.show()}hide(){this.adStage.hide()}isAdPlaying(){return this.isPlaying}get muted(){return"content"===this.audioFocus?this.contentPlayer.muted:"ad"===this.audioFocus?this.adPlayer?this.adPlayer.muted:this.contentPlayer.muted:this.isPlaying&&this.adPlayer?this.adPlayer.muted:this.contentPlayer.muted}set muted(e){if("content"===this.audioFocus)this.contentPlayer.muted=e;else if("ad"===this.audioFocus)this.adPlayer&&(this.adPlayer.muted=e);else{(this.isPlaying&&this.adPlayer?this.adPlayer:this.contentPlayer).muted=e}this.vastAdManager?.setMuted(e),this.notifyVolumeChange()}get volume(){return"content"===this.audioFocus?this.contentPlayer.volume:"ad"===this.audioFocus?this.adPlayer?this.adPlayer.volume:this.contentPlayer.volume:this.isPlaying&&this.adPlayer?this.adPlayer.volume:this.contentPlayer.volume}set volume(e){if("content"===this.audioFocus)this.contentPlayer.volume=e;else if("ad"===this.audioFocus)this.adPlayer&&(this.adPlayer.volume=e);else{(this.isPlaying&&this.adPlayer?this.adPlayer:this.contentPlayer).volume=e}this.vastAdManager?.setVolume(e),this.notifyVolumeChange()}setVolumeChangeCallback(e){this.volumeChangeCallback=e}notifyVolumeChange(){this.volumeChangeCallback?.(this.muted,this.volume)}destroy(){if(this.destroyed)return;if(this.resumeGeneration++,g(this.state),!this.sharedElementPlayer.beginSharedActivity())return;this.destroyed=!0,this.adStage.destroy(),this.resumeCoordinator.cancelResumeLandedProbe(),this.resumeCoordinator.dispose(),this.resumeCoordinator.endResumeCatchup(),this.resumeCoordinator.endContinueContentPlayout(),this.state.resumePrepositionSettle&&(clearTimeout(this.state.resumePrepositionSettle),this.state.resumePrepositionSettle=null),this.releaseContentHold(),this.clearContentGate(),this.clearFirstFrame("Controller destroyed before first frame"),this.firstFrameGateActive=!1,this.firstFrameGateShown=!1,this.resetGamPodTracking(),M(this.state),this.companionRemoveTimer&&(clearTimeout(this.companionRemoveTimer),this.companionRemoveTimer=null),this.adFadeTimer&&(clearTimeout(this.adFadeTimer),this.adFadeTimer=null),this.adPlayer?.pause(),this.clearOverlayImage(),this.pauseAdPresenter.destroy(),this.contentVolumeHandler&&(this.contentPlayer.off("volumechange",this.contentVolumeHandler),this.contentVolumeHandler=null),this.sharedElementPlayer.detachSharedAdListeners(),this.sharedElementPlayer.releaseContentElementAdPlayer(),this.state.pipTransferredToAd=!1,this.teardownFullscreenTracking(),this.teardownPictureInPictureTracking(),this.adVolumeChangeHandler&&this.adPlayer&&this.adPlayer.off("volumechange",this.adVolumeChangeHandler),this.adVolumeChangeHandler=null,this.hide(),this.adStage.destroy(),this.resizeObserver?.disconnect(),this.resizeObserver=null,this.contentVideoMutationObserver?.disconnect(),this.contentVideoMutationObserver=null,this.contentVideoElement&&this.contentVideoGeometryHandler&&(this.contentVideoElement.removeEventListener("loadedmetadata",this.contentVideoGeometryHandler),this.contentVideoElement.removeEventListener("resize",this.contentVideoGeometryHandler)),this.contentVideoElement=null,this.contentVideoGeometryHandler=null;const e=this.adStage.restoreContent(this.playerContainerCreatedBySDK,this.playerContainerOriginalStyle);this.companionElement&&this.companionElement.parentElement&&(this.companionElement.parentElement.removeChild(this.companionElement),this.companionElement=null),this.isPlaying=!1,this.currentBreak=null,this.adContainer&&this.adContainer.parentElement&&this.adContainer.parentElement.removeChild(this.adContainer),!e&&this.stage&&this.stage.parentElement&&this.stage.parentElement.removeChild(this.stage),this.stage=null,this.adContainer=null,this.adMediaContainer=null;try{this.adPlayer?.destroy()}catch(e){this.log("Ad player adapter destroy failed (ignored):",e)}this.adPlayer=null,this.callbacks=null,this.gamPodBridge.destroy(),this.vastAdManager?.destroy(),this.vastAdManager=null,this.log("Ad player destroyed")}log(...e){this.debug&&u("[OptiViewAds:AdPlayerController]",...e)}}AdPlayerController.AD_START_BUDGET_MS=5e3,AdPlayerController.GAM_POD_AD_START_BUDGET_MS=1e4,AdPlayerController.MIN_AD_START_BUDGET_MS=3e3;
|
|
@@ -168,7 +168,10 @@ export declare class BreakScheduler {
|
|
|
168
168
|
stop(): void;
|
|
169
169
|
completeBreak(breakId: string): void;
|
|
170
170
|
/** Bracket a content RELOAD so the new stream's start is not read as a backward seek. `false` also discards the stale previous position. */
|
|
171
|
+
private contentReloading;
|
|
172
|
+
private startupSeeking;
|
|
171
173
|
setContentReloading(active: boolean): void;
|
|
174
|
+
setStartupSeeking(active: boolean): void;
|
|
172
175
|
/**
|
|
173
176
|
* The runtime is about to resume-seek content to `targetSeconds` after a break ending at
|
|
174
177
|
* `breakEndSeconds`. Arms a guard so the small backward step is not read as a DVR seek-back;
|
|
@@ -184,8 +187,8 @@ export declare class BreakScheduler {
|
|
|
184
187
|
/**
|
|
185
188
|
* Advance the schedule NOW because a cue reached the playhead: the 250ms tick discovers a start
|
|
186
189
|
* ~125ms late on average, an adapter reporting PRESENTATION knows the instant. Runs
|
|
187
|
-
* {@link checkBreaks} so every tick guard applies unchanged
|
|
188
|
-
*
|
|
190
|
+
* {@link checkBreaks} so every tick guard applies unchanged, including the timing check against
|
|
191
|
+
* the tracker's media time: a segment-anchored emsg cue presenting at its segment start is ignored.
|
|
189
192
|
* Returns the id of the break this armed, or null.
|
|
190
193
|
*/
|
|
191
194
|
notifyCuePresented(pts: number): string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{BreakEvent as e,Timebase as t,getBreakEvent as a,getPostRollDelay as i,getPreRollDelay as r,isEventTrigger as s,isPauseBreak as n}from"../types/BreakManifest";import{DEFAULT_BREAK_WARNINGS as d,DEFAULT_PDT_GRACE_SECONDS as o}from"../types/OptiViewAdsConfig";import{PTS_MATCH_TOLERANCE_SECONDS as l}from"./MetadataCueTracker";import{debugLog as h}from"../diagnostics/debugLog";import{createBreakTimingState as k,getBreakStartTime as c,getCurrentTime as u,secondsUntilBreak as b}from"./breakTiming";import{RewindRearm as g}from"./RewindRearm";export const systemClock={now:()=>Date.now()};export var BreakState;!function(e){e.PENDING="pending",e.CHAINED="chained",e.ACTIVE="active",e.COMPLETED="completed",e.SKIPPED="skipped"}(BreakState||(BreakState={}));export class BreakScheduler{constructor(e,t=!1,a={enabled:!1,maxGapSeconds:0},i=systemClock,r={enabled:!1,minBreakDurationSeconds:0},s=null,n=d,l=null,h=o){this.breaks=new Map,this.timing=k(),this.checkInterval=null,this.onBreakStart=null,this.onBreakApproaching=null,this.onBreakWarning=null,this.onBreakAborted=null,this.abortNotifiedBreakIds=new Set,this.preloadedBreakIds=new Set,this.breakWarnings=d,this.breakWarningFired=new Map,this.completedBreakIds=new Set,this.unsupportedEventBreakIds=new Set,this.playbackEndedAtMs=null,this.anvatoEnteredDiagnosed=new Set,this.retiredBreaks=new Map,this.minSeenTime=null,this.JOIN_DISCRIMINATOR_SLACK_MS=1e3,this.WINDOW_ESCAPE_GRACE_MS=12e3,this.TIMING_TOLERANCE_MS=5e3,this.PRELOAD_AHEAD_SECONDS=8,this.player=e,this.debug=t,this.chainingEnabled=a.enabled,this.maxChainGapMs=1e3*Math.max(0,a.maxGapSeconds),this.tuneInEnabled=r.enabled,this.minBreakDurationMs=1e3*Math.max(0,r.minBreakDurationSeconds),this.clock=i,this.diagnose=s,this.breakWarnings=n,this.cueTracker=l;const c=Math.max(0,h);this.timingDeps={player:this.player,clock:this.clock,cueTracker:this.cueTracker,diagnose:this.diagnose,log:(...e)=>this.log(...e),pdtGraceSeconds:c,pdtGraceMs:1e3*c},this.rewind=new g({breaks:this.breaks,retiredBreaks:this.retiredBreaks,completedBreakIds:this.completedBreakIds,preloadedBreakIds:this.preloadedBreakIds,timing:this.timing,clock:this.clock,log:(...e)=>this.log(...e),getBreakStartTime:e=>this.getBreakStartTime(e)})}claimChain(e){if(!this.chainingEnabled)return;let t=this.breaks.get(e)?.break??null;for(;t;){const e=this.findSuccessor(t);if(!e)break;const a=this.breaks.get(e.id);if(!a||a.state!==BreakState.PENDING)break;a.state=BreakState.CHAINED,this.log(`🔗 Break "${e.id}" claimed as chained after "${t.id}"`),t=e}}getChainedSuccessor(e){if(!this.chainingEnabled)return null;const t=this.breaks.get(e)?.break;return t?this.findSuccessor(t,[BreakState.PENDING,BreakState.CHAINED]):null}activateChainedBreak(e){const t=this.breaks.get(e);t&&(t.state=BreakState.ACTIVE)}findSuccessor(e,t=[BreakState.PENDING]){const a=this.getBreakStartTime(e);if(null===a)return null;const i=a+1e3*(e.duration??0);let r=null;for(const s of this.breaks.values()){if(s.break.id===e.id)continue;if(!t.includes(s.state))continue;const n=this.getBreakStartTime(s.break);if(null===n)continue;if(n<=a)continue;const d=n-i;d>this.maxChainGapMs||(d<-this.TIMING_TOLERANCE_MS||(!r||n<r.start)&&(r={break:s.break,start:n}))}return r?.break??null}updateManifest(e){this.timing.timebase=e.timebase,this.rewind.setAdStartDelayMs(1e3*Math.max(0,e.adStartDelay??0));for(const t of e.breaks){if(n(t))continue;if(!this.isSchedulableEvent(t))continue;if(this.completedBreakIds.has(t.id))continue;const e=this.breaks.get(t.id);e?e.state===BreakState.PENDING&&(e.break=t):(this.breaks.set(t.id,{break:t,state:BreakState.PENDING}),this.log(`📅 Break scheduled: "${t.id}" at ${this.describeStart(t)}`))}const t=new Set(e.breaks.map(e=>e.id));for(const[e,a]of this.breaks)t.has(e)||a.state!==BreakState.ACTIVE||this.abortNotifiedBreakIds.has(e)||(this.abortNotifiedBreakIds.add(e),this.log("Break removed from manifest while active (early return):",e),this.diagnose?.("DA-BREAK-EARLY-RETURN","Active break removed from manifest; returning to content early",{breakId:e}),this.onBreakAborted?.(a.break));this.cleanupBreaks(t)}resetForChannelSwitch(e){const t=this.getActiveBreak();for(const e of[...this.breaks.keys()])e!==t?.id&&this.breaks.delete(e);for(const e of[...this.abortNotifiedBreakIds])e!==t?.id&&this.abortNotifiedBreakIds.delete(e);this.completedBreakIds.clear(),this.retiredBreaks.clear(),this.preloadedBreakIds.clear(),this.breakWarningFired.clear(),this.unsupportedEventBreakIds.clear(),this.anvatoEnteredDiagnosed.clear(),this.timing.anvatoPendingDiagnosed.clear(),this.timing.anvatoMatchedDiagnosed.clear(),this.playbackEndedAtMs=null,this.rewind.beginChannel();const a=t?{...e,breaks:e.breaks.filter(e=>e.id!==t.id)}:e;this.updateManifest(a)}isSchedulableEvent(t){return!s(t.start)||t.start.event===e.START||t.start.event===e.END||(this.unsupportedEventBreakIds.has(t.id)||(this.unsupportedEventBreakIds.add(t.id),this.log(`⚠️ Break ignored: "${t.id}" (unsupported event trigger "${t.start.event}")`),this.diagnose?.("DA-BREAK-EVENT-UNSUPPORTED","Break triggered by an event this SDK version does not schedule; the break was ignored",{breakId:t.id,event:t.start.event})),!1)}describeStart(e){const t=r(e);if(null!==t)return t>0?`playback start + ${t}s`:"playback start";const a=i(e);return null!==a?a>0?`playback end + ${a}s`:"playback end":"string"==typeof e.start?new Date(e.start).toLocaleTimeString():String(e.start)}markPlaybackStart(){null!==this.playbackEndedAtMs&&(this.playbackEndedAtMs=null,this.log("Content replayed; pending post-rolls cancelled")),this.rewind.playbackStarted||(this.rewind.markPlaybackStart(this.peekPosition()),this.log("Content playback started at",new Date(this.clock.now()).toISOString()))}markPlaybackEnd(){null===this.playbackEndedAtMs&&(this.playbackEndedAtMs=this.clock.now(),this.log("Content playback ended at",new Date(this.playbackEndedAtMs).toISOString()))}start(e,t,a,i){this.onBreakStart=e,this.onBreakApproaching=t??null,this.onBreakWarning=a??null,this.onBreakAborted=i??null,this.checkInterval&&clearInterval(this.checkInterval),this.checkInterval=setInterval(()=>{this.tick()},250),this.log("Scheduler started")}tick(){this.checkBreaks()}stop(){this.checkInterval&&(clearInterval(this.checkInterval),this.checkInterval=null),this.onBreakStart=null,this.log("Scheduler stopped")}completeBreak(e){this.completedBreakIds.add(e),this.breakWarningFired.delete(e),this.abortNotifiedBreakIds.delete(e);const t=this.breaks.get(e);t&&(this.log("Break completed and removed:",e),null===a(t.break)&&null===this.cueTracker&&this.retiredBreaks.set(e,t.break),this.breaks.delete(e),this.preloadedBreakIds.delete(e))}setContentReloading(e){this.rewind.setContentReloading(e)}notifyResumeSeek(e,t){this.rewind.notifyResumeSeek(e,t)}notifyViewerSeek(){this.rewind.notifyViewerSeek()}cleanupBreaks(e){const t=[];for(const[a,i]of this.breaks)i.state!==BreakState.COMPLETED&&i.state!==BreakState.SKIPPED&&(e.has(a)||i.state===BreakState.ACTIVE)||t.push(a);for(const e of t)this.breaks.delete(e),this.preloadedBreakIds.delete(e),this.breakWarningFired.delete(e)}getActiveBreak(){for(const e of this.breaks.values())if(e.state===BreakState.ACTIVE)return e.break;return null}getSecondsUntilBreak(e){const t=this.breaks.get(e);if(!t||t.state!==BreakState.PENDING)return null;const a=this.getCurrentTime();if(null===a)return null;const i=this.getBreakStartTime(t.break);return null===i?null:b(i,a)}notifyCuePresented(e){if(null===this.cueTracker||this.timing.timebase!==t.PTS)return null;if(!Number.isFinite(e))return null;let a=null;for(const t of this.breaks.values())if(t.state===BreakState.PENDING&&"number"==typeof t.break.start&&!(Math.abs(t.break.start-e)>l)){a=t.break.id;break}if(null===a)return null;const i=this.player.currentTime;this.checkBreaks();const r=this.breaks.get(a);return r&&r.state===BreakState.ACTIVE?this.anvatoEnteredDiagnosed.has(a)?null:(this.anvatoEnteredDiagnosed.add(a),this.log(`🎯 Break "${a}" armed by cue presentation (pts ${e})`),this.diagnose?.("DA-ANVATO-CUE-ENTERED",`Break "${a}" started from the presented cue (pts ${e}) rather than the scheduler tick.`,{breakId:a,pts:e,mediaTimeSec:i}),a):null}checkBreaks(){const e=this.getCurrentTime();this.rewind.trackPlayedTime(e);const a=this.rewind.isAdStartDelayActive();null!==e&&(this.minSeenTime=null===this.minSeenTime?e:Math.min(this.minSeenTime,e),this.rewind.rearmOnRewind(e));for(const s of this.breaks.values()){if(s.state===BreakState.ACTIVE&&this.timing.timebase===t.WALLCLOCK&&null!==e&&!this.abortNotifiedBreakIds.has(s.break.id)){const t=this.getBreakStartTime(s.break),a=1e3*(s.break.duration??0);null!==t&&a>0&&e>t+a+this.WINDOW_ESCAPE_GRACE_MS&&(this.abortNotifiedBreakIds.add(s.break.id),this.log(`⚠️ Break window escaped: "${s.break.id}" (ended ${Math.round((e-t-a)/1e3)}s ago on the stream clock)`),this.diagnose?.("DA-BREAK-WINDOW-ESCAPED","Active break outlived its wallclock window; returning to content",{breakId:s.break.id,overrunSec:Math.round((e-t-a)/100)/10}),this.onBreakAborted?.(s.break));continue}if(s.state!==BreakState.PENDING)continue;const n=r(s.break);if(null!==n){this.checkPreRollBreak(s,n,a);continue}const d=i(s.break);if(null!==d){this.checkPostRollBreak(s,d,a);continue}if(null===e)continue;if(a)continue;null!==this.cueTracker&&this.timing.timebase===t.PTS&&this.maybePreload(s.break,"manifest arrival — Anvato flow");const o=this.getBreakStartTime(s.break);if(null===o)continue;const l=e-o,h=Math.round(-l/1e3);if(this.checkWarnings(s.break,h),h>0&&h<=15&&h%2==0&&this.log(`⏱️ Break "${s.break.id}" starts in ${h}s`),h>0&&h<=this.PRELOAD_AHEAD_SECONDS&&this.maybePreload(s.break,`${h}s ahead`),l>=0&&l<=this.TIMING_TOLERANCE_MS){const e=Math.max(0,l),t=1e3*(s.break.duration??0)-e;if(t<=0){this.log(`⚠️ Break missed: "${s.break.id}" (ended ${Math.round(-t/1e3)}s ago, inside the start tolerance)`),s.state=BreakState.SKIPPED,null===this.cueTracker&&this.retiredBreaks.set(s.break.id,s.break);break}if(null!==this.minSeenTime&&o<this.minSeenTime-this.JOIN_DISCRIMINATOR_SLACK_MS&&(!this.tuneInEnabled||t<this.minBreakDurationMs)){this.log(`⚠️ Break missed: "${s.break.id}" (joined in progress inside the start tolerance, ${Math.round(t/1e3)}s remaining < tune-in rules)`),s.state=BreakState.SKIPPED,null===this.cueTracker&&this.retiredBreaks.set(s.break.id,s.break);break}this.log(`🎬 BREAK TRIGGERED: "${s.break.id}"`),s.state=BreakState.ACTIVE,this.onBreakStart&&this.onBreakStart(s.break,{tuneIn:!1,elapsedSec:e/1e3,remainingSec:t/1e3});break}if(l>this.TIMING_TOLERANCE_MS){const t=o+1e3*(s.break.duration??0)-e;if(this.tuneInEnabled&&t>=this.minBreakDurationMs&&t>0){this.log(`🎬 TUNE-IN BREAK TRIGGERED: "${s.break.id}" (${Math.round(t/1e3)}s remaining)`),s.state=BreakState.ACTIVE,this.onBreakStart&&this.onBreakStart(s.break,{tuneIn:!0,elapsedSec:l/1e3,remainingSec:t/1e3});break}this.log(`⚠️ Break missed: "${s.break.id}" (was ${Math.round(l/1e3)}s ago)`),s.state=BreakState.SKIPPED,null===this.cueTracker&&this.retiredBreaks.set(s.break.id,s.break)}}}checkPreRollBreak(e,t,a){if(!this.rewind.playbackStarted)return;const i=1e3*t;if(this.rewind.adStartDelayMs>0&&i<this.rewind.adStartDelayMs)return this.log(`⚠️ Pre-roll skipped: "${e.break.id}" (delay ${t}s < adStartDelay ${this.rewind.adStartDelayMs/1e3}s)`),void(e.state=BreakState.SKIPPED);const r=this.rewind.channelPlayedMs,s=Math.round((i-r)/1e3);s>0&&s<=15&&s%2==0&&this.log(`⏱️ Pre-roll "${e.break.id}" starts in ${s}s`),s>0&&s<=this.PRELOAD_AHEAD_SECONDS&&this.maybePreload(e.break,`pre-roll, ${s}s ahead`),r>=i&&!a&&(this.log(`🎬 PRE-ROLL TRIGGERED: "${e.break.id}"${t>0?` (after ${t}s delay)`:""}`),e.state=BreakState.ACTIVE,this.onBreakStart&&this.onBreakStart(e.break,{tuneIn:!1,elapsedSec:0,remainingSec:e.break.duration??0}))}checkPostRollBreak(e,t,a){if(null===this.playbackEndedAtMs)return;const i=1e3*t,r=this.clock.now()-this.playbackEndedAtMs,s=Math.round((i-r)/1e3);s>0&&s<=this.PRELOAD_AHEAD_SECONDS&&this.maybePreload(e.break,`post-roll, ${s}s ahead`),r>=i&&!a&&(this.log(`🎬 POST-ROLL TRIGGERED: "${e.break.id}"${t>0?` (after ${t}s delay)`:""}`),e.state=BreakState.ACTIVE,this.onBreakStart&&this.onBreakStart(e.break,{tuneIn:!1,elapsedSec:0,remainingSec:e.break.duration??0}))}isAdStartDelayActive(){return this.rewind.isAdStartDelayActive()}getCurrentTime(){return u(this.timing,this.timingDeps)}maybePreload(e,t){if(this.preloadedBreakIds.has(e.id)||!this.onBreakApproaching)return;this.log(`⏳ Preloading break "${e.id}" (${t})`);!1!==this.onBreakApproaching(e)&&this.preloadedBreakIds.add(e.id)}getBreakStartTime(e){return s(e.start)?null:c(this.timing,this.timingDeps,e)}peekPosition(){if(this.timing.timebase!==t.WALLCLOCK)return 1e3*this.player.currentTime;const e=this.player.programDateTime?.getTime();return void 0===e||isNaN(e)?null:e}destroy(){this.stop(),this.breaks.clear(),this.preloadedBreakIds.clear(),this.completedBreakIds.clear(),this.retiredBreaks.clear(),this.minSeenTime=null,this.unsupportedEventBreakIds.clear(),this.playbackEndedAtMs=null,this.rewind.reset(),this.breakWarningFired.clear(),this.abortNotifiedBreakIds.clear(),this.timing.pdtMissingWarned=!1,this.timing.pdtSeen=!1,this.timing.firstPdtAttemptMs=null,this.timing.playbackStartedAtMs=null,this.timing.anvatoPendingDiagnosed.clear(),this.timing.anvatoMatchedDiagnosed.clear(),this.cueTracker?.clear()}checkWarnings(e,t){if(t<=0||0===this.breakWarnings.seconds.length)return;let a=this.breakWarningFired.get(e.id);a||(a=new Set,this.breakWarningFired.set(e.id,a));for(const i of this.breakWarnings.seconds)a.has(i)||t<=i&&(a.add(i),this.log(`Break warning for "${e.id}" (${t}s <= ${i}s)`),this.onBreakWarning?.(e,i,t))}log(...e){this.debug&&h("[OptiViewAds:BreakScheduler]",...e)}}
|
|
1
|
+
import{BreakEvent as e,Timebase as t,getBreakEvent as a,getPostRollDelay as i,getPreRollDelay as r,isEventTrigger as s,isPauseBreak as n}from"../types/BreakManifest";import{DEFAULT_BREAK_WARNINGS as d,DEFAULT_PDT_GRACE_SECONDS as o}from"../types/OptiViewAdsConfig";import{PTS_MATCH_TOLERANCE_SECONDS as l}from"./MetadataCueTracker";import{debugLog as h}from"../diagnostics/debugLog";import{createBreakTimingState as k,diagnoseAnvatoBreakEnd as c,getBreakStartTime as u,getCurrentTime as b,secondsUntilBreak as g}from"./breakTiming";import{RewindRearm as S}from"./RewindRearm";export const systemClock={now:()=>Date.now()};export var BreakState;!function(e){e.PENDING="pending",e.CHAINED="chained",e.ACTIVE="active",e.COMPLETED="completed",e.SKIPPED="skipped"}(BreakState||(BreakState={}));export class BreakScheduler{constructor(e,t=!1,a={enabled:!1,maxGapSeconds:0},i=systemClock,r={enabled:!1,minBreakDurationSeconds:0},s=null,n=d,l=null,h=o){this.breaks=new Map,this.timing=k(),this.checkInterval=null,this.onBreakStart=null,this.onBreakApproaching=null,this.onBreakWarning=null,this.onBreakAborted=null,this.abortNotifiedBreakIds=new Set,this.preloadedBreakIds=new Set,this.breakWarnings=d,this.breakWarningFired=new Map,this.completedBreakIds=new Set,this.unsupportedEventBreakIds=new Set,this.playbackEndedAtMs=null,this.anvatoEnteredDiagnosed=new Set,this.retiredBreaks=new Map,this.minSeenTime=null,this.JOIN_DISCRIMINATOR_SLACK_MS=1e3,this.WINDOW_ESCAPE_GRACE_MS=12e3,this.TIMING_TOLERANCE_MS=5e3,this.PRELOAD_AHEAD_SECONDS=8,this.contentReloading=!1,this.startupSeeking=!1,this.player=e,this.debug=t,this.chainingEnabled=a.enabled,this.maxChainGapMs=1e3*Math.max(0,a.maxGapSeconds),this.tuneInEnabled=r.enabled,this.minBreakDurationMs=1e3*Math.max(0,r.minBreakDurationSeconds),this.clock=i,this.diagnose=s,this.breakWarnings=n,this.cueTracker=l;const c=Math.max(0,h);this.timingDeps={player:this.player,clock:this.clock,cueTracker:this.cueTracker,diagnose:this.diagnose,log:(...e)=>this.log(...e),pdtGraceSeconds:c,pdtGraceMs:1e3*c},this.rewind=new S({breaks:this.breaks,retiredBreaks:this.retiredBreaks,completedBreakIds:this.completedBreakIds,preloadedBreakIds:this.preloadedBreakIds,timing:this.timing,clock:this.clock,log:(...e)=>this.log(...e),getBreakStartTime:e=>this.getBreakStartTime(e)})}claimChain(e){if(!this.chainingEnabled)return;let t=this.breaks.get(e)?.break??null;for(;t;){const e=this.findSuccessor(t);if(!e)break;const a=this.breaks.get(e.id);if(!a||a.state!==BreakState.PENDING)break;a.state=BreakState.CHAINED,this.log(`🔗 Break "${e.id}" claimed as chained after "${t.id}"`),t=e}}getChainedSuccessor(e){if(!this.chainingEnabled)return null;const t=this.breaks.get(e)?.break;return t?this.findSuccessor(t,[BreakState.PENDING,BreakState.CHAINED]):null}activateChainedBreak(e){const t=this.breaks.get(e);t&&(t.state=BreakState.ACTIVE)}findSuccessor(e,t=[BreakState.PENDING]){const a=this.getBreakStartTime(e);if(null===a)return null;const i=a+1e3*(e.duration??0);let r=null;for(const s of this.breaks.values()){if(s.break.id===e.id)continue;if(!t.includes(s.state))continue;const n=this.getBreakStartTime(s.break);if(null===n)continue;if(n<=a)continue;const d=n-i;d>this.maxChainGapMs||(d<-this.TIMING_TOLERANCE_MS||(!r||n<r.start)&&(r={break:s.break,start:n}))}return r?.break??null}updateManifest(e){this.timing.timebase=e.timebase,this.rewind.setAdStartDelayMs(1e3*Math.max(0,e.adStartDelay??0));for(const t of e.breaks){if(n(t))continue;if(!this.isSchedulableEvent(t))continue;if(this.completedBreakIds.has(t.id))continue;const e=this.breaks.get(t.id);e?e.state===BreakState.PENDING&&(e.break=t):(this.breaks.set(t.id,{break:t,state:BreakState.PENDING}),this.log(`📅 Break scheduled: "${t.id}" at ${this.describeStart(t)}`))}const t=new Set(e.breaks.map(e=>e.id));for(const[e,a]of this.breaks)t.has(e)||a.state!==BreakState.ACTIVE||this.abortNotifiedBreakIds.has(e)||(this.abortNotifiedBreakIds.add(e),this.log("Break removed from manifest while active (early return):",e),this.diagnose?.("DA-BREAK-EARLY-RETURN","Active break removed from manifest; returning to content early",{breakId:e}),this.onBreakAborted?.(a.break));this.cleanupBreaks(t)}resetForChannelSwitch(e){const t=this.getActiveBreak();for(const e of[...this.breaks.keys()])e!==t?.id&&this.breaks.delete(e);for(const e of[...this.abortNotifiedBreakIds])e!==t?.id&&this.abortNotifiedBreakIds.delete(e);this.completedBreakIds.clear(),this.retiredBreaks.clear(),this.preloadedBreakIds.clear(),this.breakWarningFired.clear(),this.unsupportedEventBreakIds.clear(),this.anvatoEnteredDiagnosed.clear(),this.timing.anvatoPendingDiagnosed.clear(),this.timing.anvatoMatchedDiagnosed.clear(),this.timing.anvatoEndDiagnosed.clear(),this.playbackEndedAtMs=null,this.rewind.beginChannel();const a=t?{...e,breaks:e.breaks.filter(e=>e.id!==t.id)}:e;this.updateManifest(a)}isSchedulableEvent(t){return!s(t.start)||t.start.event===e.START||t.start.event===e.END||(this.unsupportedEventBreakIds.has(t.id)||(this.unsupportedEventBreakIds.add(t.id),this.log(`⚠️ Break ignored: "${t.id}" (unsupported event trigger "${t.start.event}")`),this.diagnose?.("DA-BREAK-EVENT-UNSUPPORTED","Break triggered by an event this SDK version does not schedule; the break was ignored",{breakId:t.id,event:t.start.event})),!1)}describeStart(e){const t=r(e);if(null!==t)return t>0?`playback start + ${t}s`:"playback start";const a=i(e);return null!==a?a>0?`playback end + ${a}s`:"playback end":"string"==typeof e.start?new Date(e.start).toLocaleTimeString():String(e.start)}markPlaybackStart(){null!==this.playbackEndedAtMs&&(this.playbackEndedAtMs=null,this.log("Content replayed; pending post-rolls cancelled")),this.rewind.playbackStarted||(this.rewind.markPlaybackStart(this.peekPosition()),this.log("Content playback started at",new Date(this.clock.now()).toISOString()))}markPlaybackEnd(){null===this.playbackEndedAtMs&&(this.playbackEndedAtMs=this.clock.now(),this.log("Content playback ended at",new Date(this.playbackEndedAtMs).toISOString()))}start(e,t,a,i){this.onBreakStart=e,this.onBreakApproaching=t??null,this.onBreakWarning=a??null,this.onBreakAborted=i??null,this.checkInterval&&clearInterval(this.checkInterval),this.checkInterval=setInterval(()=>{this.tick()},250),this.log("Scheduler started")}tick(){this.checkBreaks()}stop(){this.checkInterval&&(clearInterval(this.checkInterval),this.checkInterval=null),this.onBreakStart=null,this.log("Scheduler stopped")}completeBreak(e){this.completedBreakIds.add(e),this.breakWarningFired.delete(e),this.abortNotifiedBreakIds.delete(e);const t=this.breaks.get(e);t&&(this.log("Break completed and removed:",e),null===a(t.break)&&null===this.cueTracker&&this.retiredBreaks.set(e,t.break),this.breaks.delete(e),this.preloadedBreakIds.delete(e))}setContentReloading(e){this.contentReloading=e,this.rewind.setContentReloading(e||this.startupSeeking)}setStartupSeeking(e){this.startupSeeking=e,this.rewind.setContentReloading(e||this.contentReloading)}notifyResumeSeek(e,t){this.rewind.notifyResumeSeek(e,t)}notifyViewerSeek(){this.rewind.notifyViewerSeek()}cleanupBreaks(e){const t=[];for(const[a,i]of this.breaks)i.state!==BreakState.COMPLETED&&i.state!==BreakState.SKIPPED&&(e.has(a)||i.state===BreakState.ACTIVE)||t.push(a);for(const e of t)this.breaks.delete(e),this.preloadedBreakIds.delete(e),this.breakWarningFired.delete(e)}getActiveBreak(){for(const e of this.breaks.values())if(e.state===BreakState.ACTIVE)return e.break;return null}getSecondsUntilBreak(e){const t=this.breaks.get(e);if(!t||t.state!==BreakState.PENDING)return null;const a=this.getCurrentTime();if(null===a)return null;const i=this.getBreakStartTime(t.break);return null===i?null:g(i,a)}notifyCuePresented(e){if(null===this.cueTracker||this.timing.timebase!==t.PTS)return null;if(!Number.isFinite(e))return null;let a=null;for(const t of this.breaks.values())if(t.state===BreakState.PENDING&&"number"==typeof t.break.start&&!(Math.abs(t.break.start-e)>l)){a=t.break.id;break}if(null===a)return null;const i=this.player.currentTime;this.checkBreaks();const r=this.breaks.get(a);return r&&r.state===BreakState.ACTIVE?this.anvatoEnteredDiagnosed.has(a)?null:(this.anvatoEnteredDiagnosed.add(a),this.log(`🎯 Break "${a}" armed by cue presentation (pts ${e})`),this.diagnose?.("DA-ANVATO-CUE-ENTERED",`Break "${a}" started from the presented cue (pts ${e}) rather than the scheduler tick.`,{breakId:a,pts:e,mediaTimeSec:i}),a):null}checkBreaks(){if(this.startupSeeking)return;const e=this.getCurrentTime();this.rewind.trackPlayedTime(e);const a=this.rewind.isAdStartDelayActive();null!==e&&(this.minSeenTime=null===this.minSeenTime?e:Math.min(this.minSeenTime,e),this.rewind.rearmOnRewind(e));for(const s of this.breaks.values()){if(s.state===BreakState.ACTIVE&&this.timing.timebase===t.WALLCLOCK&&null!==e&&!this.abortNotifiedBreakIds.has(s.break.id)){const t=this.getBreakStartTime(s.break),a=1e3*(s.break.duration??0);null!==t&&a>0&&e>t+a+this.WINDOW_ESCAPE_GRACE_MS&&(this.abortNotifiedBreakIds.add(s.break.id),this.log(`⚠️ Break window escaped: "${s.break.id}" (ended ${Math.round((e-t-a)/1e3)}s ago on the stream clock)`),this.diagnose?.("DA-BREAK-WINDOW-ESCAPED","Active break outlived its wallclock window; returning to content",{breakId:s.break.id,overrunSec:Math.round((e-t-a)/100)/10}),this.onBreakAborted?.(s.break));continue}if(s.state===BreakState.ACTIVE&&this.timing.timebase===t.PTS&&c(this.timing,this.timingDeps,s.break),s.state!==BreakState.PENDING)continue;const n=r(s.break);if(null!==n){this.checkPreRollBreak(s,n,a);continue}const d=i(s.break);if(null!==d){this.checkPostRollBreak(s,d,a);continue}if(null===e)continue;if(a)continue;null!==this.cueTracker&&this.timing.timebase===t.PTS&&this.maybePreload(s.break,"manifest arrival — Anvato flow");const o=this.getBreakStartTime(s.break);if(null===o)continue;const l=e-o,h=Math.round(-l/1e3);if(this.checkWarnings(s.break,h),h>0&&h<=15&&h%2==0&&this.log(`⏱️ Break "${s.break.id}" starts in ${h}s`),h>0&&h<=this.PRELOAD_AHEAD_SECONDS&&this.maybePreload(s.break,`${h}s ahead`),l>=0&&l<=this.TIMING_TOLERANCE_MS){const e=Math.max(0,l),t=1e3*(s.break.duration??0)-e;if(t<=0){this.log(`⚠️ Break missed: "${s.break.id}" (ended ${Math.round(-t/1e3)}s ago, inside the start tolerance)`),s.state=BreakState.SKIPPED,null===this.cueTracker&&this.retiredBreaks.set(s.break.id,s.break);break}if(null!==this.minSeenTime&&o<this.minSeenTime-this.JOIN_DISCRIMINATOR_SLACK_MS&&(!this.tuneInEnabled||t<this.minBreakDurationMs)){this.log(`⚠️ Break missed: "${s.break.id}" (joined in progress inside the start tolerance, ${Math.round(t/1e3)}s remaining < tune-in rules)`),s.state=BreakState.SKIPPED,null===this.cueTracker&&this.retiredBreaks.set(s.break.id,s.break);break}this.log(`🎬 BREAK TRIGGERED: "${s.break.id}"`),s.state=BreakState.ACTIVE,this.onBreakStart&&this.onBreakStart(s.break,{tuneIn:!1,elapsedSec:e/1e3,remainingSec:t/1e3});break}if(l>this.TIMING_TOLERANCE_MS){const t=o+1e3*(s.break.duration??0)-e;if(this.tuneInEnabled&&t>=this.minBreakDurationMs&&t>0){this.log(`🎬 TUNE-IN BREAK TRIGGERED: "${s.break.id}" (${Math.round(t/1e3)}s remaining)`),s.state=BreakState.ACTIVE,this.onBreakStart&&this.onBreakStart(s.break,{tuneIn:!0,elapsedSec:l/1e3,remainingSec:t/1e3});break}this.log(`⚠️ Break missed: "${s.break.id}" (was ${Math.round(l/1e3)}s ago)`),s.state=BreakState.SKIPPED,null===this.cueTracker&&this.retiredBreaks.set(s.break.id,s.break)}}}checkPreRollBreak(e,t,a){if(!this.rewind.playbackStarted)return;const i=1e3*t;if(this.rewind.adStartDelayMs>0&&i<this.rewind.adStartDelayMs)return this.log(`⚠️ Pre-roll skipped: "${e.break.id}" (delay ${t}s < adStartDelay ${this.rewind.adStartDelayMs/1e3}s)`),void(e.state=BreakState.SKIPPED);const r=this.rewind.channelPlayedMs,s=Math.round((i-r)/1e3);s>0&&s<=15&&s%2==0&&this.log(`⏱️ Pre-roll "${e.break.id}" starts in ${s}s`),s>0&&s<=this.PRELOAD_AHEAD_SECONDS&&this.maybePreload(e.break,`pre-roll, ${s}s ahead`),r>=i&&!a&&(this.log(`🎬 PRE-ROLL TRIGGERED: "${e.break.id}"${t>0?` (after ${t}s delay)`:""}`),e.state=BreakState.ACTIVE,this.onBreakStart&&this.onBreakStart(e.break,{tuneIn:!1,elapsedSec:0,remainingSec:e.break.duration??0}))}checkPostRollBreak(e,t,a){if(null===this.playbackEndedAtMs)return;const i=1e3*t,r=this.clock.now()-this.playbackEndedAtMs,s=Math.round((i-r)/1e3);s>0&&s<=this.PRELOAD_AHEAD_SECONDS&&this.maybePreload(e.break,`post-roll, ${s}s ahead`),r>=i&&!a&&(this.log(`🎬 POST-ROLL TRIGGERED: "${e.break.id}"${t>0?` (after ${t}s delay)`:""}`),e.state=BreakState.ACTIVE,this.onBreakStart&&this.onBreakStart(e.break,{tuneIn:!1,elapsedSec:0,remainingSec:e.break.duration??0}))}isAdStartDelayActive(){return this.rewind.isAdStartDelayActive()}getCurrentTime(){return b(this.timing,this.timingDeps)}maybePreload(e,t){if(this.preloadedBreakIds.has(e.id)||!this.onBreakApproaching)return;this.log(`⏳ Preloading break "${e.id}" (${t})`);!1!==this.onBreakApproaching(e)&&this.preloadedBreakIds.add(e.id)}getBreakStartTime(e){return s(e.start)?null:u(this.timing,this.timingDeps,e)}peekPosition(){if(this.timing.timebase!==t.WALLCLOCK)return 1e3*this.player.currentTime;const e=this.player.programDateTime?.getTime();return void 0===e||isNaN(e)?null:e}destroy(){this.stop(),this.breaks.clear(),this.preloadedBreakIds.clear(),this.completedBreakIds.clear(),this.retiredBreaks.clear(),this.minSeenTime=null,this.unsupportedEventBreakIds.clear(),this.playbackEndedAtMs=null,this.rewind.reset(),this.breakWarningFired.clear(),this.abortNotifiedBreakIds.clear(),this.timing.pdtMissingWarned=!1,this.timing.pdtSeen=!1,this.timing.firstPdtAttemptMs=null,this.timing.playbackStartedAtMs=null,this.timing.anvatoPendingDiagnosed.clear(),this.timing.anvatoMatchedDiagnosed.clear(),this.timing.anvatoEndDiagnosed.clear(),this.cueTracker?.clear()}checkWarnings(e,t){if(t<=0||0===this.breakWarnings.seconds.length)return;let a=this.breakWarningFired.get(e.id);a||(a=new Set,this.breakWarningFired.set(e.id,a));for(const i of this.breakWarnings.seconds)a.has(i)||t<=i&&(a.add(i),this.log(`Break warning for "${e.id}" (${t}s <= ${i}s)`),this.onBreakWarning?.(e,i,t))}log(...e){this.debug&&h("[OptiViewAds:BreakScheduler]",...e)}}
|
|
@@ -18,6 +18,7 @@ export declare class CmcdAdReporter {
|
|
|
18
18
|
private sessionKeys;
|
|
19
19
|
private activeBreakId;
|
|
20
20
|
private breakBeginTimestamp;
|
|
21
|
+
private breakAvailableMs;
|
|
21
22
|
private breakRequestedMs;
|
|
22
23
|
private firstAdReported;
|
|
23
24
|
/** Playable (non-companion) ad milliseconds delivered so far in the break. */
|
|
@@ -40,6 +41,9 @@ export declare class CmcdAdReporter {
|
|
|
40
41
|
private pendingAdStart;
|
|
41
42
|
private activeAdWatchedSec;
|
|
42
43
|
private activeAdDeclaredSec;
|
|
44
|
+
private activeAdIsVast;
|
|
45
|
+
/** The VAST creative duration the ad player resolved, from `adtimeupdate`. */
|
|
46
|
+
private activeAdResolvedSec;
|
|
43
47
|
private skipRequestedBreakId;
|
|
44
48
|
private abortedBreakId;
|
|
45
49
|
private cutBreakId;
|
|
@@ -84,6 +88,12 @@ export declare class CmcdAdReporter {
|
|
|
84
88
|
private onClick;
|
|
85
89
|
private onAdError;
|
|
86
90
|
private emitCustomWithAdContext;
|
|
91
|
+
/**
|
|
92
|
+
* The seconds the active ad fills: the VAST creative duration the ad player
|
|
93
|
+
* resolved, within the manifest-declared slot; the declared duration when
|
|
94
|
+
* the player resolved none.
|
|
95
|
+
*/
|
|
96
|
+
private activeAdFilledSec;
|
|
87
97
|
/**
|
|
88
98
|
* The ad keys for a milestone/click/error row: the full `as` key set when the
|
|
89
99
|
* event's asset is the active ad (same match rule as `adtimeupdate`),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{LensEventType as e,LensToken as t}from"../vendor/lens-sdk/index.js";import{AssetType as i,BreakFormat as s}from"../types/BreakManifest";import{VendorAdError as
|
|
1
|
+
import{LensEventType as e,LensToken as t}from"../vendor/lens-sdk/index.js";import{AssetType as i,BreakFormat as s,getBreakEvent as a}from"../types/BreakManifest";import{adErrorCode as d}from"../types/AdError";import{VendorAdError as r}from"../types/VendorAdError";import{SDK_VERSION as n}from"../generated/version";const h={[s.SINGLE]:"s",[s.DOUBLE]:"d",[s.LSHAPE_AD]:"la",[s.LSHAPE_CONTENT]:"lc",[s.OVERLAY]:"o"},o={[i.STATIC]:"s",[i.VAST]:"va",[i.VENDOR]:"ve"};function l(e){const t=a(e);if(null!==t)return t;const{start:i}=e;return"number"==typeof i?String(Math.round(1e3*i)):"string"==typeof i?i:i.event}function c(e){return o[e]??e}export class CmcdAdReporter{constructor(e,t){this.getHandle=e,this.log=t,this.sessionKeys={},this.activeBreakId=null,this.breakBeginTimestamp=null,this.breakAvailableMs=null,this.breakRequestedMs=null,this.firstAdReported=!1,this.breakFilledMs=0,this.breakWatchedMs=0,this.activeAdKeys=null,this.activeAdCompanion=null,this.activeAdAssetId=null,this.activeAdIsVendor=!1,this.activeAdBaseSec=0,this.breakLastTimeSec=0,this.pendingAdStart=null,this.activeAdWatchedSec=0,this.activeAdDeclaredSec=null,this.activeAdIsVast=!1,this.activeAdResolvedSec=null,this.skipRequestedBreakId=null,this.abortedBreakId=null,this.cutBreakId=null,this.erroredAssetId=null,this.finalizedBreakId=null}onSessionStart(e){this.sessionKeys={adv:n,admv:e.version,...e.organizationId?{adoid:e.organizationId}:{},...e.channelId?{adcid:e.channelId}:{},...e.eventId?{adeid:e.eventId}:{}},this.updateHeartbeat()}onSessionEnd(){this.finalizeActiveBreak(),this.sessionKeys={},this.resetBreakState(),this.guard("setHeartbeatPayload",e=>e.setHeartbeatPayload())}finalizeActiveBreak(){if(null===this.activeBreakId)return;const e=this.activeBreakId;this.emitPendingAdStart(null),null!==this.activeAdKeys&&this.emitAdEnd(this.activeAdKeys,this.activeAdCompanion,"a"),this.emitBreakEnd(e),this.finalizedBreakId=e}onSkipRequested(e){this.skipRequestedBreakId=e}onBreakAborted(e){this.abortedBreakId=e}onBreakCut(e){this.cutBreakId=e}onEvent(e){switch(e.type){case"adbreakbegin":this.onBreakBegin(e);break;case"adbegin":this.onAdBegin(e);break;case"adtimeupdate":{const t=e;this.breakLastTimeSec=Math.max(this.breakLastTimeSec,t.currentTime);null!==this.pendingAdStart&&(t.asset.id===this.pendingAdStart.assetId||this.pendingAdStart.isVendor&&t.break.id===this.activeBreakId)&&t.currentTime>(this.pendingAdStart?.isVendor?this.pendingAdStart.baseSec:0)&&null!==this.breakBeginTimestamp&&this.emitPendingAdStart(t.timestamp-this.breakBeginTimestamp);if(t.asset.id===this.activeAdAssetId||this.activeAdIsVendor&&t.break.id===this.activeBreakId){const e=t.currentTime-this.activeAdBaseSec;this.activeAdWatchedSec=Math.max(this.activeAdWatchedSec,e),this.activeAdIsVast&&Number.isFinite(t.duration)&&t.duration>0&&(this.activeAdResolvedSec=t.duration)}break}case"adend":this.onAdEnd(e);break;case"adbreakend":this.onBreakEnd(e);break;case"adfirstquartile":case"admidpoint":case"adthirdquartile":this.onMilestone(e);break;case"adclick":this.onClick(e);break;case"aderror":this.onAdError(e)}}onBreakBegin(i){this.resetBreakState(),this.finalizedBreakId=null,this.activeBreakId=i.break.id,this.breakBeginTimestamp=i.timestamp;const s=Math.round(1e3*i.remainingSec),a=this.requestedPlayableMs(i.break,i.format,s)??s;this.breakAvailableMs=s,this.breakRequestedMs=a,this.guard("abs",d=>{return d.emit(e.AD_BREAK_START,{custom:{...this.sessionKeys,brid:i.break.id,brst:l(i.break),brmd:Math.round(1e3*i.break.duration),brad:s,brrd:a,...i.format?{brf:new t((r=i.format,h[r]??r))}:{}}});var r}),this.updateHeartbeat()}onAdBegin(e){this.emitPendingAdStart(null);const s=e.asset,a=s.interaction?.clickThrough,d=!this.firstAdReported&&null!==this.breakBeginTimestamp;this.firstAdReported=!0;const r={brid:e.break.id,adasid:s.id,adid:e.adId??s.id,...void 0!==e.podPosition?{adpos:String(e.podPosition)}:{},...void 0!==s.duration?{adasd:Math.round(1e3*s.duration)}:{},adast:new t(c(s.type)),...a?{adcl:!0}:{},...e.creativeId?{adgcrid:e.creativeId}:{},...e.adId&&"string"==typeof s.uri&&"vendor"===s.type?{adgpid:s.uri}:{}};this.activeAdKeys=r,this.activeAdCompanion=s.companion?{id:s.companion.id,parentAssetId:s.id}:null,this.activeAdAssetId=s.id,this.activeAdIsVendor=s.type===i.VENDOR,this.activeAdBaseSec=this.activeAdIsVendor?this.breakLastTimeSec:0,this.activeAdWatchedSec=0,this.activeAdDeclaredSec=s.duration??null,this.activeAdIsVast=s.type===i.VAST,this.activeAdResolvedSec=null;const n={...this.sessionKeys,...r},h=s.companion?{...this.sessionKeys,brid:e.break.id,adasid:s.companion.id,adid:s.companion.id,adast:new t(c(s.companion.type)),adcomp:!0,adpasid:s.id}:null;d?this.pendingAdStart={assetId:s.id,custom:n,companionCustom:h,isVendor:this.activeAdIsVendor,baseSec:this.activeAdBaseSec}:this.emitAdStart(n,h),this.updateHeartbeat()}emitAdStart(t,i){this.guard("as",i=>i.emit(e.AD_START,{custom:t})),i&&this.guard("as",t=>t.emit(e.AD_START,{custom:i}))}emitPendingAdStart(e){const t=this.pendingAdStart;if(!t)return;this.pendingAdStart=null;const i=null!==e?{...t.custom,brttf:e}:t.custom;this.emitAdStart(i,t.companionCustom)}onAdEnd(e){if(e.break.id===this.finalizedBreakId)return;this.emitPendingAdStart(null);const t=e.asset,i=this.adEndReason(e),s=this.activeAdKeys??{brid:e.break.id,adasid:t.id,adid:e.adId??t.id},a=t.companion?{id:t.companion.id,parentAssetId:t.id}:null;this.emitAdEnd(s,a,i)}emitAdEnd(i,s,a){const d=this.activeAdFilledSec(),r=null!==d?Math.round(1e3*d):null,n=Math.round(1e3*this.activeAdWatchedSec),h=null!==r?Math.min(n,r):n;"s"===a&&this.guard("sk",t=>t.emit(e.AD_SKIP,{custom:{...this.sessionKeys,...i}})),this.guard("ae",s=>s.emit(e.AD_END,{custom:{...this.sessionKeys,...i,ader:new t(a),adwd:h}})),s&&this.guard("ae",d=>d.emit(e.AD_END,{custom:{...this.sessionKeys,brid:i.brid,adasid:s.id,adid:s.id,adcomp:!0,adpasid:s.parentAssetId,ader:new t(a)}})),this.breakWatchedMs+=h,this.breakFilledMs+=null!==r?r:h,this.activeAdKeys=null,this.activeAdCompanion=null,this.activeAdAssetId=null,this.activeAdIsVendor=!1,this.activeAdBaseSec=0,this.activeAdWatchedSec=0,this.activeAdDeclaredSec=null,this.activeAdIsVast=!1,this.activeAdResolvedSec=null,this.erroredAssetId===i.adasid&&(this.erroredAssetId=null),this.updateHeartbeat()}onBreakEnd(e){if(e.break.id!==this.finalizedBreakId){if(this.emitPendingAdStart(null),null!==this.breakAvailableMs){const t=this.requestedPlayableMs(e.break,e.format,this.breakAvailableMs);null!==t&&(this.breakRequestedMs=t)}this.emitBreakEnd(e.break.id)}}emitBreakEnd(t){const i=this.breakAvailableMs,s=null!==this.breakRequestedMs&&null!==i?Math.min(this.breakRequestedMs,i):this.breakRequestedMs,a=null!==s?Math.min(this.breakFilledMs,s):this.breakFilledMs,d=Math.min(this.breakWatchedMs,a);this.guard("abe",r=>r.emit(e.AD_BREAK_END,{custom:{...this.sessionKeys,brid:t,...null!==i?{brad:i}:{},...null!==s?{brrd:s}:{},brfd:a,brwd:d}})),this.resetBreakState(),this.updateHeartbeat()}onMilestone(e){this.emitPendingAdStart(null),this.emitCustomWithAdContext(e.type,e.break,e.asset)}onClick(e){this.emitPendingAdStart(null),this.emitCustomWithAdContext("adclick",e.break,e.asset)}onAdError(e){"ad"===e.source&&(this.emitPendingAdStart(null),e.asset&&(this.erroredAssetId=e.asset.id),this.guard("aderror",t=>t.emitCustom("aderror",{custom:{...this.sessionKeys,adec:e.asset?d(e.error):"DA-BREAK-NO-PLAYABLE-ASSET",...e.error instanceof r?{adevc:e.error.vendorCode}:{},...e.break&&e.asset?this.adContextKeys(e.break,e.asset):{...e.break?{brid:e.break.id}:{},...e.asset?{adasid:e.asset.id,adid:e.asset.id}:{}}}})))}emitCustomWithAdContext(e,t,i){this.guard(e,s=>s.emitCustom(e,{custom:{...this.sessionKeys,...this.adContextKeys(t,i)}}))}activeAdFilledSec(){const e=this.activeAdDeclaredSec,t=this.activeAdResolvedSec;return null===t?e:null===e?t:Math.min(t,e)}adContextKeys(e,t){return(t.id===this.activeAdAssetId||this.activeAdIsVendor&&e.id===this.activeBreakId)&&null!==this.activeAdKeys?{...this.activeAdKeys}:{brid:e.id,adasid:t.id,...void 0!==this.activeAdKeys?.adid?{adid:this.activeAdKeys.adid}:{}}}adEndReason(e){return this.skipRequestedBreakId===e.break.id?(this.skipRequestedBreakId=null,"s"):this.erroredAssetId===e.asset.id?"e":this.abortedBreakId===e.break.id?"a":this.cutBreakId===e.break.id?(this.cutBreakId=null,"a"):"c"}requestedPlayableMs(e,t,i){const s=Array.isArray(e.variant)?e.variant:[e.variant],a=s.find(e=>e.format===t)??s[0];if(!a)return null;if(0===a.assets.length)return null;const d=a.assets.map(e=>e.duration);if(d.some(e=>void 0===e||!Number.isFinite(e)||e<=0))return i;const r=d.reduce((e,t)=>e+(t??0),0);return Math.min(Math.round(1e3*r),i)}updateHeartbeat(){this.guard("setHeartbeatPayload",e=>e.setHeartbeatPayload({custom:{...this.sessionKeys,...this.activeBreakId?{brid:this.activeBreakId}:{},...void 0!==this.activeAdKeys?.adid?{adid:this.activeAdKeys.adid}:{}}}))}resetBreakState(){this.emitPendingAdStart(null),this.activeBreakId=null,this.breakBeginTimestamp=null,this.breakAvailableMs=null,this.breakRequestedMs=null,this.firstAdReported=!1,this.breakFilledMs=0,this.breakWatchedMs=0,this.activeAdKeys=null,this.activeAdCompanion=null,this.activeAdAssetId=null,this.activeAdIsVendor=!1,this.activeAdBaseSec=0,this.breakLastTimeSec=0,this.activeAdWatchedSec=0,this.activeAdDeclaredSec=null,this.activeAdIsVast=!1,this.activeAdResolvedSec=null,this.skipRequestedBreakId=null,this.abortedBreakId=null,this.cutBreakId=null,this.erroredAssetId=null}guard(e,t){const i=this.getHandle();if(i)try{t(i)}catch(t){this.log(`Lens CMCD ${e} failed:`,t)}}}
|
|
@@ -5,7 +5,8 @@ import { Asset } from '../types/BreakManifest';
|
|
|
5
5
|
* reports the real composition per ad at runtime via `StreamEvent.getAd().getAdPodInfo()`.
|
|
6
6
|
* Contract: every `adbegin` gets exactly one `adend`; a new ad closes the previous one even if
|
|
7
7
|
* IMA never reported completion; {@link finish} closes whatever is open; re-reporting the open
|
|
8
|
-
* ad is idempotent (IMA re-fires `STARTED` when a viewer seeks back into an ad).
|
|
8
|
+
* ad is idempotent (IMA re-fires `STARTED` when a viewer seeks back into an ad). IMA can split
|
|
9
|
+
* one pod into several ad breaks, so {@link onAdBreakEnded} only finishes once the last ad was seen.
|
|
9
10
|
*/
|
|
10
11
|
/** One IMA pod-ad report, normalized by the render layer so this module never sees an IMA object. */
|
|
11
12
|
export interface GamPodAdInfo {
|
|
@@ -51,15 +52,25 @@ export declare class GamPodAdTracker {
|
|
|
51
52
|
private openPosition;
|
|
52
53
|
private reportedAnyAd;
|
|
53
54
|
private finished;
|
|
55
|
+
private lastPosition;
|
|
56
|
+
private lastTotal;
|
|
54
57
|
constructor(podAsset: Asset);
|
|
55
58
|
/** The ad currently open, or null. Drives quartile attribution. */
|
|
56
59
|
get currentAd(): GamPodCurrentAd | null;
|
|
57
60
|
/** True once IMA reported at least one ad; while false the pod is still reported the legacy way. */
|
|
58
61
|
get sawAnyAd(): boolean;
|
|
62
|
+
/** 1-based position of the last ad IMA reported; 0 before any. */
|
|
63
|
+
get lastAdPosition(): number;
|
|
64
|
+
/** Ads in the pod per the last IMA report; 0 when IMA did not say. */
|
|
65
|
+
get lastTotalAds(): number;
|
|
66
|
+
/** True once IMA reported the pod's last ad, so anything after it is slate. */
|
|
67
|
+
get reportedLastAd(): boolean;
|
|
59
68
|
/** Closes the previous ad if it never completed, then opens the reported one. Idempotent for the open ad. */
|
|
60
69
|
onAdStarted(info: GamPodAdInfo): GamPodAdStep[];
|
|
61
70
|
/** IMA reported the open ad completing. No-op when nothing is open. */
|
|
62
71
|
onAdComplete(): GamPodAdStep[];
|
|
72
|
+
/** IMA ended an ad break while pod stream remains: close the open ad, and finish only after the last ad. */
|
|
73
|
+
onAdBreakEnded(): GamPodAdStep[];
|
|
63
74
|
/** Close whatever is open and refuse further reports, so a late IMA event cannot reopen a finished pod. */
|
|
64
75
|
finish(): GamPodAdStep[];
|
|
65
76
|
/** Emit the `adend` for the open ad, if any, and clear it. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function derivePodAdAsset(t,
|
|
1
|
+
export function derivePodAdAsset(t,i,s){return{...t,id:`${t.id}-ad-${i}`,..."number"==typeof s&&Number.isFinite(s)&&s>0?{duration:s}:{}}}function t(t,i){return"number"==typeof t&&Number.isFinite(t)?Math.max(i,Math.floor(t)):i}export class GamPodAdTracker{constructor(t){this.podAsset=t,this.open=null,this.openPosition=0,this.reportedAnyAd=!1,this.finished=!1,this.lastPosition=0,this.lastTotal=0}get currentAd(){return this.open}get sawAnyAd(){return this.reportedAnyAd}get lastAdPosition(){return this.lastPosition}get lastTotalAds(){return this.lastTotal}get reportedLastAd(){return this.lastTotal>0&&this.lastPosition>=this.lastTotal}onAdStarted(i){if(this.finished)return[];const s=function(t){if("number"!=typeof t||!Number.isFinite(t))return;const i=Math.floor(t);return i>=1?i:void 0}(i.adPosition),e=s??1;if(this.open&&this.openPosition===e)return[];const n=this.closeOpen(),o=e-1;return this.open={asset:derivePodAdAsset(this.podAsset,e,i.duration),adIndex:o,totalAds:t(i.totalAds,e),adPosition:s,adId:i.adId,creativeId:i.creativeId},this.openPosition=e,this.lastPosition=e,this.lastTotal=t(i.totalAds,0),this.reportedAnyAd=!0,n.push({type:"adbegin",...this.open}),n}onAdComplete(){return this.finished?[]:this.closeOpen()}onAdBreakEnded(){return this.finished?[]:this.reportedLastAd?this.finish():this.closeOpen()}finish(){if(this.finished)return[];const t=this.closeOpen();return this.finished=!0,t}closeOpen(){if(!this.open)return[];const t=[{type:"adend",...this.open}];return this.open=null,this.openPosition=0,t}}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import type { LensHandle } from '../vendor/lens-sdk/index.js';
|
|
1
|
+
import type { LensBatch, LensHandle } from '../vendor/lens-sdk/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* The Ads SDK's product identity in Lens: bound at join, spanning every session
|
|
4
4
|
* of this SDK instance. Lens destination filters key on it.
|
|
5
5
|
*/
|
|
6
6
|
export declare const LENS_NAMESPACE = "ads";
|
|
7
|
+
/** Batch built-in ingest reports without changing Lens's default heartbeat cadence. */
|
|
8
|
+
export declare const LENS_BATCH: LensBatch;
|
|
7
9
|
/**
|
|
8
10
|
* Ties Lens observability to the Ads SDK session lifecycle. Always on and not
|
|
9
11
|
* integrator-configurable — no public config option reaches this class.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Lens as s}from"../vendor/lens-sdk/index.js";import{configureLensBackend as e}from"./lensBackend";export const LENS_NAMESPACE="ads";export class LensReporter{constructor(s=()=>{}){this.log=s,this.lensHandle=null,this.joinFailed=!1,this.destroyed=!1}get handle(){return this.lensHandle}startSession(){const s=this.join();if(s)try{s.startSession()}catch(s){this.log("Lens session start failed:",s)}}endSession(){if(this.lensHandle)try{this.lensHandle.endSession()}catch(s){this.log("Lens session end failed:",s)}}destroy(){this.destroyed=!0;const s=this.lensHandle;if(this.lensHandle=null,s)try{s.dispose()}catch(s){this.log("Lens dispose failed:",s)}}join(){if(this.lensHandle)return this.lensHandle;if(this.destroyed||this.joinFailed)return null;try{e()}catch(s){this.log("Lens backend selection failed:",s)}try{this.lensHandle=s.join({namespace:"ads"})}catch(s){this.joinFailed=!0,this.log("Lens join failed:",s)}return this.lensHandle}}
|
|
1
|
+
import{Lens as s}from"../vendor/lens-sdk/index.js";import{configureLensBackend as e}from"./lensBackend";export const LENS_NAMESPACE="ads";export const LENS_BATCH={maxEvents:10,maxDelayMs:6e4};export class LensReporter{constructor(s=()=>{}){this.log=s,this.lensHandle=null,this.joinFailed=!1,this.destroyed=!1}get handle(){return this.lensHandle}startSession(){const s=this.join();if(s)try{s.startSession()}catch(s){this.log("Lens session start failed:",s)}}endSession(){if(this.lensHandle)try{this.lensHandle.endSession()}catch(s){this.log("Lens session end failed:",s)}}destroy(){this.destroyed=!0;const s=this.lensHandle;if(this.lensHandle=null,s)try{s.dispose()}catch(s){this.log("Lens dispose failed:",s)}}join(){if(this.lensHandle)return this.lensHandle;if(this.destroyed||this.joinFailed)return null;try{e()}catch(s){this.log("Lens backend selection failed:",s)}try{this.lensHandle=s.join({namespace:"ads",batch:LENS_BATCH})}catch(s){this.joinFailed=!0,this.log("Lens join failed:",s)}return this.lensHandle}}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { PlayerAdapter } from '../interfaces/PlayerAdapter';
|
|
2
|
+
import type { LiveStartPushbackConfig, PtsSource } from '../types/OptiViewAdsConfig';
|
|
3
|
+
/** Whether the startup adjustment applies, and which setting decided it. */
|
|
4
|
+
export interface LiveStartEnablement {
|
|
5
|
+
enabled: boolean;
|
|
6
|
+
enabledBy: 'config' | 'ptsSource';
|
|
7
|
+
}
|
|
8
|
+
/** An explicit `enabled` wins; otherwise the adjustment follows Anvato cue signaling. */
|
|
9
|
+
export declare function resolveLiveStartEnablement(config: LiveStartPushbackConfig | undefined, ptsSource: PtsSource | undefined): LiveStartEnablement;
|
|
10
|
+
interface LiveStartPushbackDeps {
|
|
11
|
+
blocked: () => boolean;
|
|
12
|
+
setSeeking: (active: boolean) => void;
|
|
13
|
+
diagnose: (code: string, message: string, context: Record<string, unknown>) => void;
|
|
14
|
+
}
|
|
15
|
+
export declare class LiveStartPushbackController {
|
|
16
|
+
private readonly player;
|
|
17
|
+
private readonly config;
|
|
18
|
+
private readonly deps;
|
|
19
|
+
private armed;
|
|
20
|
+
private playbackSeen;
|
|
21
|
+
private startOffsetRequested;
|
|
22
|
+
private generation;
|
|
23
|
+
private cleanupLanding;
|
|
24
|
+
private readonly enablement;
|
|
25
|
+
constructor(player: PlayerAdapter, config: LiveStartPushbackConfig | undefined, deps: LiveStartPushbackDeps, ptsSource?: PtsSource);
|
|
26
|
+
start(): void;
|
|
27
|
+
stop(): void;
|
|
28
|
+
private disarm;
|
|
29
|
+
private readonly onPlaying;
|
|
30
|
+
private readonly attempt;
|
|
31
|
+
/** The engine already began at the requested distance: report it measured, with no seek. */
|
|
32
|
+
private reportStartOffset;
|
|
33
|
+
private publishedEdge;
|
|
34
|
+
private skip;
|
|
35
|
+
}
|
|
36
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{contentSeekableEnd as e}from"./adPlayer/liveEdge";import{resolveLiveStartPushback as t}from"./resolveLiveStartPushback";export function resolveLiveStartEnablement(e,t){return"boolean"==typeof e?.enabled?{enabled:e.enabled,enabledBy:"config"}:{enabled:"anvatoCue"===t,enabledBy:"ptsSource"}}const i=e=>"number"==typeof e&&Number.isFinite(e)&&e>0,s=e=>Math.round(1e3*e)/1e3;export class LiveStartPushbackController{constructor(a,n,r,l){this.player=a,this.config=n,this.deps=r,this.armed=!1,this.playbackSeen=!1,this.startOffsetRequested=!1,this.generation=0,this.cleanupLanding=null,this.onPlaying=()=>{this.playbackSeen=!0,this.attempt()},this.attempt=()=>{if(!this.playbackSeen)return;if(!this.armed||this.deps.blocked()||this.player.paused||!1===this.player.playbackReady)return;const a=this.player.isLive??(this.player.duration===1/0||!Number.isFinite(this.player.duration)&&null);if(null===a)return;const n=this.startOffsetRequested;if(this.disarm(),this.startOffsetRequested=!1,!a)return;const r=this.player.targetDuration,l=i(r)?r:this.config?.targetDurationSeconds,o=this.config?.minimumDistanceTargetDurations??5,d=this.player.currentTime,u=e(this.player),h=this.publishedEdge(u),f=this.player.videoElement?.seekable,g=this.player.seekableStart??(f?.length?f.start(f.length-1):null),p={enabledBy:this.enablement.enabledBy,position:d,seekableStart:g,seekableEnd:u,liveEdge:h,liveEdgeSource:i(this.player.liveEdge)?"adapter":"seekable",targetDurationSec:i(l)?l:null,targetDurationSource:i(r)?"adapter":i(l)?"config":"unavailable",requestedOffsetTargetDurations:o,startOffsetRequested:n};let c=null;if(i(l)?i(o)&&Number.isFinite(l*o)?(null===h||null===g||!Number.isFinite(g)||h<=g||!Number.isFinite(d))&&(c="no-range"):c="invalid-config":c="no-target-duration",!c&&i(l)&&null!==h&&Object.assign(p,{initialOffsetSec:s(h-d),initialOffsetTargetDurations:s((h-d)/l),requestedOffsetSec:s(o*l)}),!this.enablement.enabled)return void this.skip("disabled",p);if(c||!i(l)||null===h||null===g)return void this.skip(c??"no-range",p);if(n&&h-d>=o*l-.25)return void this.reportStartOffset(d,h,l,o,p);const y=t(d,g,h,l,o);if(null===y)return void this.skip(h-d>=o*l?"already-behind":"no-backward-target",p);Object.assign(p,{target:y,rewindSec:s(d-y),rewindTargetDurations:s((d-y)/l),targetOffsetSec:s(h-y),targetOffsetTargetDurations:s((h-y)/l),windowClamped:y>h-o*l,method:"seek"});const b=this.generation;this.deps.setSeeking(!0);const m=t=>{if(b!==this.generation||!this.cleanupLanding)return;this.cleanupLanding();const i="seeked"===t?this.player.currentTime:null,a=this.publishedEdge(e(this.player)),n=null!==i&&Number.isFinite(i)&&null!==a?a-i:null;this.deps.diagnose("DA-LIVE-START-PUSHBACK-LANDED",`Live start landing: TD=${l}s, offset=${null===n?"unknown":`${s(n)}s (${s(n/l)}xTD)`}, ${t}`,{...p,reason:t,landed:i,liveEdgeAtLanding:a,landedOffsetSec:null===n?null:s(n),landedOffsetTargetDurations:null===n?null:s(n/l),errorSec:null===i?null:s(i-y)})},S=()=>m("seeked"),D=setTimeout(()=>m("timeout"),1e4);if(this.cleanupLanding=()=>{this.cleanupLanding=null,clearTimeout(D),this.player.off("seeked",S),this.deps.setSeeking(!1)},this.player.on("seeked",S),this.deps.diagnose("DA-LIVE-START-PUSHBACK",`Live start: TD=${l}s, offset=${p.initialOffsetSec}s (${p.initialOffsetTargetDurations}xTD), requested=${o*l}s (${o}xTD), rewind=${p.rewindSec}s (${p.rewindTargetDurations}xTD), target=${y}s`,p),b===this.generation)try{this.player.seek(y)}catch{m("seek-failed")}},this.enablement=resolveLiveStartEnablement(n,l)}start(){this.stop(),this.armed=!0,this.playbackSeen=!this.player.paused&&!1!==this.player.playbackReady;const e=this.config?.minimumDistanceTargetDurations??5,t=i(this.player.targetDuration)?this.player.targetDuration:i(this.config?.targetDurationSeconds)?this.config.targetDurationSeconds:null;this.startOffsetRequested=this.enablement.enabled&&i(e)&&!0===this.player.requestLiveStartOffset?.(e,t),this.player.on("playing",this.onPlaying),this.player.on("timeupdate",this.attempt),this.attempt()}stop(){this.generation++,this.disarm(),this.cleanupLanding?.()}disarm(){this.armed=!1,this.player.off("playing",this.onPlaying),this.player.off("timeupdate",this.attempt),this.startOffsetRequested&&this.player.requestLiveStartOffset?.(null,null)}reportStartOffset(e,t,i,a,n){const r=t-a*i,l=t-e,o={...n,target:s(r),rewindSec:0,rewindTargetDurations:0,targetOffsetSec:s(a*i),targetOffsetTargetDurations:s(a),windowClamped:!1,method:"start-offset"};this.deps.diagnose("DA-LIVE-START-PUSHBACK",`Live start: TD=${i}s, began ${s(l)}s (${s(l/i)}xTD) behind live via the engine start offset, requested=${s(a*i)}s (${a}xTD), no seek`,o),this.deps.diagnose("DA-LIVE-START-PUSHBACK-LANDED",`Live start landing: TD=${i}s, offset=${s(l)}s (${s(l/i)}xTD), start-offset`,{...o,reason:"start-offset",landed:e,liveEdgeAtLanding:t,landedOffsetSec:s(l),landedOffsetTargetDurations:s(l/i),errorSec:s(e-r)})}publishedEdge(e){const t=this.player.liveEdge;return!i(t)||t-this.player.currentTime>=86400?e:null===e?t:Math.max(t,e)}skip(e,t){this.deps.diagnose("DA-LIVE-START-PUSHBACK-SKIPPED",`Live start unchanged: ${e}, TD=${t.targetDurationSec??"unknown"}s, offset=${t.initialOffsetSec??"unknown"}s (${t.initialOffsetTargetDurations??"unknown"}xTD)`,{...t,reason:e})}}
|