@dolby-optiview/ads-sdk-core 1.3.0 → 1.5.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/dist/OptiViewAds.js +1 -1
- package/dist/OptiViewAdsExternalVendors.d.ts +32 -0
- package/dist/generated/diagnosticCodes.d.ts +16 -1
- 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 +2 -2
- package/dist/services/AdPlayerController.d.ts +4 -12
- package/dist/services/AdPlayerController.js +1 -1
- package/dist/services/BreakScheduler.d.ts +9 -17
- package/dist/services/BreakScheduler.js +1 -1
- package/dist/services/BreakSchedulerTypes.d.ts +4 -0
- package/dist/services/SchedulerSnapback.d.ts +32 -0
- package/dist/services/SchedulerSnapback.js +1 -0
- package/dist/services/SnapbackGuard.d.ts +36 -0
- package/dist/services/SnapbackGuard.js +1 -0
- package/dist/services/adPlayer/AdPlayerCallbacks.d.ts +2 -0
- package/dist/services/adPlayer/AdPlayerEvents.d.ts +1 -7
- package/dist/services/adPlayer/AdPlayerEvents.js +1 -1
- package/dist/services/adPlayer/AdPlayerState.d.ts +1 -1
- package/dist/services/adPlayer/AdPlayerState.js +1 -1
- package/dist/services/adPlayer/AssetPlayback.d.ts +3 -0
- package/dist/services/adPlayer/AssetPlayback.js +1 -1
- package/dist/services/adPlayer/AudioController.d.ts +58 -0
- package/dist/services/adPlayer/AudioController.js +1 -0
- package/dist/services/adPlayer/ContentResumeCoordinator.d.ts +2 -0
- package/dist/services/adPlayer/ContentResumeCoordinator.js +1 -1
- package/dist/services/adPlayer/SharedElementBreakPlayer.d.ts +3 -0
- package/dist/services/adPlayer/SharedElementBreakPlayer.js +1 -1
- package/dist/services/adPlayer/SurfaceRevealReporter.d.ts +28 -0
- package/dist/services/adPlayer/SurfaceRevealReporter.js +1 -0
- package/dist/services/checkEventBreak.d.ts +5 -0
- package/dist/services/checkEventBreak.js +1 -0
- package/dist/services/resolveResumePoint.d.ts +3 -1
- package/dist/services/resolveResumePoint.js +1 -1
- package/dist/session/BreakLifecycle.js +1 -1
- package/dist/session/SessionStarter.js +1 -1
- package/dist/session/contentSnapback.d.ts +5 -1
- package/dist/session/contentSnapback.js +1 -1
- package/dist/types/BreakManifest.d.ts +26 -5
- package/dist/types/Events.d.ts +1 -0
- package/dist/types/ExternalVendorConfiguration.d.ts +3 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{BreakFormat as e,Timebase as t,isPostRollBreak as s}from"../types/BreakManifest";import{DEFAULT_AD_BREAK_CUT_SAFETY_MARGIN_SEC as r,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{createDomPresentationHost as C,toPresentationSurfaces as v}from"./adPlayer/presentation/DomPresentationHost";import{AdPreloader as k}from"./adPlayer/AdPreloader";import{PresentationModeTracker as f}from"./adPlayer/PresentationModeTracker";import{SharedElementBreakPlayer as S}from"./adPlayer/SharedElementBreakPlayer";import{ContentResumeCoordinator as B}from"./adPlayer/ContentResumeCoordinator";import{resumeContentAfterBreak as b}from"./adPlayer/ContentRecovery";import{AssetPlayback as E}from"./adPlayer/AssetPlayback";import{BreakSequenceDriver as F}from"./adPlayer/BreakSequenceDriver";import{VariantSelector as M}from"./adPlayer/VariantSelector";import{AdPlayerEvents as T}from"./adPlayer/AdPlayerEvents";import{armPlaybackCutoff as R,clearPlaybackCutoff as I}from"./adPlayer/PlaybackCutoff";import{AdError as G}from"../types/AdError";import{SuccessorQueue as V}from"./adPlayer/SuccessorQueue";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,s,i,n=!1,o,d="parallel",l="overlay",h=r,u=a,b=!1,R=c,I="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=s??null,this.clock=R,this.singleSurfaceDevice=b,this.contentPlayer=e,this.adBreakCutSafetyMarginSec=h,this.doubleBoxAudio=u,this.container=t,s&&(this.playerContainer=s),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),buildStaticAssetUrl:(e,t)=>this.buildStaticAssetUrl(e,t),diagnose:(e,t,s)=>this.diagnose(e,t,s),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,s)=>this.diagnose(e,t,s),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,s,r,a)=>this.callbacks?.onAdStart(e,t,s,r,a),emitAdEnd:(e,t,s,r,a)=>this.callbacks?.onAdEnd(e,t,s,r,a),onAdEnded:()=>this.onAdEnded(),pushAssetParameters:(e,t)=>this.pushAssetParameters(e,t),assetParameterResolver:this.assetParameters,diagnose:(e,t,s)=>this.diagnose(e,t,s),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,buildStaticAssetUrl:(e,t)=>this.buildStaticAssetUrl(e,t),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),presentationHost:C(()=>v(this.playerContainer,this.adContainer,this.stage))}),this.preloader=new k({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),buildStaticAssetUrl:(e,t)=>this.buildStaticAssetUrl(e,t),resolveAssetUri:(e,t,s)=>this.resolveAssetUri(e,t,s),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,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e)}),this.presentationModes=new f(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:()=>this.restoreLayout(),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,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e)}),this.resumeCoordinator=new B(this.state,{contentPlayer:this.contentPlayer,clock:this.clock,getInsertionMode:()=>this.effectiveInsertionMode(),gateContentForBreak:()=>this.gateContentForBreak(),clearContentGate:e=>this.clearContentGate(e),diagnose:(e,t,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e)}),this.recoveryDeps={contentPlayer:e,getCallbacks:()=>this.callbacks,preloadMode:d,contentRecovery:I,resumeCoordinator:this.resumeCoordinator,diagnose:(e,t,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e)},this.sharedElementPlayer=new S(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,s)=>this.diagnose(e,t,s),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 M(this.state,{getEffectiveInsertionMode:()=>this.effectiveInsertionMode(),singleSurfaceDevice:this.singleSurfaceDevice,preloadMode:this.preloadMode,getIsContentPictureInPicture:()=>this.isContentPictureInPicture(),getIsGamReady:()=>this.isGamReady(),getSupportsParallelBuffering:()=>this.contentPlayer.supportsParallelBuffering,diagnose:(e,t,s)=>this.diagnose(e,t,s)}),this.successorQueue=new V({getAdPlayer:()=>this.adPlayer,isEnabled:()=>"parallel"===this.preloadMode&&"overlay"===this.effectiveInsertionMode()&&!this.state.sharedAdClaimActive,buildStaticAssetUrl:(e,t)=>this.buildStaticAssetUrl(e,t),getCallbacks:()=>this.state.callbacks,log:(...e)=>this.log(...e)}),this.assetPlayback=new E(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,buildStaticAssetUrl:(e,t)=>this.buildStaticAssetUrl(e,t),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),takeQueuedSuccessor:(e,t,s)=>this.successorQueue.take(e,t,s),queueUpcomingAd:()=>{const e=this.sequencer?.upcomingAsset();e&&this.currentBreak&&this.successorQueue.prepare(this.currentBreak,e,!1)},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,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e)}),this.breakSequenceDriver=new F(this.state,{successorQueue:this.successorQueue,getAssets:e=>this.getAssets(e),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,s)=>this.playAsset(e,t,s),endBreak:()=>this.endBreak(),playBreak:(e,t,s)=>this.playBreak(e,t,s),diagnose:(e,t,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e)}),this.adPlayerEvents=new T(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},dropQueuedAd:()=>this.successorQueue.drop(),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,s){this.diagnoseHandler?.(e,t,s)}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}onChainSuccessorChanged(){this.sequencer&&this.currentBreak&&this.isPlaying&&this.drive(this.sequencer.onOutcome({kind:"successor-changed"}),!0)}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,s=!1){this.adInsertionType=e,this.breakTimebase=t,this.state.continueContentDuringBreak=s,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)}resolveExternalVendorConfiguration(e){return this.assetParameters.resolveExternalVendorConfiguration(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)}buildStaticAssetUrl(e,t){return this.assetParameters.buildStaticAssetUrl(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,r,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,r);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=r,this.isPlaying=!0,this.firstFrameGateActive=a?.firstFrameGate??!1,this.firstFrameGateShown=!1;const h=this.continueContentDuringBreakReason(l.format),u=s(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;R(this.state,e,()=>{this.log("Break-cut timer expired, ending break"),this.firstFrameRejector?this.onAdError(new G("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 G("Ad playback startup timed out","DA-AD-START-TIMEOUT")))}this.sequencer=new o("overlay",()=>null,this.selectionInputs(),e=>this.resolveChainedSuccessor(e)),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:s}=h(e,this.selectionInputs());return(t??s)?.format??l(e)}getVariant(e){return this.variantSelector.getVariant(e)}applyLayout(e){this.adStage.applyLayout(e)}restoreLayout(){this.adStage.restoreLayout()}getAssets(e){return d(this.getVariant(e))}async playAsset(e,t,s){return this.assetPlayback.playAsset(e,t,s)}clearOverlayImage(){this.adStage.clearOverlayImage()}async showPauseImage(e){return this.pauseAdPresenter.showPauseImage(e)}hidePauseImage(){this.pauseAdPresenter.hidePauseImage()}async drive(e,t=!1){return this.breakSequenceDriver.drive(e,t)}emitStep(e){this.breakSequenceDriver.emitStep(e)}async resolveAssetUri(e,t,s){return this.assetPlayback.resolveAssetUri(e,t,s)}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),I(this.state),this.state.gamPodTracker&&this.emitGamPodSteps(this.state.gamPodTracker.finish()),this.resetGamPodTracking(),this.pushAssetParameters(void 0);const e=this.currentBreak;let r=this.abortingBreak?null:this.resolveChainedSuccessor(e.id);if(r){const e=this.breakNothingToPlayReason(r);this.diagnoseUnknownVariantFormats(r),e&&(this.diagnose("DA-BREAK-NO-PLAYABLE-ASSET",`chained break ${r.id} has no playable variant (${e}); it will not begin.`,{breakId:r.id,reason:e}),this.callbacks?.onAdError(r,void 0,new G(`Break ${r.id} has no playable variant (${e})`,"DA-BREAK-NO-PLAYABLE-ASSET")),this.callbacks?.onChainedBreakSkipped?.(r),r=null)}if(r)return void this.advanceToChainedBreak(e,r);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.successorQueue.reset(),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(),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 a="recreate"===this.recoveryDeps.contentRecovery&&"function"==typeof this.contentPlayer.recreateMediaPipeline,i="single-decoder"===this.preloadMode||a?this.adPlayer?.unload?.():void 0;if(this.contentPaused&&s(e))this.contentPaused=!1,this.clearContentGate();else if(this.contentPaused){this.contentPaused=!1,this.clearContentGate(this.resumeHandoverInMotion);const s=this.resumeGeneration;b(this.recoveryDeps,{breakInfo:e,earlyReturnOffsetSec:this.state.earlyReturnResumeOffsetSec,isLiveResume:this.breakTimebase===t.WALLCLOCK,released:i,isCurrent:()=>s===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),I(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,I(this.state),this.sequencer&&this.currentBreak?this.drive(this.sequencer.onOutcome({kind:"break-elapsed"})):this.endBreak())}stopActiveBreak(){g(this.state),this.successorQueue.reset(),this.sharedElementPlayer.cancelSharedRestore()&&this.isPlaying&&(this.log("Aborting active break on session end:",this.currentBreak?.id),I(this.state),this.sharedElementPlayer.detachSharedAdListeners(),this.sharedElementPlayer.releaseContentElementAdPlayer(),this.adPlayer?.pause(),this.clearOverlayImage(),this.vastAdManager?.reset(),this.hide(),this.restoreLayout(),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.successorQueue.reset(),!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(),I(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;
|
|
1
|
+
import{BreakFormat as e,Timebase as t,isPostRollBreak as s}from"../types/BreakManifest";import{DEFAULT_AD_BREAK_CUT_SAFETY_MARGIN_SEC as r,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 h}from"./AdBreakSequencer";import{selectVariantDetailed as l}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 p}from"./adPlayer/AssetParameterResolver";import{GamPodBridge as y}from"./adPlayer/GamPodBridge";import{AdStage as A}from"./adPlayer/AdStage";import{createDomPresentationHost as C,toPresentationSurfaces as k}from"./adPlayer/presentation/DomPresentationHost";import{AdPreloader as v}from"./adPlayer/AdPreloader";import{PresentationModeTracker as f}from"./adPlayer/PresentationModeTracker";import{SharedElementBreakPlayer as S}from"./adPlayer/SharedElementBreakPlayer";import{ContentResumeCoordinator as B}from"./adPlayer/ContentResumeCoordinator";import{resumeContentAfterBreak as b}from"./adPlayer/ContentRecovery";import{AssetPlayback as E}from"./adPlayer/AssetPlayback";import{SurfaceRevealReporter as T}from"./adPlayer/SurfaceRevealReporter";import{BreakSequenceDriver as M}from"./adPlayer/BreakSequenceDriver";import{VariantSelector as R}from"./adPlayer/VariantSelector";import{AdPlayerEvents as F}from"./adPlayer/AdPlayerEvents";import{AudioController as I}from"./adPlayer/AudioController";import{armPlaybackCutoff as G,clearPlaybackCutoff as w}from"./adPlayer/PlaybackCutoff";import{AdError as D}from"../types/AdError";import{SuccessorQueue as V}from"./adPlayer/SuccessorQueue";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,s,i,n=!1,o,d="parallel",h="overlay",l=r,u=a,b=!1,G=c,w="none"){this.playerContainerCreatedBySDK=!1,this.adMediaContainer=null,this.adPlayer=null,this.surfaceReveal=new T((e,t,s)=>this.diagnose(e,t,s)),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.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.state=m(),this.state.playerContainer=s??null,this.clock=G,this.singleSurfaceDevice=b,this.contentPlayer=e,this.audio=new I({contentPlayer:e,isAdPlaying:()=>this.isPlaying,getVastAdManager:()=>this.vastAdManager,onVolumeChange:(e,t)=>this.volumeChangeCallback?.(e,t),log:(...e)=>this.log(...e)}),this.adBreakCutSafetyMarginSec=l,this.doubleBoxAudio=u,this.container=t,s&&(this.playerContainer=s),this.createAdAdapter=i,this.debug=n,this.state.gamConfig=o,this.preloadMode=d,this.insertionMode=h,this.pauseAdPresenter=new P({contentPlayer:this.contentPlayer,getStage:()=>this.stage,resolveUri:e=>this.resolveUri(e),buildStaticAssetUrl:(e,t)=>this.buildStaticAssetUrl(e,t),diagnose:(e,t,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e)}),this.assetParameters=new p(this.state,{contentPlayer:this.contentPlayer,container:this.container,getGamStreamManager:()=>this.state.gamStreamManager,getCurrentBreak:()=>this.currentBreak,getEffectiveBreakDuration:()=>this.effectiveBreakDurationSec,diagnose:(e,t,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e)}),this.gamPodBridge=new y(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,s,r,a)=>this.callbacks?.onAdStart(e,t,s,r,a),emitAdEnd:(e,t,s,r,a)=>this.callbacks?.onAdEnd(e,t,s,r,a),onAdEnded:()=>this.onAdEnded(),pushAssetParameters:(e,t)=>this.pushAssetParameters(e,t),assetParameterResolver:this.assetParameters,diagnose:(e,t,s)=>this.diagnose(e,t,s),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,e&&this.audio.attachAdPlayer(e)},createAdAdapter:e=>this.createAdAdapter(e),setupEventListeners:()=>this.setupEventListeners(),resizeVast:()=>this.vastAdManager?.resize(),getVariant:e=>this.getVariant(e),getCurrentBreak:()=>this.currentBreak,buildStaticAssetUrl:(e,t)=>this.buildStaticAssetUrl(e,t),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),presentationHost:C(()=>k(this.playerContainer,this.adContainer,this.stage))}),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),buildStaticAssetUrl:(e,t)=>this.buildStaticAssetUrl(e,t),resolveAssetUri:(e,t,s)=>this.resolveAssetUri(e,t,s),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,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e)}),this.presentationModes=new f(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:()=>this.restoreLayout(),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,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e)}),this.resumeCoordinator=new B(this.state,{contentPlayer:this.contentPlayer,clock:this.clock,getInsertionMode:()=>this.effectiveInsertionMode(),gateContentForBreak:()=>this.gateContentForBreak(),clearContentGate:e=>this.clearContentGate(e),muteContent:()=>this.audio.mute("content"),diagnose:(e,t,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e)}),this.recoveryDeps={contentPlayer:e,getCallbacks:()=>this.callbacks,preloadMode:d,contentRecovery:w,resumeCoordinator:this.resumeCoordinator,diagnose:(e,t,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e)},this.sharedElementPlayer=new S(this.state,{contentPlayer:this.contentPlayer,getAdPlayer:()=>this.adPlayer,surfaceReveal:this.surfaceReveal,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,s)=>this.diagnose(e,t,s),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 R(this.state,{getEffectiveInsertionMode:()=>this.effectiveInsertionMode(),singleSurfaceDevice:this.singleSurfaceDevice,preloadMode:this.preloadMode,getIsContentPictureInPicture:()=>this.isContentPictureInPicture(),getIsGamReady:()=>this.isGamReady(),getSupportsParallelBuffering:()=>this.contentPlayer.supportsParallelBuffering,diagnose:(e,t,s)=>this.diagnose(e,t,s)}),this.successorQueue=new V({getAdPlayer:()=>this.adPlayer,isEnabled:()=>"parallel"===this.preloadMode&&"overlay"===this.effectiveInsertionMode()&&!this.state.sharedAdClaimActive,buildStaticAssetUrl:(e,t)=>this.buildStaticAssetUrl(e,t),getCallbacks:()=>this.state.callbacks,log:(...e)=>this.log(...e)}),this.assetPlayback=new E(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,buildStaticAssetUrl:(e,t)=>this.buildStaticAssetUrl(e,t),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(),surfaceReveal:this.surfaceReveal,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),takeQueuedSuccessor:(e,t,s)=>this.successorQueue.take(e,t,s),queueUpcomingAd:()=>{const e=this.sequencer?.upcomingAsset();e&&this.currentBreak&&this.successorQueue.prepare(this.currentBreak,e,!1)},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,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e)}),this.breakSequenceDriver=new M(this.state,{successorQueue:this.successorQueue,getAssets:e=>this.getAssets(e),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,s)=>this.playAsset(e,t,s),endBreak:()=>this.endBreak(),playBreak:(e,t,s)=>this.playBreak(e,t,s),diagnose:(e,t,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e)}),this.adPlayerEvents=new F(this.state,{getAdPlayer:()=>this.adPlayer,getContentPlayer:()=>this.contentPlayer,getGamEventCallbacks:()=>this.gamEventCallbacks,getSequencer:()=>this.sequencer,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},dropQueuedAd:()=>this.successorQueue.drop(),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,s){this.diagnoseHandler?.(e,t,s)}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}onChainSuccessorChanged(){this.sequencer&&this.currentBreak&&this.isPlaying&&this.drive(this.sequencer.onOutcome({kind:"successor-changed"}),!0)}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,s=!1){this.adInsertionType=e,this.breakTimebase=t,this.state.continueContentDuringBreak=s,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)}resolveExternalVendorConfiguration(e){return this.assetParameters.resolveExternalVendorConfiguration(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)}buildStaticAssetUrl(e,t){return this.assetParameters.buildStaticAssetUrl(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?h(this.currentBreak):void 0)}async playBreak(t,r,a){if(this.state.preloadGeneration++,this.resumeGeneration++,!this.sharedElementPlayer.beginSharedActivity())return;this.state.snapbackResumePosition=a?.snapbackResumePosition,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,r);if(!this.adPlayer)return void this.log("playBreak called before initialize()");const h=this.getVariant(t);if(!h)return void this.log("No valid variant found for break:",t.id);this.currentBreak=t,this.callbacks=r,this.isPlaying=!0,this.firstFrameGateActive=a?.firstFrameGate??!1,this.firstFrameGateShown=!1;const l=this.continueContentDuringBreakReason(h.format),u=s(t),c=this.shouldPauseContent(h.format)&&"ok"===l&&!u;c||u||this.state.continueRefusalReported||!this.shouldPauseContent(h.format)||(this.state.continueRefusalReported=!0,this.diagnose("DA-CONTINUE-PLAYOUT-REFUSED",`Content will be paused for breaks this session: ${l}`,{reason:l,format:h.format})),h.format===e.DOUBLE?this.audio.mute("ad"===this.doubleBoxAudio?"content":"ad"):c?this.audio.mute("content"):this.shouldPauseContent(h.format)&&this.audio.unmute("ad");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(h.format)?(this.resumeCoordinator.captureBreakAnchor(t),c?(this.contentPaused=!1,this.resumeCoordinator.beginContinueContentPlayout()):(this.contentPaused=!0,this.gateContentForBreak())):this.contentPaused=!1,this.applyLayout(h),h.format===e.LSHAPE_CONTENT)return this.renderLshapeContentBackdrop(h),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(h));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;G(this.state,e,()=>{this.log("Break-cut timer expired, ending break"),this.firstFrameRejector?this.onAdError(new D("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 D("Ad playback startup timed out","DA-AD-START-TIMEOUT")))}this.sequencer=new o("overlay",()=>null,this.selectionInputs(),e=>this.resolveChainedSuccessor(e)),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:s}=l(e,this.selectionInputs());return(t??s)?.format??h(e)}getVariant(e){return this.variantSelector.getVariant(e)}applyLayout(e){this.adStage.applyLayout(e)}restoreLayout(){this.adStage.restoreLayout()}getAssets(e){return d(this.getVariant(e))}async playAsset(e,t,s){return this.assetPlayback.playAsset(e,t,s)}clearOverlayImage(){this.adStage.clearOverlayImage()}async showPauseImage(e){return this.pauseAdPresenter.showPauseImage(e)}hidePauseImage(){this.pauseAdPresenter.hidePauseImage()}async drive(e,t=!1){return this.breakSequenceDriver.drive(e,t)}emitStep(e){this.breakSequenceDriver.emitStep(e)}async resolveAssetUri(e,t,s){return this.assetPlayback.resolveAssetUri(e,t,s)}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),w(this.state),this.state.gamPodTracker&&this.emitGamPodSteps(this.state.gamPodTracker.finish()),this.resetGamPodTracking(),this.pushAssetParameters(void 0);const e=this.currentBreak;let r=this.abortingBreak?null:this.resolveChainedSuccessor(e.id);if(r){const e=this.breakNothingToPlayReason(r);this.diagnoseUnknownVariantFormats(r),e&&(this.diagnose("DA-BREAK-NO-PLAYABLE-ASSET",`chained break ${r.id} has no playable variant (${e}); it will not begin.`,{breakId:r.id,reason:e}),this.callbacks?.onAdError(r,void 0,new D(`Break ${r.id} has no playable variant (${e})`,"DA-BREAK-NO-PLAYABLE-ASSET")),this.callbacks?.onChainedBreakSkipped?.(r),r=null)}if(r)return void this.advanceToChainedBreak(e,r);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.successorQueue.reset(),this.firstFrameGateActive=!1,this.firstFrameGateShown=!1,this.clearFirstFrame("Break ended before first frame"),this.audio.unmuteAll(),this.clearOverlayImage(),this.vastAdManager?.reset(),this.hide(),this.restoreLayout(),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 a="recreate"===this.recoveryDeps.contentRecovery&&"function"==typeof this.contentPlayer.recreateMediaPipeline,i="single-decoder"===this.preloadMode||a?this.adPlayer?.unload?.():void 0,n=!this.contentPaused&&void 0!==this.state.snapbackResumePosition;if(this.contentPaused&&s(e))this.contentPaused=!1,this.clearContentGate();else if(this.contentPaused){this.contentPaused=!1,this.clearContentGate(this.resumeHandoverInMotion);const s=this.resumeGeneration;b(this.recoveryDeps,{breakInfo:e,earlyReturnOffsetSec:this.state.earlyReturnResumeOffsetSec,isLiveResume:this.breakTimebase===t.WALLCLOCK,released:i,isCurrent:()=>s===this.resumeGeneration})}this.abortingBreak=!1,this.state.earlyReturnResumeOffsetSec=null;const o=this.resumeGeneration;this.callbacks?.onBreakEnd(e),n&&o===this.resumeGeneration&&!s(e)&&this.resumeCoordinator.applyResumeSeek(e)}currentAdAsset(){return this.isPlaying?this.currentAsset:null}skipBreak(){this.isPlaying&&(this.log("Skipping break:",this.currentBreak?.id),w(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,w(this.state),this.sequencer&&this.currentBreak?this.drive(this.sequencer.onOutcome({kind:"break-elapsed"})):this.endBreak())}stopActiveBreak(){g(this.state),this.successorQueue.reset(),this.sharedElementPlayer.cancelSharedRestore()&&this.isPlaying&&(this.log("Aborting active break on session end:",this.currentBreak?.id),w(this.state),this.sharedElementPlayer.detachSharedAdListeners(),this.sharedElementPlayer.releaseContentElementAdPlayer(),this.adPlayer?.pause(),this.clearOverlayImage(),this.vastAdManager?.reset(),this.hide(),this.restoreLayout(),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.audio.unmuteAll())}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(),this.surfaceReveal.surfaceShown()}hide(){this.adStage.hide(),this.surfaceReveal.surfaceHidden()}isAdPlaying(){return this.isPlaying}get muted(){return this.audio.muted}set muted(e){this.audio.muted=e}get volume(){return this.audio.volume}set volume(e){this.audio.volume=e}setVolumeChangeCallback(e){this.volumeChangeCallback=e}destroy(){if(this.destroyed)return;if(this.resumeGeneration++,g(this.state),this.successorQueue.reset(),!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(),w(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.audio.destroy(),this.sharedElementPlayer.detachSharedAdListeners(),this.sharedElementPlayer.releaseContentElementAdPlayer(),this.state.pipTransferredToAd=!1,this.teardownFullscreenTracking(),this.teardownPictureInPictureTracking(),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;
|
|
@@ -3,6 +3,7 @@ import type { AdBreakSkipReason } from '../types/Events';
|
|
|
3
3
|
import { Break, BreakManifest } from '../types/BreakManifest';
|
|
4
4
|
import { type ResolvedBreakWarnings } from '../types/OptiViewAdsConfig';
|
|
5
5
|
import type { MetadataCueTracker } from './MetadataCueTracker';
|
|
6
|
+
import type { SnapbackRequest } from './SnapbackGuard';
|
|
6
7
|
import { type ChainSuccessorChangedCallback } from './BreakChain';
|
|
7
8
|
import { type BreakAbortedCallback, type BreakApproachingCallback, type BreakStartCallback, type BreakWarningCallback, type Clock, type SchedulerDiagnose, type TuneInOptions } from './BreakSchedulerTypes';
|
|
8
9
|
export { BreakState, systemClock, type BreakAbortedCallback, type BreakApproachingCallback, type BreakStartCallback, type BreakStartContext, type BreakWarningCallback, type Clock, type SchedulerDiagnose, type TuneInOptions, } from './BreakSchedulerTypes';
|
|
@@ -70,6 +71,7 @@ export declare class BreakScheduler {
|
|
|
70
71
|
* close to the start the join landed. The minimum keeps the DVR contract: seeking back re-earns it.
|
|
71
72
|
*/
|
|
72
73
|
private minSeenTime;
|
|
74
|
+
private readonly snapback;
|
|
73
75
|
/**
|
|
74
76
|
* Slack for the join discriminator: the first tick lags session start by up to one interval,
|
|
75
77
|
* so a break missed by less than this is boundary noise. A genuine join lands seconds past the start.
|
|
@@ -163,6 +165,11 @@ export declare class BreakScheduler {
|
|
|
163
165
|
notifyResumeSeek(targetSeconds: number, breakEndSeconds: number): void;
|
|
164
166
|
/** A viewer seek through the SDK: drop the resume-seek guard so the step is judged by the normal rules. */
|
|
165
167
|
notifyViewerSeek(): void;
|
|
168
|
+
setSnapbackOnSeek(enabled: boolean, canPlay?: (brk: Break) => boolean): void;
|
|
169
|
+
setOnSnapback(callback: ((request: SnapbackRequest) => void) | null): void;
|
|
170
|
+
planSeek(target: number): SnapbackRequest | null;
|
|
171
|
+
notifySeeked(): void;
|
|
172
|
+
private startBreak;
|
|
166
173
|
private skipBreak;
|
|
167
174
|
private cleanupBreaks;
|
|
168
175
|
getActiveBreak(): Break | null;
|
|
@@ -173,32 +180,17 @@ export declare class BreakScheduler {
|
|
|
173
180
|
id: string;
|
|
174
181
|
secondsUntil: number;
|
|
175
182
|
} | null;
|
|
176
|
-
/**
|
|
177
|
-
* Advance the schedule NOW because a cue reached the playhead: the 250ms tick discovers a start
|
|
178
|
-
* ~125ms late on average, an adapter reporting PRESENTATION knows the instant. Runs
|
|
179
|
-
* {@link checkBreaks} so every tick guard applies unchanged, including the timing check against
|
|
180
|
-
* the tracker's media time: a segment-anchored emsg cue presenting at its segment start is ignored.
|
|
181
|
-
* Returns the id of the break this armed, or null.
|
|
182
|
-
*/
|
|
183
|
+
/** Check the schedule on cue presentation. Returns the break armed by this cue, or null. */
|
|
183
184
|
notifyCuePresented(pts: number): string | null;
|
|
184
185
|
private checkBreaks;
|
|
185
|
-
/** Pre-rolls fire once `delay` seconds of content have PLAYED since playback start (spec `event: 'start'`). */
|
|
186
186
|
private checkPreRollBreak;
|
|
187
|
-
/**
|
|
188
|
-
* Post-rolls fire `delay` seconds of wallclock after content playback ended (spec `event: 'end'`).
|
|
189
|
-
* An ad-free start still active at that moment keeps the break pending, not consumed.
|
|
190
|
-
*/
|
|
191
187
|
private checkPostRollBreak;
|
|
192
|
-
/** While true no break may start and consumers must not apply break-driven restrictions (e.g. pause ads). */
|
|
193
188
|
isAdStartDelayActive(): boolean;
|
|
194
189
|
private getCurrentTime;
|
|
195
190
|
/** Retry rejected callbacks; an accepted preload fires at most once per break. */
|
|
196
191
|
private maybePreload;
|
|
197
192
|
private getBreakStartTime;
|
|
198
|
-
/**
|
|
199
|
-
* Side-effect-free read of the timebase position (no PDT grace bookkeeping, no clock
|
|
200
|
-
* fallback). Used to anchor played time when playback starts.
|
|
201
|
-
*/
|
|
193
|
+
/** Timebase position without PDT grace bookkeeping or a clock fallback. */
|
|
202
194
|
private peekPosition;
|
|
203
195
|
destroy(): void;
|
|
204
196
|
/** Fire configured pre-break warnings, each threshold at most once and only while `secondsUntil > 0`. Pre-rolls excluded. */
|
|
@@ -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 h}from"../types/OptiViewAdsConfig";import{PTS_MATCH_TOLERANCE_SECONDS as l}from"./MetadataCueTracker";import{debugLog as o}from"../diagnostics/debugLog";import{createBreakTimingState as k,describeBreakStart as c,diagnoseAnvatoBreakEnd as u,getBreakStartTime as b,getCurrentTime as g,secondsUntilBreak as S}from"./breakTiming";import{BreakPreloadLedger as m}from"./BreakPreloadLedger";import{skipBreaksStartedBy as p,soonestBreak as B}from"./ssbStream";import{RewindRearm as E}from"./RewindRearm";import{BreakChain as f}from"./BreakChain";import{BreakState as D,systemClock as I}from"./BreakSchedulerTypes";export{BreakState,systemClock}from"./BreakSchedulerTypes";export const TICK_INTERVAL_MS=250;export class BreakScheduler{constructor(e,t=!1,a={enabled:!1,maxGapSeconds:0},i=I,r={enabled:!1,minBreakDurationSeconds:0},s=null,n=d,l=null,o=h){this.breaks=new Map,this.timing=k(),this.checkInterval=null,this.startDeadline=null,this.nextStartDeadlineMs=null,this.onBreakStart=null,this.onBreakApproaching=null,this.onBreakWarning=null,this.onBreakAborted=null,this.onBreakSkipped=null,this.beforeBreakCheck=null,this.breakStartsHeld=!1,this.preloadLedger=new m,this.onAdStartDelayElapsed=null,this.adStartDelayElapsedNotified=!1,this.abortNotifiedBreakIds=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.chain=new f({breaks:this.breaks,enabled:a.enabled,maxGapMs:1e3*Math.max(0,a.maxGapSeconds),toleranceMs:this.TIMING_TOLERANCE_MS,getBreakStartTime:e=>this.getBreakStartTime(e),log:(...e)=>this.log(...e)}),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,o);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 E({breaks:this.breaks,retiredBreaks:this.retiredBreaks,completedBreakIds:this.completedBreakIds,preloadLedger:this.preloadLedger,timing:this.timing,clock:this.clock,log:(...e)=>this.log(...e),getBreakStartTime:e=>this.getBreakStartTime(e)})}claimChain(e){this.chain.claim(e)}getChainedSuccessor(e){return this.chain.successorOf(e)}activateChainedBreak(e){this.chain.activate(e)}setOnChainSuccessorChanged(e){this.chain.setOnSuccessorChanged(e)}updateManifest(e){this.timing.timebase=e.timebase;const t=this.rewind.adStartDelayMs;this.rewind.setAdStartDelayMs(1e3*Math.max(0,e.adStartDelay??0)),t>0&&0===this.rewind.adStartDelayMs&&this.notifyAdStartDelayElapsed();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!==D.PENDING&&e.state!==D.CHAINED||(e.break=t):(this.breaks.set(t.id,{break:t,state:D.PENDING}),this.log(`📅 Break scheduled: "${t.id}" at ${c(t)}`))}const a=new Set(e.breaks.map(e=>e.id));for(const[e,t]of this.breaks)a.has(e)||t.state!==D.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?.(t.break));this.cleanupBreaks(a);const i=this.getActiveBreak();i&&!this.abortNotifiedBreakIds.has(i.id)&&this.chain.refresh(i)}applyEventBoundary(e){const t=this.rewind.playbackStarted;this.updateManifest(t?{...e,adStartDelay:0}:e),t&&this.rewind.ignoreAdStartDelay()}setEventBoundaryCheck(e){this.beforeBreakCheck=e}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.preloadLedger.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)}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()))}setOnAdStartDelayElapsed(e){this.onAdStartDelayElapsed=e}holdBreakStarts(){this.breakStartsHeld=!0}releaseBreakStarts(){const e=this.breakStartsHeld;this.breakStartsHeld=!1,this.preloadLedger.checkAtBreakStart(e,()=>this.checkBreaks())}areBreakStartsHeld(){return this.breakStartsHeld||this.rewind.isAdStartDelayActive()}notifyAdStartDelayElapsed(){this.adStartDelayElapsedNotified||(this.adStartDelayElapsedNotified=!0,this.onAdStartDelayElapsed?.())}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,r){this.onBreakStart=e,this.onBreakApproaching=t??null,this.onBreakWarning=a??null,this.onBreakAborted=i??null,this.onBreakSkipped=r??null,this.checkInterval&&clearInterval(this.checkInterval),this.checkInterval=setInterval(()=>{this.tick()},250),this.log("Scheduler started")}tick(){!1!==this.beforeBreakCheck?.(this.getCurrentTime())&&(this.checkBreaks(),null!==this.nextStartDeadlineMs&&this.armStartDeadline(this.nextStartDeadlineMs))}armStartDeadline(e){null!==this.checkInterval&&null===this.startDeadline&&(this.startDeadline=setTimeout(()=>{this.startDeadline=null,this.tick()},Math.max(1,Math.ceil(e))))}clearStartDeadline(){this.startDeadline&&clearTimeout(this.startDeadline),this.startDeadline=null}suspend(){this.checkInterval&&clearInterval(this.checkInterval),this.checkInterval=null,this.clearStartDeadline()}resume(){!this.checkInterval&&this.onBreakStart&&(this.checkInterval=setInterval(()=>this.tick(),250))}skipPendingBreaksDueBy(e){return p(this.getCurrentTime(),[...this.breaks.values()],e=>e.state===D.PENDING?this.getBreakStartTime(e.break):null,t=>this.skipBreak(t,e))}stop(){this.checkInterval&&(clearInterval(this.checkInterval),this.checkInterval=null),this.clearStartDeadline(),this.onBreakStart=null,this.onBreakSkipped=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.preloadLedger.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()}skipBreak(e,t){e.state=D.SKIPPED,this.completedBreakIds.add(e.break.id),this.onBreakSkipped?.(e.break,t)}cleanupBreaks(e){const t=[];for(const[a,i]of this.breaks)i.state!==D.COMPLETED&&i.state!==D.SKIPPED?e.has(a)||i.state===D.ACTIVE||(this.skipBreak(i,"removed"),t.push(a)):t.push(a);for(const e of t)this.breaks.delete(e),this.preloadLedger.delete(e),this.breakWarningFired.delete(e)}getActiveBreak(){for(const e of this.breaks.values())if(e.state===D.ACTIVE)return e.break;return null}getSecondsUntilBreak(e){const t=this.breaks.get(e);if(!t||t.state!==D.PENDING)return null;const a=this.getCurrentTime();if(null===a)return null;const i=this.getBreakStartTime(t.break);return null===i?null:S(i,a)}getNextPendingBreak(){return B([...this.breaks.keys()],e=>this.getSecondsUntilBreak(e))}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===D.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===D.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.nextStartDeadlineMs=null,this.startupSeeking)return;const e=this.getCurrentTime();this.rewind.trackPlayedTime(e),this.rewind.adStartDelayMs>0&&!this.rewind.isAdStartDelayActive()&&this.notifyAdStartDelayElapsed();const a=this.areBreakStartsHeld();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===D.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===D.ACTIVE&&this.timing.timebase===t.PTS&&u(this.timing,this.timingDeps,s.break),s.state!==D.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 h=this.getBreakStartTime(s.break);if(null===h)continue;const l=e-h,o=Math.round(-l/1e3);if(this.timing.timebase===t.WALLCLOCK&&l<0&&-l<=250&&(this.nextStartDeadlineMs=Math.min(this.nextStartDeadlineMs??1/0,-l)),this.checkWarnings(s.break,o),o>0&&o<=15&&o%2==0&&this.log(`⏱️ Break "${s.break.id}" starts in ${o}s`),o>0&&o<=this.PRELOAD_AHEAD_SECONDS&&this.maybePreload(s.break,`${o}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)`),this.skipBreak(s,t>0&&this.tuneInEnabled?"insufficient-remaining":"late"),null===this.cueTracker&&this.breaks.get(s.break.id)===s&&this.retiredBreaks.set(s.break.id,s.break);break}if(null!==this.minSeenTime&&h<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)`),this.skipBreak(s,t>0&&this.tuneInEnabled?"insufficient-remaining":"late"),null===this.cueTracker&&this.breaks.get(s.break.id)===s&&this.retiredBreaks.set(s.break.id,s.break);break}this.preloadLedger.shouldRetryRejectedAtStart(s.break.id)&&this.maybePreload(s.break,"retrying a rejected preload at break start"),this.log(`🎬 BREAK TRIGGERED: "${s.break.id}"`),s.state=D.ACTIVE,this.onBreakStart&&this.onBreakStart(s.break,{tuneIn:!1,elapsedSec:e/1e3,remainingSec:t/1e3});break}if(l>this.TIMING_TOLERANCE_MS){const t=h+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=D.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)`),this.skipBreak(s,t>0&&this.tuneInEnabled?"insufficient-remaining":"late"),null===this.cueTracker&&this.breaks.get(s.break.id)===s&&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 this.skipBreak(e,"start-delay");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=D.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=D.ACTIVE,this.onBreakStart&&this.onBreakStart(e.break,{tuneIn:!1,elapsedSec:0,remainingSec:e.break.duration??0}))}isAdStartDelayActive(){return this.areBreakStartsHeld()}getCurrentTime(){return g(this.timing,this.timingDeps)}maybePreload(e,t){if(this.preloadLedger.wasPreloaded(e.id)||!this.onBreakApproaching)return;this.log(`⏳ Preloading break "${e.id}" (${t})`);const a=!1!==this.onBreakApproaching(e);this.preloadLedger.recordAttempt(e.id,a)}getBreakStartTime(e){return s(e.start)?null:b(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.preloadLedger.clear(),this.completedBreakIds.clear(),this.retiredBreaks.clear(),this.minSeenTime=null,this.breakStartsHeld=!1,this.onAdStartDelayElapsed=null,this.adStartDelayElapsedNotified=!1,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&&o("[OptiViewAds:BreakScheduler]",...e)}}
|
|
1
|
+
import{BreakEvent as e,Timebase as t,getBreakEvent as a,getPostRollDelay as i,getPreRollDelay as s,isEventTrigger as r,isPauseBreak as n}from"../types/BreakManifest";import{DEFAULT_BREAK_WARNINGS as h,DEFAULT_PDT_GRACE_SECONDS as d}from"../types/OptiViewAdsConfig";import{PTS_MATCH_TOLERANCE_SECONDS as l}from"./MetadataCueTracker";import{debugLog as o}from"../diagnostics/debugLog";import{createBreakTimingState as k,describeBreakStart as c,diagnoseAnvatoBreakEnd as u,getBreakStartTime as b,getCurrentTime as g,secondsUntilBreak as p}from"./breakTiming";import{BreakPreloadLedger as m}from"./BreakPreloadLedger";import{checkEventBreak as S}from"./checkEventBreak";import{SchedulerSnapback as B}from"./SchedulerSnapback";import{skipBreaksStartedBy as f,soonestBreak as E}from"./ssbStream";import{RewindRearm as I}from"./RewindRearm";import{BreakChain as y}from"./BreakChain";import{BreakState as D,systemClock as A}from"./BreakSchedulerTypes";export{BreakState,systemClock}from"./BreakSchedulerTypes";export const TICK_INTERVAL_MS=250;export class BreakScheduler{constructor(e,t=!1,a={enabled:!1,maxGapSeconds:0},i=A,s={enabled:!1,minBreakDurationSeconds:0},r=null,n=h,l=null,o=d){this.breaks=new Map,this.timing=k(),this.checkInterval=null,this.startDeadline=null,this.nextStartDeadlineMs=null,this.onBreakStart=null,this.onBreakApproaching=null,this.onBreakWarning=null,this.onBreakAborted=null,this.onBreakSkipped=null,this.beforeBreakCheck=null,this.breakStartsHeld=!1,this.preloadLedger=new m,this.onAdStartDelayElapsed=null,this.adStartDelayElapsedNotified=!1,this.abortNotifiedBreakIds=new Set,this.breakWarnings=h,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.chain=new y({breaks:this.breaks,enabled:a.enabled,maxGapMs:1e3*Math.max(0,a.maxGapSeconds),toleranceMs:this.TIMING_TOLERANCE_MS,getBreakStartTime:e=>this.getBreakStartTime(e),log:(...e)=>this.log(...e)}),this.tuneInEnabled=s.enabled,this.minBreakDurationMs=1e3*Math.max(0,s.minBreakDurationSeconds),this.clock=i,this.diagnose=r,this.snapback=new B({breaks:this.breaks,retiredBreaks:this.retiredBreaks,completedBreakIds:this.completedBreakIds,clock:i,diagnose:r,state:()=>({timebase:this.timing.timebase,started:this.rewind.playbackStarted,held:this.areBreakStartsHeld()||this.contentReloading||this.startupSeeking,active:null!==this.getActiveBreak(),position:this.player.currentTime}),preload:(e,t)=>this.maybePreload(e,t)}),this.breakWarnings=n,this.cueTracker=l;const c=Math.max(0,o);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 I({breaks:this.breaks,retiredBreaks:this.retiredBreaks,completedBreakIds:this.completedBreakIds,preloadLedger:this.preloadLedger,timing:this.timing,clock:this.clock,log:(...e)=>this.log(...e),getBreakStartTime:e=>this.getBreakStartTime(e)})}claimChain(e){this.chain.claim(e)}getChainedSuccessor(e){return this.chain.successorOf(e)}activateChainedBreak(e){this.chain.activate(e),this.snapback.guard.markPlayed(e)}setOnChainSuccessorChanged(e){this.chain.setOnSuccessorChanged(e)}updateManifest(e){this.timing.timebase=e.timebase;const t=this.rewind.adStartDelayMs;this.rewind.setAdStartDelayMs(1e3*Math.max(0,e.adStartDelay??0)),t>0&&0===this.rewind.adStartDelayMs&&this.notifyAdStartDelayElapsed();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!==D.PENDING&&e.state!==D.CHAINED||(e.break=t):(this.breaks.set(t.id,{break:t,state:D.PENDING}),this.log(`📅 Break scheduled: "${t.id}" at ${c(t)}`))}const a=new Set(e.breaks.map(e=>e.id));for(const[e,t]of this.breaks)a.has(e)||t.state!==D.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?.(t.break));this.cleanupBreaks(a);const i=this.getActiveBreak();i&&!this.abortNotifiedBreakIds.has(i.id)&&this.chain.refresh(i)}applyEventBoundary(e){const t=this.rewind.playbackStarted;this.updateManifest(t?{...e,adStartDelay:0}:e),t&&this.rewind.ignoreAdStartDelay()}setEventBoundaryCheck(e){this.beforeBreakCheck=e}resetForChannelSwitch(e){this.snapback.guard.reset();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.preloadLedger.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!r(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)}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()))}setOnAdStartDelayElapsed(e){this.onAdStartDelayElapsed=e}holdBreakStarts(){this.breakStartsHeld=!0}releaseBreakStarts(){const e=this.breakStartsHeld;this.breakStartsHeld=!1,this.preloadLedger.checkAtBreakStart(e,()=>this.checkBreaks())}areBreakStartsHeld(){return this.breakStartsHeld||this.rewind.isAdStartDelayActive()}notifyAdStartDelayElapsed(){this.adStartDelayElapsedNotified||(this.adStartDelayElapsedNotified=!0,this.onAdStartDelayElapsed?.())}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,s){this.onBreakStart=e,this.onBreakApproaching=t??null,this.onBreakWarning=a??null,this.onBreakAborted=i??null,this.onBreakSkipped=s??null,this.checkInterval&&clearInterval(this.checkInterval),this.checkInterval=setInterval(()=>{this.tick()},250),this.log("Scheduler started")}tick(){!1!==this.beforeBreakCheck?.(this.getCurrentTime())&&(this.checkBreaks(),null!==this.nextStartDeadlineMs&&this.armStartDeadline(this.nextStartDeadlineMs))}armStartDeadline(e){null!==this.checkInterval&&null===this.startDeadline&&(this.startDeadline=setTimeout(()=>{this.startDeadline=null,this.tick()},Math.max(1,Math.ceil(e))))}clearStartDeadline(){this.startDeadline&&clearTimeout(this.startDeadline),this.startDeadline=null}suspend(){this.checkInterval&&clearInterval(this.checkInterval),this.checkInterval=null,this.clearStartDeadline()}resume(){!this.checkInterval&&this.onBreakStart&&(this.checkInterval=setInterval(()=>this.tick(),250))}skipPendingBreaksDueBy(e){return f(this.getCurrentTime(),[...this.breaks.values()],e=>e.state===D.PENDING?this.getBreakStartTime(e.break):null,t=>this.skipBreak(t,e))}stop(){this.checkInterval&&(clearInterval(this.checkInterval),this.checkInterval=null),this.clearStartDeadline(),this.onBreakStart=null,this.onBreakSkipped=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.preloadLedger.delete(e))}setContentReloading(e){this.snapback.guard.resetPosition(),this.contentReloading=e,this.rewind.setContentReloading(e||this.startupSeeking)}setStartupSeeking(e){this.snapback.guard.resetPosition(),this.startupSeeking=e,this.rewind.setContentReloading(e||this.contentReloading)}notifyResumeSeek(e,t){this.snapback.guard.expectSeek(e,this.clock.now()),this.rewind.notifyResumeSeek(e,t)}notifyViewerSeek(){this.rewind.notifyViewerSeek()}setSnapbackOnSeek(e,t){this.snapback.enabled=e,this.snapback.canPlay=t}setOnSnapback(e){this.snapback.onCorrect=e}planSeek(e){return this.snapback.planSeek(e)}notifySeeked(){this.snapback.notifySeeked()}startBreak(e,t){this.snapback.guard.markPlayed(e.id),this.onBreakStart?.(e,t)}skipBreak(e,t){e.state=D.SKIPPED,this.completedBreakIds.add(e.break.id),this.onBreakSkipped?.(e.break,t)}cleanupBreaks(e){const t=[];for(const[a,i]of this.breaks)i.state!==D.COMPLETED&&i.state!==D.SKIPPED?e.has(a)||i.state===D.ACTIVE||(this.skipBreak(i,"removed"),t.push(a)):t.push(a);for(const e of t)this.breaks.delete(e),this.preloadLedger.delete(e),this.breakWarningFired.delete(e)}getActiveBreak(){for(const e of this.breaks.values())if(e.state===D.ACTIVE)return e.break;return null}getSecondsUntilBreak(e){const t=this.breaks.get(e);if(!t||t.state!==D.PENDING)return null;const a=this.getCurrentTime();if(null===a)return null;const i=this.getBreakStartTime(t.break);return null===i?null:p(i,a)}getNextPendingBreak(){return E([...this.breaks.keys()],e=>this.getSecondsUntilBreak(e))}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===D.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 s=this.breaks.get(a);return s&&s.state===D.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.nextStartDeadlineMs=null,this.startupSeeking)return;const e=this.getCurrentTime();this.rewind.trackPlayedTime(e),this.rewind.adStartDelayMs>0&&!this.rewind.isAdStartDelayActive()&&this.notifyAdStartDelayElapsed();const a=this.areBreakStartsHeld(),r=this.snapback.sample();if(!r.waiting){if(r.ready){const e=this.breaks.get(r.ready.breakId);if(e)return e.state=D.ACTIVE,void this.startBreak(e.break,{tuneIn:!1,elapsedSec:0,remainingSec:e.break.duration,snapback:void 0===r.ready.resumePosition?{}:{resumePosition:r.ready.resumePosition}})}null!==e&&(this.minSeenTime=null===this.minSeenTime?e:Math.min(this.minSeenTime,e),r.internal||this.rewind.rearmOnRewind(e));for(const r of this.breaks.values()){if(r.state===D.ACTIVE&&this.timing.timebase===t.WALLCLOCK&&null!==e&&!this.abortNotifiedBreakIds.has(r.break.id)){const t=this.getBreakStartTime(r.break),a=1e3*(r.break.duration??0);null!==t&&a>0&&e>t+a+this.WINDOW_ESCAPE_GRACE_MS&&(this.abortNotifiedBreakIds.add(r.break.id),this.log(`⚠️ Break window escaped: "${r.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:r.break.id,overrunSec:Math.round((e-t-a)/100)/10}),this.onBreakAborted?.(r.break));continue}if(r.state===D.ACTIVE&&this.timing.timebase===t.PTS&&u(this.timing,this.timingDeps,r.break),r.state!==D.PENDING)continue;const n=s(r.break);if(null!==n){this.checkPreRollBreak(r,n,a);continue}const h=i(r.break);if(null!==h){this.checkPostRollBreak(r,h,a);continue}if(null===e)continue;if(a)continue;null!==this.cueTracker&&this.timing.timebase===t.PTS&&this.maybePreload(r.break,"manifest arrival — Anvato flow");const d=this.getBreakStartTime(r.break);if(null===d)continue;const l=e-d,o=Math.round(-l/1e3);if(this.timing.timebase===t.WALLCLOCK&&l<0&&-l<=250&&(this.nextStartDeadlineMs=Math.min(this.nextStartDeadlineMs??1/0,-l)),this.checkWarnings(r.break,o),o>0&&o<=15&&o%2==0&&this.log(`⏱️ Break "${r.break.id}" starts in ${o}s`),o>0&&o<=this.PRELOAD_AHEAD_SECONDS&&this.maybePreload(r.break,`${o}s ahead`),l>=0&&l<=this.TIMING_TOLERANCE_MS){const e=Math.max(0,l),t=1e3*(r.break.duration??0)-e;if(t<=0){this.log(`⚠️ Break missed: "${r.break.id}" (ended ${Math.round(-t/1e3)}s ago, inside the start tolerance)`),this.skipBreak(r,t>0&&this.tuneInEnabled?"insufficient-remaining":"late"),null===this.cueTracker&&this.breaks.get(r.break.id)===r&&this.retiredBreaks.set(r.break.id,r.break);break}if(null!==this.minSeenTime&&d<this.minSeenTime-this.JOIN_DISCRIMINATOR_SLACK_MS&&(!this.tuneInEnabled||t<this.minBreakDurationMs)){this.log(`⚠️ Break missed: "${r.break.id}" (joined in progress inside the start tolerance, ${Math.round(t/1e3)}s remaining < tune-in rules)`),this.skipBreak(r,t>0&&this.tuneInEnabled?"insufficient-remaining":"late"),null===this.cueTracker&&this.breaks.get(r.break.id)===r&&this.retiredBreaks.set(r.break.id,r.break);break}this.preloadLedger.shouldRetryRejectedAtStart(r.break.id)&&this.maybePreload(r.break,"retrying a rejected preload at break start"),this.log(`🎬 BREAK TRIGGERED: "${r.break.id}"`),r.state=D.ACTIVE,this.onBreakStart&&this.startBreak(r.break,{tuneIn:!1,elapsedSec:e/1e3,remainingSec:t/1e3});break}if(l>this.TIMING_TOLERANCE_MS){const t=d+1e3*(r.break.duration??0)-e;if(this.tuneInEnabled&&t>=this.minBreakDurationMs&&t>0){this.log(`🎬 TUNE-IN BREAK TRIGGERED: "${r.break.id}" (${Math.round(t/1e3)}s remaining)`),r.state=D.ACTIVE,this.onBreakStart&&this.startBreak(r.break,{tuneIn:!0,elapsedSec:l/1e3,remainingSec:t/1e3});break}this.log(`⚠️ Break missed: "${r.break.id}" (was ${Math.round(l/1e3)}s ago)`),this.skipBreak(r,t>0&&this.tuneInEnabled?"insufficient-remaining":"late"),null===this.cueTracker&&this.breaks.get(r.break.id)===r&&this.retiredBreaks.set(r.break.id,r.break)}}}}checkPreRollBreak(e,t,a){S(e,t,this.rewind.playbackStarted?this.rewind.channelPlayedMs:null,this.rewind.adStartDelayMs,a,(e,t)=>this.maybePreload(e,t),e=>this.skipBreak(e,"start-delay"),(e,t)=>this.startBreak(e,t))}checkPostRollBreak(e,t,a){S(e,t,null===this.playbackEndedAtMs?null:this.clock.now()-this.playbackEndedAtMs,0,a,(e,t)=>this.maybePreload(e,t),e=>this.skipBreak(e,"start-delay"),(e,t)=>this.startBreak(e,t))}isAdStartDelayActive(){return this.areBreakStartsHeld()}getCurrentTime(){return g(this.timing,this.timingDeps)}maybePreload(e,t){if(this.preloadLedger.wasPreloaded(e.id)||!this.onBreakApproaching)return;this.log(`⏳ Preloading break "${e.id}" (${t})`);const a=!1!==this.onBreakApproaching(e);this.preloadLedger.recordAttempt(e.id,a)}getBreakStartTime(e){return r(e.start)?null:b(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.snapback.guard.reset(),this.snapback.onCorrect=null,this.stop(),this.breaks.clear(),this.preloadLedger.clear(),this.completedBreakIds.clear(),this.retiredBreaks.clear(),this.minSeenTime=null,this.breakStartsHeld=!1,this.onAdStartDelayElapsed=null,this.adStartDelayElapsedNotified=!1,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&&o("[OptiViewAds:BreakScheduler]",...e)}}
|
|
@@ -37,6 +37,10 @@ export interface BreakStartContext {
|
|
|
37
37
|
elapsedSec: number;
|
|
38
38
|
/** Effective remaining duration, in seconds, the break should be presented for. */
|
|
39
39
|
remainingSec: number;
|
|
40
|
+
/** VOD seek correction; seek-over resumes at this position, seek-into uses normal policy. */
|
|
41
|
+
snapback?: {
|
|
42
|
+
resumePosition?: number;
|
|
43
|
+
};
|
|
40
44
|
}
|
|
41
45
|
export type BreakStartCallback = (breakInfo: Break, context: BreakStartContext) => void;
|
|
42
46
|
/**
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Timebase, type Break } from '../types/BreakManifest';
|
|
2
|
+
import type { Clock, SchedulerDiagnose } from './BreakSchedulerTypes';
|
|
3
|
+
import type { TrackedBreak } from './RewindRearm';
|
|
4
|
+
import { SnapbackGuard, type SnapbackRequest } from './SnapbackGuard';
|
|
5
|
+
export declare class SchedulerSnapback {
|
|
6
|
+
private readonly deps;
|
|
7
|
+
readonly guard: SnapbackGuard;
|
|
8
|
+
enabled: boolean;
|
|
9
|
+
canPlay: ((brk: Break) => boolean) | undefined;
|
|
10
|
+
onCorrect: ((request: SnapbackRequest) => void) | null;
|
|
11
|
+
constructor(deps: {
|
|
12
|
+
breaks: Map<string, TrackedBreak>;
|
|
13
|
+
retiredBreaks: Map<string, Break>;
|
|
14
|
+
completedBreakIds: Set<string>;
|
|
15
|
+
clock: Clock;
|
|
16
|
+
diagnose: SchedulerDiagnose | null;
|
|
17
|
+
state: () => {
|
|
18
|
+
timebase: Timebase;
|
|
19
|
+
started: boolean;
|
|
20
|
+
held: boolean;
|
|
21
|
+
active: boolean;
|
|
22
|
+
position: number;
|
|
23
|
+
};
|
|
24
|
+
preload: (brk: Break, label: string) => void;
|
|
25
|
+
});
|
|
26
|
+
private eligibility;
|
|
27
|
+
private breaks;
|
|
28
|
+
private arm;
|
|
29
|
+
planSeek(target: number): SnapbackRequest | null;
|
|
30
|
+
notifySeeked(): void;
|
|
31
|
+
sample(): ReturnType<SnapbackGuard['sample']>;
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Timebase as s}from"../types/BreakManifest";import{BreakState as e}from"./BreakSchedulerTypes";import{SnapbackGuard as t}from"./SnapbackGuard";export class SchedulerSnapback{constructor(s){this.deps=s,this.enabled=!1,this.onCorrect=null,this.guard=new t(s.diagnose)}eligibility(){const e=this.deps.state();return{...e,enabled:this.enabled&&e.timebase===s.MEDIATIME}}breaks(){const s=new Map(this.deps.retiredBreaks);for(const[e,t]of this.deps.breaks)s.set(e,t.break);return[...s.values()]}arm(s){const t=this.breaks().find(e=>e.id===s.breakId);t&&(this.deps.completedBreakIds.delete(t.id),this.deps.breaks.set(t.id,{break:t,state:e.PENDING}),this.deps.preload(t,"VOD snapback"))}planSeek(s){const e=this.guard.plan(s,this.breaks(),this.eligibility(),this.deps.clock.now(),this.canPlay);return e&&this.arm(e),e}notifySeeked(){const s=this.guard.seeked(this.deps.state().position,this.breaks(),this.eligibility(),this.deps.clock.now(),this.canPlay);s&&(this.arm(s),this.onCorrect?.(s))}sample(){return this.guard.sample(this.deps.state().position,this.breaks(),this.eligibility(),this.deps.clock.now(),s=>{this.arm(s),this.onCorrect?.(s)},this.canPlay)}}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type Break } from '../types/BreakManifest';
|
|
2
|
+
import type { SchedulerDiagnose } from './BreakSchedulerTypes';
|
|
3
|
+
export interface SnapbackRequest {
|
|
4
|
+
breakId: string;
|
|
5
|
+
target: number;
|
|
6
|
+
resumePosition?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface SnapbackEligibility {
|
|
9
|
+
enabled: boolean;
|
|
10
|
+
started: boolean;
|
|
11
|
+
held: boolean;
|
|
12
|
+
active: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** Portable seek policy. All positions are content seconds; deadlines use the injected clock. */
|
|
15
|
+
export declare class SnapbackGuard {
|
|
16
|
+
private readonly diagnose;
|
|
17
|
+
private played;
|
|
18
|
+
private lastPosition;
|
|
19
|
+
private internalTarget;
|
|
20
|
+
private internalDeadline;
|
|
21
|
+
private armed;
|
|
22
|
+
constructor(diagnose: SchedulerDiagnose | null);
|
|
23
|
+
markPlayed(id: string): void;
|
|
24
|
+
expectSeek(target: number, now: number): void;
|
|
25
|
+
reset(): void;
|
|
26
|
+
resetPosition(): void;
|
|
27
|
+
private isInternal;
|
|
28
|
+
plan(target: number, breaks: Break[], eligibility: SnapbackEligibility, now: number, canPlay?: (brk: Break) => boolean): SnapbackRequest | null;
|
|
29
|
+
seeked(position: number, breaks: Break[], eligibility: SnapbackEligibility, now: number, canPlay?: (brk: Break) => boolean): SnapbackRequest | null;
|
|
30
|
+
sample(position: number, breaks: Break[], eligibility: SnapbackEligibility, now: number, correct: (request: SnapbackRequest) => void, canPlay?: (brk: Break) => boolean): {
|
|
31
|
+
waiting: boolean;
|
|
32
|
+
internal: boolean;
|
|
33
|
+
ready?: SnapbackRequest;
|
|
34
|
+
};
|
|
35
|
+
private abandon;
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{getBreakEvent as t}from"../types/BreakManifest";import{selectVariantDetailed as e}from"./selectVariant";export class SnapbackGuard{constructor(t){this.diagnose=t,this.played=new Set,this.lastPosition=null,this.internalTarget=null,this.internalDeadline=0,this.armed=null}markPlayed(t){this.played.add(t)}expectSeek(t,e){this.internalTarget=t,this.internalDeadline=e+2e3,this.lastPosition=t}reset(){this.played.clear(),this.resetPosition()}resetPosition(){this.lastPosition=null,this.internalTarget=null,this.armed=null}isInternal(t,e){return null!==this.internalTarget&&(e<=this.internalDeadline&&Math.abs(t-this.internalTarget)<=.5||(this.internalTarget=null,!1))}plan(i,a,n,s,r=t=>null!==e(t).selected){const l=this.armed?.origin??this.lastPosition;this.armed=null,this.internalTarget=null,this.lastPosition=n.started?i:null;const o=n.enabled?n.started&&null!==l?n.held?"break-starts-held":n.active?"break-active":null:"before-playback":"disabled";if(null!==o||!Number.isFinite(i)||null===l||l===i)return o&&n.enabled&&this.diagnose?.("DA-SNAPBACK-NOT-APPLIED","Seek did not apply VOD snapback",{reason:o}),null;const d=a.filter(e=>{if("number"!=typeof e.start||!Number.isFinite(e.start)||null!==t(e)||!e.controls?.snapback||this.played.has(e.id))return!1;const a=e.start+e.duration;return i>=e.start&&i<a||i>l&&l<e.start&&i>=a}).sort((t,e)=>Number(t.start)-Number(e.start)||(t.id<e.id?-1:t.id>e.id?1:0));for(const t of d){if(!r(t)){this.diagnose?.("DA-SNAPBACK-NOT-APPLIED","Seek crossed an unplayable snapback break",{breakId:t.id,reason:"no-playable-variant"});continue}const e={breakId:t.id,target:Number(t.start),...i>=Number(t.start)+t.duration?{resumePosition:i}:{}};return this.armed={request:e,origin:l,deadline:s+1e3,attempts:1},this.diagnose?.("DA-SNAPBACK-APPLIED","Viewer seek corrected to an unplayed VOD break",{...e}),e}return null}seeked(t,e,i,a,n){return this.armed&&(Math.abs(t-this.armed.request.target)<=.5||Math.abs(t-this.armed.origin)<=.5)||this.isInternal(t,a)?null:this.plan(t,e,i,a,n)}sample(t,e,i,a,n,s){if(!Number.isFinite(t))return{waiting:!1,internal:!1};if(!i.started)return{waiting:!1,internal:!1};if(this.armed){const r=this.armed;if(!e.some(t=>t.id===r.request.breakId)||!i.enabled||i.held)return this.abandon(t,"ineligible"),{waiting:!1,internal:!0};if(Math.abs(t-r.request.target)<=.5)return this.armed=null,this.expectSeek(t,a),{waiting:!1,internal:!0,ready:r.request};if(null!==this.lastPosition&&Math.abs(t-this.lastPosition)>2&&Math.abs(t-r.origin)>.5){const r=this.plan(t,e,i,a,s);return r&&n(r),{waiting:null!==r,internal:!0}}return a>=r.deadline&&(1===r.attempts?(r.attempts++,r.deadline=a+1e3,n(r.request)):this.abandon(t,"seek-did-not-land")),{waiting:null!==this.armed,internal:!0}}const r=this.isInternal(t,a);if(!r&&null!==this.lastPosition&&Math.abs(t-this.lastPosition)>2){const r=this.plan(t,e,i,a,s);if(r)return n(r),{waiting:!0,internal:!0}}return this.lastPosition=t,{waiting:!1,internal:r}}abandon(t,e){this.diagnose?.("DA-SNAPBACK-ABANDONED","VOD snapback correction abandoned",{breakId:this.armed?.request.breakId,reason:e}),this.armed=null,this.lastPosition=t}}
|
|
@@ -47,6 +47,8 @@ export interface AdPlayerCallbacks {
|
|
|
47
47
|
onContentReloading?: (active: boolean) => void;
|
|
48
48
|
}
|
|
49
49
|
export interface PlayBreakOptions {
|
|
50
|
+
/** Original seek-over target, overriding insertion/resume offset even on no-fill. */
|
|
51
|
+
snapbackResumePosition?: number | undefined;
|
|
50
52
|
/** Seconds to present the break for (tune-in breaks: the remaining time); defaults to `break.duration`. */
|
|
51
53
|
effectiveDurationSec?: number;
|
|
52
54
|
/** Defer the overlay and `onBreakVisible` until the first ad frame renders (held delay:0 fullscreen pre-rolls). */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PlayerAdapter,
|
|
1
|
+
import type { PlayerAdapter, TimedMetadataCue } from '../../interfaces/PlayerAdapter';
|
|
2
2
|
import { type Break } from '../../types/BreakManifest';
|
|
3
3
|
import type { GamEventCallbacks } from '../AdPlayerController';
|
|
4
4
|
import type { AdBreakSequencer, AdBreakTransition } from '../AdBreakSequencer';
|
|
@@ -8,12 +8,6 @@ export interface AdPlayerEventsDeps {
|
|
|
8
8
|
getContentPlayer: () => PlayerAdapter;
|
|
9
9
|
getGamEventCallbacks: () => GamEventCallbacks;
|
|
10
10
|
getSequencer: () => AdBreakSequencer | null;
|
|
11
|
-
getAudioFocus: () => 'content' | 'ad' | null;
|
|
12
|
-
getSyncing: () => boolean;
|
|
13
|
-
setSyncing: (value: boolean) => void;
|
|
14
|
-
setAdVolumeChangeHandler: (handler: PlayerAdapterEventHandler | null) => void;
|
|
15
|
-
setContentVolumeHandler: (handler: (() => void) | null) => void;
|
|
16
|
-
notifyVolumeChange: () => void;
|
|
17
11
|
onAdEnded: () => void;
|
|
18
12
|
processTimedMetadata: (cue: TimedMetadataCue) => void;
|
|
19
13
|
toAdError: (mediaError?: unknown) => Error;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{AssetType as t}from"../../types/BreakManifest";export class AdPlayerEvents{constructor(t,e){this.state=t,this.deps=e}recordAdProgress(t){!Number.isFinite(t)||t<=0||(this.deps.onGamPodAdProgress(t),t>this.state.currentAssetElapsedSec&&(this.state.currentAssetElapsedSec=t))}settleAssetProgress(){let t=this.state.currentAssetElapsedSec;if(t<=0){const e=this.state.currentAsset?.duration;"number"==typeof e&&Number.isFinite(e)&&e>0&&(t=e)}this.state.adSecondsPlayedThisBreak+=t,this.state.currentAssetElapsedSec=0}reportAdProgress(e){if(this.state.isPlaying&&this.state.currentBreak&&this.state.currentAsset&&(this.recordAdProgress(e.currentTime),this.deps.maybePrepositionContentForResume(),this.state.currentBreak&&this.deps.maybeCatchUpContentForResume(this.state.currentBreak),this.state.callbacks?.onAdTimeUpdate(this.state.currentBreak,this.state.currentAsset,e.currentTime,e.duration),this.state.currentAsset.type===t.STATIC&&e.duration>0)){const t=e.currentTime/e.duration,s=this.deps.getStaticQuartilesFired(),r=this.deps.getGamEventCallbacks();t>=.25&&!s.has("first")&&(s.add("first"),r.onAdFirstQuartile?.(this.state.currentBreak,this.state.currentAsset)),t>=.5&&!s.has("mid")&&(s.add("mid"),r.onAdMidpoint?.(this.state.currentBreak,this.state.currentAsset)),t>=.75&&!s.has("third")&&(s.add("third"),r.onAdThirdQuartile?.(this.state.currentBreak,this.state.currentAsset))}}setupEventListeners(){const t=this.deps.getAdPlayer();
|
|
1
|
+
import{AssetType as t}from"../../types/BreakManifest";export class AdPlayerEvents{constructor(t,e){this.state=t,this.deps=e}recordAdProgress(t){!Number.isFinite(t)||t<=0||(this.deps.onGamPodAdProgress(t),t>this.state.currentAssetElapsedSec&&(this.state.currentAssetElapsedSec=t))}settleAssetProgress(){let t=this.state.currentAssetElapsedSec;if(t<=0){const e=this.state.currentAsset?.duration;"number"==typeof e&&Number.isFinite(e)&&e>0&&(t=e)}this.state.adSecondsPlayedThisBreak+=t,this.state.currentAssetElapsedSec=0}reportAdProgress(e){if(this.state.isPlaying&&this.state.currentBreak&&this.state.currentAsset&&(this.recordAdProgress(e.currentTime),this.deps.maybePrepositionContentForResume(),this.state.currentBreak&&this.deps.maybeCatchUpContentForResume(this.state.currentBreak),this.state.callbacks?.onAdTimeUpdate(this.state.currentBreak,this.state.currentAsset,e.currentTime,e.duration),this.state.currentAsset.type===t.STATIC&&e.duration>0)){const t=e.currentTime/e.duration,s=this.deps.getStaticQuartilesFired(),r=this.deps.getGamEventCallbacks();t>=.25&&!s.has("first")&&(s.add("first"),r.onAdFirstQuartile?.(this.state.currentBreak,this.state.currentAsset)),t>=.5&&!s.has("mid")&&(s.add("mid"),r.onAdMidpoint?.(this.state.currentBreak,this.state.currentAsset)),t>=.75&&!s.has("third")&&(s.add("third"),r.onAdThirdQuartile?.(this.state.currentBreak,this.state.currentAsset))}}setupEventListeners(){const t=this.deps.getAdPlayer();t&&(t.on("ended",()=>{this.deps.onAdEnded()}),t.on("timedmetadata",t=>{t&&this.deps.processTimedMetadata(t)}),t.on("error",t=>{this.onAdError(this.deps.toAdError(t))}),t.on("timeupdate",()=>this.reportAdProgress(t)),t.on("waiting",()=>{this.state.isPlaying&&this.state.currentBreak&&this.state.currentAsset&&this.state.callbacks?.onAdWaiting(this.state.currentBreak,this.state.currentAsset)}),t.on("playing",()=>{if(this.state.isPlaying&&this.state.currentBreak&&this.state.currentAsset&&!t.paused){if(this.state.startPlaybackCutoff?.(),this.state.firstFrameResolver)return this.deps.revealFirstFrame(),this.state.firstFrameResolver(),void this.state.callbacks?.onAdPlaying(this.state.currentBreak,this.state.currentAsset);this.state.callbacks?.onAdPlaying(this.state.currentBreak,this.state.currentAsset)}}))}onAdError(t){const e=this.deps.getSequencer(),s=this.state.currentBreak;if(!s||!e)return;const r=this.state.preloadGeneration,a=this.state.erroredAsset;if(a?.breakInfo!==s||a.generation!==r){if(this.state.erroredAsset={breakInfo:s,generation:r},this.state.firstFrameRejector){const e=this.state.firstFrameRejector;this.deps.clearFirstFrameResolver(),e(t)}this.deps.log("Ad error:",t.message),this.deps.dropQueuedAd(),this.deps.setPendingAdError(t),this.deps.drive(e.onOutcome({kind:"asset-errored"}))}else this.deps.log("Ignoring further ad error for the same asset:",t.message)}}
|
|
@@ -60,6 +60,7 @@ export interface AdPlayerState {
|
|
|
60
60
|
savedContentEdgeDistance: number | null;
|
|
61
61
|
earlyReturnResumeOffsetSec: number | null;
|
|
62
62
|
contentPositionAtBreakStart: number;
|
|
63
|
+
snapbackResumePosition: number | undefined;
|
|
63
64
|
/**
|
|
64
65
|
* Whether this break's anchor is the in-stream cue rather than the pause position — i.e.
|
|
65
66
|
* whether the content under the ad is known slate of exactly the advertised duration.
|
|
@@ -102,7 +103,6 @@ export interface AdPlayerState {
|
|
|
102
103
|
continueCorrections: number[];
|
|
103
104
|
continueDriftUnrecoverable: boolean;
|
|
104
105
|
continueRefusalReported: boolean;
|
|
105
|
-
contentMutedBeforeCatchup: boolean | null;
|
|
106
106
|
resumeAlignCorrectionsMs: number[];
|
|
107
107
|
resumeLandedProbe: (() => void) | null;
|
|
108
108
|
adSecondsPlayedThisBreak: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function invalidateMediaPreload(e){return e.preparedMedia=null,++e.preloadGeneration}export function createAdPlayerState(){return{currentBreak:null,currentAsset:null,isPlaying:!1,contentPaused:!1,callbacks:null,durationTimer:null,startPlaybackCutoff:null,effectiveBreakDurationSec:null,adInsertionType:void 0,breakTimebase:null,adBreakCutSafetyMarginSec:0,detectedDeviceType:void 0,sessionAssetParameters:void 0,updatedAssetParameters:void 0,manifestAssetParameters:void 0,sessionAssetParameterMacros:{},pushedAssetParameters:null,gamStreamManager:null,gamPodMode:"none",gamPodTracker:null,gamPodAsset:null,earlyGamPodAdStart:null,withheldPodAdBegin:null,gamConfig:void 0,gamInitPromise:null,currentGamIdentity:void 0,playerContainer:null,stage:null,contentVideoElement:null,adContainer:null,adMediaContainer:null,adPlayer:null,companionElement:null,overlayImageEl:null,isContentFullscreen:!1,trackedContentVideo:null,fullscreenEnterHandler:null,fullscreenExitHandler:null,contentPictureInPicture:!1,pipTransferredToAd:!1,pipAdoptedForBreak:!1,sharedAdClaimActive:!1,savedContentSrc:null,savedContentTime:0,savedContentLive:!1,savedContentEdgeDistance:null,earlyReturnResumeOffsetSec:null,contentPositionAtBreakStart:0,resumeAnchorIsCue:!1,resumeAnchorIsPdt:!1,resumeAnchorSeekableEnd:null,resumePrepositionedTo:null,resumePrepositionSettle:null,resumeCatchupActive:!1,resumeCatchupRan:!1,lastResumeAlignRemainingSec:Number.POSITIVE_INFINITY,resumeAlignBlockedDiagnosed:!1,lastResumeSafeEnd:Number.NEGATIVE_INFINITY,resumeSawSeekDone:!1,resumeBestSawMarginSec:Number.NEGATIVE_INFINITY,resumeAlignTicks:0,resumeSawSteps:[],continueContentDuringBreak:!1,continueQualityOverrideApplied:!1,continueContentDuringBreakActive:!1,keepContentPlayingHandler:null,continuePlayoutStartWallMs:0,continuePlayoutTicker:null,continueRateWritten:null,continueRateWrittenAtMs:0,continueRateHonoured:!0,continueMaxAbsDriftSec:0,continueCorrections:[],continueDriftUnrecoverable:!1,continueRefusalReported:!1,
|
|
1
|
+
export function invalidateMediaPreload(e){return e.preparedMedia=null,++e.preloadGeneration}export function createAdPlayerState(){return{currentBreak:null,currentAsset:null,isPlaying:!1,contentPaused:!1,callbacks:null,durationTimer:null,startPlaybackCutoff:null,effectiveBreakDurationSec:null,adInsertionType:void 0,breakTimebase:null,adBreakCutSafetyMarginSec:0,detectedDeviceType:void 0,sessionAssetParameters:void 0,updatedAssetParameters:void 0,manifestAssetParameters:void 0,sessionAssetParameterMacros:{},pushedAssetParameters:null,gamStreamManager:null,gamPodMode:"none",gamPodTracker:null,gamPodAsset:null,earlyGamPodAdStart:null,withheldPodAdBegin:null,gamConfig:void 0,gamInitPromise:null,currentGamIdentity:void 0,playerContainer:null,stage:null,contentVideoElement:null,adContainer:null,adMediaContainer:null,adPlayer:null,companionElement:null,overlayImageEl:null,isContentFullscreen:!1,trackedContentVideo:null,fullscreenEnterHandler:null,fullscreenExitHandler:null,contentPictureInPicture:!1,pipTransferredToAd:!1,pipAdoptedForBreak:!1,sharedAdClaimActive:!1,savedContentSrc:null,savedContentTime:0,savedContentLive:!1,savedContentEdgeDistance:null,earlyReturnResumeOffsetSec:null,contentPositionAtBreakStart:0,snapbackResumePosition:void 0,resumeAnchorIsCue:!1,resumeAnchorIsPdt:!1,resumeAnchorSeekableEnd:null,resumePrepositionedTo:null,resumePrepositionSettle:null,resumeCatchupActive:!1,resumeCatchupRan:!1,lastResumeAlignRemainingSec:Number.POSITIVE_INFINITY,resumeAlignBlockedDiagnosed:!1,lastResumeSafeEnd:Number.NEGATIVE_INFINITY,resumeSawSeekDone:!1,resumeBestSawMarginSec:Number.NEGATIVE_INFINITY,resumeAlignTicks:0,resumeSawSteps:[],continueContentDuringBreak:!1,continueQualityOverrideApplied:!1,continueContentDuringBreakActive:!1,keepContentPlayingHandler:null,continuePlayoutStartWallMs:0,continuePlayoutTicker:null,continueRateWritten:null,continueRateWrittenAtMs:0,continueRateHonoured:!0,continueMaxAbsDriftSec:0,continueCorrections:[],continueDriftUnrecoverable:!1,continueRefusalReported:!1,resumeAlignCorrectionsMs:[],resumeLandedProbe:null,adSecondsPlayedThisBreak:0,currentAssetElapsedSec:0,preparedMedia:null,preloadGeneration:0,erroredAsset:null,firstFrameGateActive:!1,firstFrameGateShown:!1,firstFrameResolver:null,firstFrameRejector:null,firstFrameTimeout:null,adStartDeadlineMs:null,adStartBudgetMs:0,vastAdManager:null}}
|
|
@@ -2,6 +2,7 @@ import type { PlayerAdapter } from '../../interfaces/PlayerAdapter';
|
|
|
2
2
|
import { Asset, Break, BreakVariant, StaticAsset, VastAsset, VendorAsset } from '../../types/BreakManifest';
|
|
3
3
|
import type { AdBreakSequencer, AdBreakTransition } from '../AdBreakSequencer';
|
|
4
4
|
import { type AdPlayerState } from './AdPlayerState';
|
|
5
|
+
import type { SurfaceRevealReporter } from './SurfaceRevealReporter';
|
|
5
6
|
export interface AssetPlaybackDeps {
|
|
6
7
|
getAdPlayer: () => PlayerAdapter | null;
|
|
7
8
|
getAdContainer: () => HTMLDivElement | null;
|
|
@@ -23,6 +24,8 @@ export interface AssetPlaybackDeps {
|
|
|
23
24
|
drive: (transition: AdBreakTransition) => Promise<void>;
|
|
24
25
|
clearOverlayImage: () => void;
|
|
25
26
|
revealFirstFrame: () => void;
|
|
27
|
+
/** Watches for the ad's first frame on screen (`DA-AD-SURFACE-REVEALED`). */
|
|
28
|
+
surfaceReveal: SurfaceRevealReporter;
|
|
26
29
|
transferPictureInPictureToAd: () => Promise<void>;
|
|
27
30
|
isGamPodAsset: (asset: Asset) => boolean;
|
|
28
31
|
/** Whether the GAM pod asset was skipped because its identity mismatches the stream session. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{AssetType as e,BreakFormat as t,Vendor as s,MediaType as r,isVastAsset as i}from"../../types/BreakManifest";import{AdError as a}from"../../types/AdError";import{resolveVastAdmission as d}from"../resolveVastAdmission";import{classifyMediaError as n,toClassifiedAdError as o}from"./classifyMediaError";import{VastAdManager as h,isImaCsaiSdkAvailable as
|
|
1
|
+
import{AssetType as e,BreakFormat as t,Vendor as s,MediaType as r,isVastAsset as i}from"../../types/BreakManifest";import{AdError as a}from"../../types/AdError";import{resolveVastAdmission as d}from"../resolveVastAdmission";import{classifyMediaError as n,toClassifiedAdError as o}from"./classifyMediaError";import{VastAdManager as h,isImaCsaiSdkAvailable as c}from"../VastAdManager";import{invalidateMediaPreload as p}from"./AdPlayerState";export class AssetPlayback{constructor(e,t){this.state=e,this.deps=t}get currentBreak(){return this.state.currentBreak}async playAsset(e,t,s){const d=this.currentBreak,n=this.deps.getAdPlayer();if(!d||!n)return;const h=this.state.preparedMedia,c=p(this.state),l=()=>this.state.preloadGeneration===c&&this.currentBreak===d&&this.deps.getAdPlayer()===n;if(this.state.currentAsset=e,this.state.currentAssetElapsedSec=0,this.deps.clearStaticQuartiles(),this.deps.clearOverlayImage(),this.deps.log("Playing asset:",e.id,"type:",e.type,"index:",t),i(e))return void this.playVastAsset(e,t,s);if(e.mediaType===r.IMAGE)return void await this.playImageAsset(e,t,s);this.deps.surfaceReveal.watch(e.id,d.id);const u=await this.resolveAssetUri(e,this.state.effectiveBreakDurationSec??d.duration);if(!l())return;if(!u)return void this.deps.onAdError(this.missingUriError(e));this.deps.log("Resolved URI:",u),this.deps.reportPhase("uri-resolved");const A=d.id;try{this.deps.armAdStartBudget(e);const r=0===t&&h?.breakId===A&&h.assetId===e.id&&h.uri===u&&h.player===n,i=!r&&this.deps.takeQueuedSuccessor(A,e.id,u);if(i)this.deps.reportPhase("load-skipped-queued");else if(r)this.deps.reportPhase("load-skipped-preloaded");else{if(this.deps.reportPhase("load-start"),!await this.deps.loadWithinBudget(()=>n.load(u),{breakId:A,assetId:e.id,uri:u})||!l())return;this.deps.reportPhase("load-resolved")}this.reportPreparedDuration(n,d,e,t,s);const o=n.videoElement;if(o&&(o.style.cssText="display:block;width:100%;height:100%;object-fit:contain;"),this.deps.reportPhase("play-called"),o&&this.deps.surfaceReveal.watchVideo(o),this.state.firstFrameGateActive&&!this.state.firstFrameGateShown){const e=this.deps.waitForFirstFrame();await this.playRetryingInterruption(n,A),await e}else{let e=null;const t=await Promise.race([this.playRetryingInterruption(n,A).then(()=>!0),new Promise(t=>{e=setTimeout(()=>t(!1),this.deps.remainingAdStartBudgetMs())})]);if(e&&clearTimeout(e),!l())return;if(!t)return void this.deps.onAdError(new a(`Ad did not start within ${this.state.adStartBudgetMs}ms ad-start budget`,"DA-AD-START-TIMEOUT"))}if(!l())return;this.state.adStartDeadlineMs=null,this.state.adStartBudgetMs=0,i&&this.state.startPlaybackCutoff?.(),this.deps.transferPictureInPictureToAd();const c=this.deps.getSequencer();if(c){const t=this.deps.isGamPodAsset(e);t&&this.deps.beginGamPodTracking(e),await this.deps.drive(c.onOutcome({kind:"asset-started"})),!t&&l()&&this.deps.queueUpcomingAd()}else this.state.callbacks?.onAdStart(this.currentBreak,e,t,s)}catch(e){if(!l())return;this.deps.onAdError(o(e))}}reportPreparedDuration(e,t,s,r,i){const a=e.duration;if(!Number.isFinite(a)||a<=0)return;const d=s.duration;this.deps.diagnose("DA-AD-DURATION-PREPARED",`Ad media for break ${t.id} prepared: ${a}s of playable ad`,{breakId:t.id,assetId:s.id,index:r,total:i,preparedDurationSec:a,..."number"==typeof d?{declaredDurationSec:d}:{},breakDurationSec:this.state.effectiveBreakDurationSec??t.duration})}async playRetryingInterruption(e,t){const s=this.state.preloadGeneration;try{await e.play()}catch(r){if(s!==this.state.preloadGeneration||!function(e){return"AbortError"===e?.name}(r)||!this.isStartingBreak(t))throw r;this.deps.diagnose("DA-AD-START-INTERRUPTED","Ad play() was interrupted by a browser-initiated pause before the ad started; play() re-issued once.",{breakId:t,error:r instanceof Error?r.message:String(r)}),this.deps.reportPhase("play-retried"),await e.play()}}isStartingBreak(e){return this.state.isPlaying&&this.state.currentBreak?.id===e}playVastAsset(e,s,r){if(!this.currentBreak||!this.deps.getAdPlayer())return;const i=this.deps.getVariant(this.currentBreak)?.format??t.SINGLE,n=d({format:i,ssai:!1});if(!n.admitted)return this.deps.diagnose(n.diagnostic??"DA-VAST-UNSUPPORTED-FORMAT",`VAST asset '${e.id}' is not supported in '${i}' breaks (single/double/lshape_ad only); skipping.`,{breakId:this.currentBreak.id,assetId:e.id,format:i}),void this.deps.onAdError(new a(`VAST not supported in ${i} format`,n.diagnostic??"DA-VAST-UNSUPPORTED-FORMAT"));if(!c())return this.deps.diagnose("DA-VAST-IMA-SDK-MISSING","Google IMA client-side SDK (ima3.js) is unavailable; cannot play VAST ad.",{breakId:this.currentBreak.id,assetId:e.id}),void this.deps.onAdError(new a("IMA client-side SDK (ima3.js) not available","DA-VAST-IMA-SDK-MISSING"));const o=this.deps.buildVastTagUrl(e,this.currentBreak);if(!o)return void this.deps.onAdError(new a("No valid VAST tag URL for asset","DA-AD-URI-MISSING"));const p=this.deps.getAdPlayer(),l=p?.videoElement,u=this.deps.getAdContainer();if(!u||!l||!p)return void this.deps.onAdError(new a("VAST playout requires an ad container and ad video element","DA-AD-URI-MISSING"));this.deps.revealFirstFrame(),this.deps.surfaceReveal.watch(e.id,this.currentBreak.id),l.style.cssText="display:block;width:100%;height:100%;object-fit:contain;",this.state.vastAdManager||(this.state.vastAdManager=new h(u,l,this.deps.debug)),this.state.vastAdManager.setCallbacks({onAdStarted:t=>{if(!this.currentBreak)return;const i=Array.from(u.querySelectorAll("video")).find(e=>!e.paused);this.deps.surfaceReveal.watchVideo(i??l),this.state.startPlaybackCutoff?.(),this.deps.setVastPodPosition(t);const a=this.deps.getSequencer();a?this.deps.drive(a.onOutcome({kind:"asset-started"})):this.state.callbacks?.onAdStart(this.currentBreak,e,s,r,{podPosition:t})},onAdProgress:(t,s)=>{this.currentBreak&&(this.deps.recordAdProgress(t),this.state.callbacks?.onAdTimeUpdate(this.currentBreak,e,t,s))},onAdFirstQuartile:()=>{this.currentBreak&&this.deps.getGamEventCallbacks().onAdFirstQuartile?.(this.currentBreak,e)},onAdMidpoint:()=>{this.currentBreak&&this.deps.getGamEventCallbacks().onAdMidpoint?.(this.currentBreak,e)},onAdThirdQuartile:()=>{this.currentBreak&&this.deps.getGamEventCallbacks().onAdThirdQuartile?.(this.currentBreak,e)}}),this.deps.log("Playing VAST asset via IMA CSAI:",o);(this.state.vastAdManager.hasPreloadedManager()?(this.state.vastAdManager.setMuted(p.muted),this.state.vastAdManager.startPreloaded()):this.state.vastAdManager.playVast(o,{muted:p.muted})).then(()=>{this.deps.onAdEnded()},t=>{const s=t instanceof Error?t.message:String(t),r=/ima3\.js|client-side SDK/i.test(s)?"DA-VAST-IMA-SDK-MISSING":"DA-VAST-IMA-ERROR",i=t instanceof a?t:new a(s,r);this.deps.diagnose(r,s,{...this.currentBreak?{breakId:this.currentBreak.id}:{},assetId:e.id}),this.deps.onAdError(i)})}async playImageAsset(e,t,s){const r=this.currentBreak,i=this.deps.getAdMediaContainer();if(!r||!i)return;const d=this.state.effectiveBreakDurationSec??r.duration,n=await this.resolveAssetUri(e,d);if(!n)return void this.deps.onAdError(this.missingUriError(e));const o=document.createElement("img");o.className="dolby-overlay-image",o.style.cssText="display:block;width:100%;height:100%;object-fit:contain;",this.deps.setOverlayImage(o);const h=e.duration??d;o.onload=()=>{if(this.deps.getOverlayImage()!==o||!this.currentBreak)return;this.state.startPlaybackCutoff?.();const r=this.deps.getSequencer();r?this.deps.drive(r.onOutcome({kind:"asset-started"})):this.state.callbacks?.onAdStart(this.currentBreak,e,t,s),h>0?this.deps.setImageAssetTimer(setTimeout(()=>this.onImageAssetEnded(),1e3*h)):this.onImageAssetEnded()},o.onerror=()=>{this.deps.getOverlayImage()===o&&this.deps.onAdError(new a(`Overlay image failed to load: ${n}`,"DA-IMAGE-LOAD-FAILED"))},o.src=n,i.appendChild(o)}onImageAssetEnded(){const e=this.deps.getSequencer();if(!this.currentBreak||!this.state.isPlaying||!e)return;const t=this.deps.getImageAssetTimer();t&&(clearTimeout(t),this.deps.setImageAssetTimer(null)),this.deps.drive(e.onOutcome({kind:"asset-ended"}))}async resolveAssetUri(t,r,i=this.currentBreak){switch(t.type){case e.STATIC:return this.deps.buildStaticAssetUrl(t,i);case e.VENDOR:{const e=t;return e.vendor===s.GAM?await this.deps.buildGamUrl(e,r):(this.deps.log("Unsupported vendor:",e.vendor),null)}default:return this.deps.log("Unsupported asset type:",t.type),null}}adPlayerStreamRemainingSec(){const e=this.deps.getAdPlayer();if(!e)return 0;const{duration:t,currentTime:s}=e;if(!Number.isFinite(t)||t<=0)return 0;if(!Number.isFinite(s)||s<0)return 0;const r=t-s;return r>0?r:0}revealFirstFrame(){this.deps.revealFirstFrame()}toAdError(e){const t=e,s=n(e);if(t&&("number"==typeof t.code||t.message)){const e="number"==typeof t.code?` (code ${t.code})`:"",r=t.message?`: ${t.message}`:"";return new a(`Ad playback error${e}${r}`,s)}return new a("Ad playback error",s)}missingUriError(e){return this.deps.hasGamIdentityMismatch(e)?new a("GAM pod asset identity does not match the stream session","DA-GAM-POD-IDENTITY-MISMATCH"):new a("No valid URI for asset","DA-AD-URI-MISSING")}}
|