@dolby-optiview/ads-sdk-core 1.0.0-beta.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.
Files changed (187) hide show
  1. package/dist/OptiViewAds.d.ts +190 -0
  2. package/dist/OptiViewAds.js +1 -0
  3. package/dist/config/keys.d.ts +23 -0
  4. package/dist/config/keys.js +1 -0
  5. package/dist/diagnostics/DiagnosticBuffer.d.ts +33 -0
  6. package/dist/diagnostics/DiagnosticBuffer.js +1 -0
  7. package/dist/diagnostics/DiagnosticEvent.d.ts +71 -0
  8. package/dist/diagnostics/DiagnosticEvent.js +1 -0
  9. package/dist/diagnostics/debugLog.d.ts +8 -0
  10. package/dist/diagnostics/debugLog.js +1 -0
  11. package/dist/diagnostics/errorCodes.d.ts +45 -0
  12. package/dist/diagnostics/errorCodes.js +1 -0
  13. package/dist/diagnostics/index.d.ts +6 -0
  14. package/dist/diagnostics/index.js +1 -0
  15. package/dist/diagnostics/playbackErrorContext.d.ts +22 -0
  16. package/dist/diagnostics/playbackErrorContext.js +1 -0
  17. package/dist/generated/diagnosticCodes.d.ts +487 -0
  18. package/dist/generated/diagnosticCodes.js +1 -0
  19. package/dist/generated/version.d.ts +5 -0
  20. package/dist/generated/version.js +1 -0
  21. package/dist/id3/buildId3.d.ts +25 -0
  22. package/dist/id3/buildId3.js +1 -0
  23. package/dist/id3/index.d.ts +3 -0
  24. package/dist/id3/index.js +1 -0
  25. package/dist/id3/parseId3Geob.d.ts +28 -0
  26. package/dist/id3/parseId3Geob.js +1 -0
  27. package/dist/index.d.ts +45 -0
  28. package/dist/index.js +1 -0
  29. package/dist/interfaces/PlayerAdapter.d.ts +163 -0
  30. package/dist/interfaces/PlayerAdapter.js +1 -0
  31. package/dist/internal/index.d.ts +1 -0
  32. package/dist/internal/index.js +1 -0
  33. package/dist/server/index.d.ts +20 -0
  34. package/dist/server/index.js +1 -0
  35. package/dist/services/AdBreakSequencer.d.ts +146 -0
  36. package/dist/services/AdBreakSequencer.js +1 -0
  37. package/dist/services/AdPlayerController.d.ts +495 -0
  38. package/dist/services/AdPlayerController.js +1 -0
  39. package/dist/services/BreakScheduler.d.ts +215 -0
  40. package/dist/services/BreakScheduler.js +1 -0
  41. package/dist/services/BreakTransitionTimer.d.ts +48 -0
  42. package/dist/services/BreakTransitionTimer.js +1 -0
  43. package/dist/services/CmcdAdReporter.d.ts +81 -0
  44. package/dist/services/CmcdAdReporter.js +1 -0
  45. package/dist/services/GamPodAdTracker.d.ts +65 -0
  46. package/dist/services/GamPodAdTracker.js +1 -0
  47. package/dist/services/GamPodUrlBuilder.d.ts +30 -0
  48. package/dist/services/GamPodUrlBuilder.js +1 -0
  49. package/dist/services/GamStreamManager.d.ts +67 -0
  50. package/dist/services/GamStreamManager.js +1 -0
  51. package/dist/services/LensReporter.d.ts +30 -0
  52. package/dist/services/LensReporter.js +1 -0
  53. package/dist/services/ManifestService.d.ts +91 -0
  54. package/dist/services/ManifestService.js +1 -0
  55. package/dist/services/MetadataCueTracker.d.ts +44 -0
  56. package/dist/services/MetadataCueTracker.js +1 -0
  57. package/dist/services/PauseAdController.d.ts +108 -0
  58. package/dist/services/PauseAdController.js +1 -0
  59. package/dist/services/RewindRearm.d.ts +109 -0
  60. package/dist/services/RewindRearm.js +1 -0
  61. package/dist/services/SsaiController.d.ts +59 -0
  62. package/dist/services/SsaiController.js +1 -0
  63. package/dist/services/StitcherUrlBuilder.d.ts +34 -0
  64. package/dist/services/StitcherUrlBuilder.js +1 -0
  65. package/dist/services/VastAdManager.d.ts +89 -0
  66. package/dist/services/VastAdManager.js +1 -0
  67. package/dist/services/adPlayer/AdPlayerEvents.d.ts +39 -0
  68. package/dist/services/adPlayer/AdPlayerEvents.js +1 -0
  69. package/dist/services/adPlayer/AdPlayerState.d.ts +98 -0
  70. package/dist/services/adPlayer/AdPlayerState.js +1 -0
  71. package/dist/services/adPlayer/AdPreloader.d.ts +54 -0
  72. package/dist/services/adPlayer/AdPreloader.js +1 -0
  73. package/dist/services/adPlayer/AdStage.d.ts +57 -0
  74. package/dist/services/adPlayer/AdStage.js +1 -0
  75. package/dist/services/adPlayer/AssetParameterResolver.d.ts +44 -0
  76. package/dist/services/adPlayer/AssetParameterResolver.js +1 -0
  77. package/dist/services/adPlayer/AssetPlayback.d.ts +71 -0
  78. package/dist/services/adPlayer/AssetPlayback.js +1 -0
  79. package/dist/services/adPlayer/BreakSequenceDriver.d.ts +46 -0
  80. package/dist/services/adPlayer/BreakSequenceDriver.js +1 -0
  81. package/dist/services/adPlayer/ContentResumeCoordinator.d.ts +44 -0
  82. package/dist/services/adPlayer/ContentResumeCoordinator.js +1 -0
  83. package/dist/services/adPlayer/ContinueContentPlayout.d.ts +26 -0
  84. package/dist/services/adPlayer/ContinueContentPlayout.js +1 -0
  85. package/dist/services/adPlayer/GamPodBridge.d.ts +66 -0
  86. package/dist/services/adPlayer/GamPodBridge.js +1 -0
  87. package/dist/services/adPlayer/PauseAdPresenter.d.ts +60 -0
  88. package/dist/services/adPlayer/PauseAdPresenter.js +1 -0
  89. package/dist/services/adPlayer/PresentationModeTracker.d.ts +66 -0
  90. package/dist/services/adPlayer/PresentationModeTracker.js +1 -0
  91. package/dist/services/adPlayer/SharedElementBreakPlayer.d.ts +54 -0
  92. package/dist/services/adPlayer/SharedElementBreakPlayer.js +1 -0
  93. package/dist/services/adPlayer/VariantSelector.d.ts +38 -0
  94. package/dist/services/adPlayer/VariantSelector.js +1 -0
  95. package/dist/services/adPlayer/connectPauseAd.d.ts +10 -0
  96. package/dist/services/adPlayer/connectPauseAd.js +1 -0
  97. package/dist/services/adPlayer/liveEdge.d.ts +6 -0
  98. package/dist/services/adPlayer/liveEdge.js +1 -0
  99. package/dist/services/assetParameterMacroTable.d.ts +9 -0
  100. package/dist/services/assetParameterMacroTable.js +1 -0
  101. package/dist/services/assetParameterMacros.d.ts +69 -0
  102. package/dist/services/assetParameterMacros.js +1 -0
  103. package/dist/services/breakTiming.d.ts +60 -0
  104. package/dist/services/breakTiming.js +1 -0
  105. package/dist/services/channelSwitch.d.ts +32 -0
  106. package/dist/services/channelSwitch.js +1 -0
  107. package/dist/services/detectDeviceType.d.ts +15 -0
  108. package/dist/services/detectDeviceType.js +1 -0
  109. package/dist/services/detectInsertionMode.d.ts +38 -0
  110. package/dist/services/detectInsertionMode.js +1 -0
  111. package/dist/services/detectPreloadMode.d.ts +13 -0
  112. package/dist/services/detectPreloadMode.js +1 -0
  113. package/dist/services/ed25519.d.ts +19 -0
  114. package/dist/services/ed25519.js +1 -0
  115. package/dist/services/fetchWithTimeout.d.ts +35 -0
  116. package/dist/services/fetchWithTimeout.js +1 -0
  117. package/dist/services/lensBackend.d.ts +16 -0
  118. package/dist/services/lensBackend.js +1 -0
  119. package/dist/services/lensBackendDevelopment.d.ts +1 -0
  120. package/dist/services/lensBackendDevelopment.js +1 -0
  121. package/dist/services/lensBackendProduction.d.ts +9 -0
  122. package/dist/services/lensBackendProduction.js +1 -0
  123. package/dist/services/manifestSignature.d.ts +43 -0
  124. package/dist/services/manifestSignature.js +1 -0
  125. package/dist/services/manifestTrustedKeys.d.ts +9 -0
  126. package/dist/services/manifestTrustedKeys.js +1 -0
  127. package/dist/services/parseBreakManifest.d.ts +15 -0
  128. package/dist/services/parseBreakManifest.js +1 -0
  129. package/dist/services/parseVastCompanion.d.ts +31 -0
  130. package/dist/services/parseVastCompanion.js +1 -0
  131. package/dist/services/parseVastLinear.d.ts +36 -0
  132. package/dist/services/parseVastLinear.js +1 -0
  133. package/dist/services/pictureInPicture.d.ts +28 -0
  134. package/dist/services/pictureInPicture.js +1 -0
  135. package/dist/services/resolveAssetParameters.d.ts +6 -0
  136. package/dist/services/resolveAssetParameters.js +1 -0
  137. package/dist/services/resolveDeliveryMode.d.ts +54 -0
  138. package/dist/services/resolveDeliveryMode.js +1 -0
  139. package/dist/services/resolveGamVendorConfiguration.d.ts +55 -0
  140. package/dist/services/resolveGamVendorConfiguration.js +1 -0
  141. package/dist/services/resolvePauseResumePoint.d.ts +32 -0
  142. package/dist/services/resolvePauseResumePoint.js +1 -0
  143. package/dist/services/resolvePollingDecision.d.ts +24 -0
  144. package/dist/services/resolvePollingDecision.js +1 -0
  145. package/dist/services/resolveResumePoint.d.ts +38 -0
  146. package/dist/services/resolveResumePoint.js +1 -0
  147. package/dist/services/resolveTargetedUri.d.ts +22 -0
  148. package/dist/services/resolveTargetedUri.js +1 -0
  149. package/dist/services/resolveVastAdmission.d.ts +34 -0
  150. package/dist/services/resolveVastAdmission.js +1 -0
  151. package/dist/services/selectVariant.d.ts +47 -0
  152. package/dist/services/selectVariant.js +1 -0
  153. package/dist/services/urlOrigin.d.ts +23 -0
  154. package/dist/services/urlOrigin.js +1 -0
  155. package/dist/session/AdBreakStatusTracker.d.ts +49 -0
  156. package/dist/session/AdBreakStatusTracker.js +1 -0
  157. package/dist/session/BreakLifecycle.d.ts +62 -0
  158. package/dist/session/BreakLifecycle.js +1 -0
  159. package/dist/session/SessionStarter.d.ts +98 -0
  160. package/dist/session/SessionStarter.js +1 -0
  161. package/dist/session/SessionState.d.ts +37 -0
  162. package/dist/session/SessionState.js +1 -0
  163. package/dist/types/BreakManifest.d.ts +335 -0
  164. package/dist/types/BreakManifest.js +1 -0
  165. package/dist/types/Events.d.ts +245 -0
  166. package/dist/types/Events.js +1 -0
  167. package/dist/types/OptiViewAdsConfig.d.ts +425 -0
  168. package/dist/types/OptiViewAdsConfig.js +1 -0
  169. package/dist/types/VendorAdError.d.ts +12 -0
  170. package/dist/types/VendorAdError.js +1 -0
  171. package/dist/vendor/lens-sdk/THIRD-PARTY-NOTICES.txt +266 -0
  172. package/dist/vendor/lens-sdk/compat.d.ts +9 -0
  173. package/dist/vendor/lens-sdk/encoding-cmcd.d.ts +9 -0
  174. package/dist/vendor/lens-sdk/encoding-json.d.ts +9 -0
  175. package/dist/vendor/lens-sdk/encoding.d.ts +42 -0
  176. package/dist/vendor/lens-sdk/environment.d.ts +35 -0
  177. package/dist/vendor/lens-sdk/index.d.ts +3 -0
  178. package/dist/vendor/lens-sdk/index.js +1 -0
  179. package/dist/vendor/lens-sdk/legacy.d.ts +12 -0
  180. package/dist/vendor/lens-sdk/lens-core-Q5lbQGMC.js +1 -0
  181. package/dist/vendor/lens-sdk/lens-global.d.ts +23 -0
  182. package/dist/vendor/lens-sdk/lens.d.ts +21 -0
  183. package/dist/vendor/lens-sdk/transport.d.ts +46 -0
  184. package/dist/vendor/lens-sdk/types.d.ts +218 -0
  185. package/dist/vendor/lens-sdk/user-id.d.ts +5 -0
  186. package/dist/vendor/lens-sdk/validation.d.ts +30 -0
  187. package/package.json +57 -0
@@ -0,0 +1,38 @@
1
+ import { AdInsertionType } from '../types/OptiViewAdsConfig';
2
+ /** Inputs (seconds) for the portable resume-point rule, locked by the conformance harness. */
3
+ export interface ResumePointInput {
4
+ /**
5
+ * The content position where the break paused content. NOT the break's `start`: on the
6
+ * wallclock timebase that is an ISO timestamp unrelated to the content timeline.
7
+ */
8
+ anchor: number;
9
+ /** The break's configured (maximum) duration in seconds. */
10
+ duration: number;
11
+ /** Ad actually played this break; caps the offset. Non-positive or non-finite disables the cap. */
12
+ adDuration: number;
13
+ /** The client's insertion type, ONLY when set explicitly; `undefined` lets the manifest drive. */
14
+ insertionType?: AdInsertionType | undefined;
15
+ /** The manifest's seek length from the anchor. Absent lets the ad duration decide; `0` asks for no seek. */
16
+ resumeOffset?: number | undefined;
17
+ }
18
+ /** Which precedence level produced the offset. */
19
+ export type ResumePointSource = 'client' | 'manifest' | 'ad-duration';
20
+ /** The resolved resume decision. */
21
+ export interface ResumePoint {
22
+ /** Whether to seek at all. A zero offset means stay where content paused. */
23
+ seek: boolean;
24
+ /** Absolute content position to seek to. Only meaningful when `seek`. */
25
+ target: number;
26
+ /** The offset in seconds after precedence and clamping. */
27
+ offset: number;
28
+ /** The precedence level that produced the offset. */
29
+ source: ResumePointSource;
30
+ /** Whether the ad-duration cap shortened the offset. */
31
+ clamped: boolean;
32
+ }
33
+ /**
34
+ * Precedence: explicit client `insertionType` (`insertion` → 0, `replacement` → duration), then
35
+ * the manifest's `resumeOffset` (`0` included), then the ad duration. Capped at the ad duration.
36
+ * No timebase: identical on VOD and live.
37
+ */
38
+ export declare function resolveResumePoint(input: ResumePointInput): ResumePoint;
@@ -0,0 +1 @@
1
+ export function resolveResumePoint(e){const{anchor:t,duration:i,adDuration:n,insertionType:o,resumeOffset:r}=e;let s,u;void 0!==o?(s="insertion"===o?0:i,u="client"):void 0!==r?(s=r,u="manifest"):(s=n,u="ad-duration"),(!Number.isFinite(s)||s<0)&&(s=0);const a=Number.isFinite(n)&&n>0?n:0,c=s>0&&a>0&&a<s;return c&&(s=a),{seek:s>0,target:t+s,offset:s,source:u,clamped:c}}
@@ -0,0 +1,22 @@
1
+ import { AssetUri, DeviceType } from '../types/BreakManifest';
2
+ /**
3
+ * Resolve a `static`/`vast` asset's `uri` field (a plain string, or an
4
+ * `AssetUri[]` where each entry may carry `targeting.deviceType`) to the single
5
+ * URI to play, as a pure function shared (by behavioural contract) with the
6
+ * Kotlin and Swift cores and locked by the cross-language conformance harness.
7
+ *
8
+ * Selection order:
9
+ * 1. `uri` is a plain string → return it as-is (no targeting is possible).
10
+ * 2. `deviceType` is known and an entry's `targeting.deviceType` matches it →
11
+ * that entry's `value`.
12
+ * 3. Otherwise, the first entry with no `targeting.deviceType` (the manifest's
13
+ * default/untargeted URI) → that entry's `value`.
14
+ * 4. Otherwise (every entry is targeted and none matched — e.g. an unknown
15
+ * device class), fall back to `uris[0].value` so playback still proceeds
16
+ * with *some* URI rather than none.
17
+ * 5. An empty/missing array resolves to `null`.
18
+ *
19
+ * This is the single source of truth for `AdPlayerController.resolveUri` and
20
+ * the native asset-URI resolvers.
21
+ */
22
+ export declare function resolveTargetedUri(uri: string | AssetUri[], deviceType?: DeviceType): string | null;
@@ -0,0 +1 @@
1
+ export function resolveTargetedUri(e,r){if("string"==typeof e)return e;if(!Array.isArray(e)||0===e.length)return null;if(r){const t=e.find(e=>e.targeting?.deviceType===r);if(t)return t.value}const t=e.find(e=>void 0===e.targeting?.deviceType);return t?t.value:e[0].value}
@@ -0,0 +1,34 @@
1
+ import { BreakFormat } from '../types/BreakManifest';
2
+ /**
3
+ * The structured diagnostic a rejected VAST asset raises. A `null` diagnostic
4
+ * accompanies an admitted asset. Both codes come from the S1 taxonomy.
5
+ */
6
+ export type VastAdmissionDiagnostic = 'DA-VAST-SSAI-UNSUPPORTED' | 'DA-VAST-UNSUPPORTED-FORMAT';
7
+ /** Inputs for the portable VAST-admission decision (a `vast` asset is assumed). */
8
+ export interface VastAdmissionInput {
9
+ /** The break variant's format. */
10
+ format: BreakFormat;
11
+ /** Whether the session is running in SSAI (server-guided/stitched) mode. */
12
+ ssai: boolean;
13
+ }
14
+ /** The decision: whether a `vast` asset is playable, plus the rejection code if not. */
15
+ export interface VastAdmission {
16
+ admitted: boolean;
17
+ diagnostic: VastAdmissionDiagnostic | null;
18
+ }
19
+ /**
20
+ * Decide whether a `vast` (client-side / CSAI) asset may be played, as a pure
21
+ * function shared (by behavioural contract) with the Kotlin and Swift cores and
22
+ * locked by the cross-language conformance harness.
23
+ *
24
+ * VAST is **SGAI-only** and **linear-only**. In priority order:
25
+ * 1. SSAI mode → reject with `DA-VAST-SSAI-UNSUPPORTED` (SSAI never reads the
26
+ * break manifest; a `vast` asset here is a misconfiguration).
27
+ * 2. non-linear format (`overlay` / `lshape_content`) → reject with
28
+ * `DA-VAST-UNSUPPORTED-FORMAT`.
29
+ * 3. otherwise (`single` / `double` / `lshape_ad`) → admit.
30
+ *
31
+ * This is the single source of truth for the renderer format guard (see
32
+ * `AdPlayerController.playVastAsset` and the native `OverlayAdRenderer`s).
33
+ */
34
+ export declare function resolveVastAdmission(input: VastAdmissionInput): VastAdmission;
@@ -0,0 +1 @@
1
+ import{BreakFormat as t}from"../types/BreakManifest";export function resolveVastAdmission(i){const{format:s,ssai:a}=i;return a?{admitted:!1,diagnostic:"DA-VAST-SSAI-UNSUPPORTED"}:s===t.OVERLAY||s===t.LSHAPE_CONTENT?{admitted:!1,diagnostic:"DA-VAST-UNSUPPORTED-FORMAT"}:{admitted:!0,diagnostic:null}}
@@ -0,0 +1,47 @@
1
+ import { BreakFormat } from '../types/BreakManifest';
2
+ import type { Break, BreakVariant } from '../types/BreakManifest';
3
+ /**
4
+ * Break-variant selection per the manifest spec's `targeting`, locked across the three cores by
5
+ * the `variant-selection` conformance fixture. One ordered walk with two filters:
6
+ * 1. Device type: `targeting.deviceType` equals the detected class; no `deviceType` is a
7
+ * default that qualifies anywhere; an unrecognised value never qualifies and is NOT promoted
8
+ * to a default. With no detected device only defaults qualify.
9
+ * 2. Format playability: a qualifying variant the platform cannot render is passed over;
10
+ * unknown formats are never playable.
11
+ * Declaration order wins. The result is two-stage because the device filter gates the
12
+ * downgrade path: `selected` null but `deviceQualified` set means the declared formats cannot
13
+ * render here and the last-resort downgrades may apply; both null means the break must not begin.
14
+ */
15
+ /** The scheduled break formats a renderer can be asked to draw. */
16
+ export declare const RENDERABLE_BREAK_FORMATS: readonly BreakFormat[];
17
+ /** Every format ANY selection may consider known. */
18
+ export declare const KNOWN_BREAK_FORMATS: readonly string[];
19
+ /** Inputs the platform layer feeds into a selection. */
20
+ export interface VariantSelectionInputs {
21
+ /** Detected device class. Undefined = unknown: targeted variants never match, defaults still qualify. */
22
+ deviceType?: string | undefined;
23
+ /** Formats renderable in the CURRENT context (PiP narrows it, see {@link playableFormatsFor}). Omitted = all renderable. */
24
+ playableFormats?: readonly string[] | undefined;
25
+ }
26
+ /** The two-stage result of a selection — see the module doc. */
27
+ export interface VariantSelection {
28
+ /** The variant to play as declared; null when nothing passed both filters. */
29
+ selected: BreakVariant | null;
30
+ /** First variant passing the DEVICE filter with a KNOWN format: the downgrade candidate when `selected` is null. */
31
+ deviceQualified: BreakVariant | null;
32
+ /**
33
+ * Raw `format` strings this device WAS allowed to see but were passed over as unknown to this
34
+ * SDK version (absent format reported as `''`). Only variants actually examined are listed.
35
+ * Carried on the result so the selector stays pure; each core diagnoses from its own layer.
36
+ */
37
+ skippedUnknownFormats: string[];
38
+ }
39
+ /** The playable-format set for the web renderer's current context; native renderers mirror this table. */
40
+ export declare function playableFormatsFor(context: {
41
+ pictureInPicture?: boolean;
42
+ singleSurfaceDevice?: boolean;
43
+ }): readonly BreakFormat[];
44
+ /** The break's declared variants as a list (the manifest allows both shapes). */
45
+ export declare function variantsOf(breakInfo: Break): BreakVariant[];
46
+ /** Run the two-filter ordered walk (see the module doc). */
47
+ export declare function selectVariantDetailed(breakInfo: Break, inputs?: VariantSelectionInputs): VariantSelection;
@@ -0,0 +1 @@
1
+ import{BreakFormat as e}from"../types/BreakManifest";export const RENDERABLE_BREAK_FORMATS=[e.SINGLE,e.DOUBLE,e.LSHAPE_AD,e.LSHAPE_CONTENT,e.OVERLAY];export const KNOWN_BREAK_FORMATS=[...RENDERABLE_BREAK_FORMATS];export function playableFormatsFor(t){return t.pictureInPicture?[e.SINGLE,e.LSHAPE_CONTENT]:t.singleSurfaceDevice?RENDERABLE_BREAK_FORMATS.filter(t=>t!==e.DOUBLE):RENDERABLE_BREAK_FORMATS}export function variantsOf(e){const t=e.variant;return Array.isArray(t)?t:t?[t]:[]}function t(e,t){const n=e.targeting?.deviceType;return void 0===n||void 0!==t&&n===t}export function selectVariantDetailed(e,n={}){const r=n.playableFormats??RENDERABLE_BREAK_FORMATS;let i=null,o=null;const E=[];for(const A of variantsOf(e)){if(!t(A,n.deviceType))continue;const e=A.format,a=void 0!==e&&KNOWN_BREAK_FORMATS.includes(e);if(a||E.push(e??""),null===o&&a&&(o=A),null===i&&void 0!==e&&r.includes(e)&&(i=A),null!==i)break}return{selected:i,deviceQualified:o,skippedUnknownFormats:E}}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Origin derivation for diagnostics — portable brain behavior.
3
+ *
4
+ * The diagnostics report is part of the cross-language contract (AGENTS.md Core
5
+ * Principle 5), so this is hand-written rather than delegated to `new URL()`,
6
+ * `URLComponents` or `java.net.URI`: those disagree with each other on explicit
7
+ * default ports (`https://h:443` — JS drops the port, the others keep it), on
8
+ * userinfo, and on malformed input. A platform parser per core would give three
9
+ * subtly different reports for the same session. This rule is locked by the
10
+ * `url-origin-v1` conformance fixture; the Kotlin and Swift mirrors must stay
11
+ * character-for-character equivalent.
12
+ */
13
+ /**
14
+ * The origin of an absolute URL: `scheme://host` plus `:port` whenever a port
15
+ * was written, lowercased scheme and host, userinfo and path/query/fragment
16
+ * dropped. Returns `null` when the input is not an absolute URL (a relative
17
+ * manifest path is legitimate, it simply has no origin to report).
18
+ *
19
+ * Ports are never normalised away: `https://h:443` reports `https://h:443`, not
20
+ * `https://h`. Reporting what the integrator wrote keeps the three cores
21
+ * identical and keeps the report honest.
22
+ */
23
+ export declare function resolveUrlOrigin(url: string): string | null;
@@ -0,0 +1 @@
1
+ export function resolveUrlOrigin(e){const n=e.indexOf("://");if(n<=0)return null;const l=e.slice(0,n).toLowerCase();if(l.includes("/")||l.includes("?")||l.includes("#"))return null;const t=e.slice(n+3);let s=t.length;for(const e of["/","?","#"]){const n=t.indexOf(e);-1!==n&&n<s&&(s=n)}let c=t.slice(0,s);const i=c.lastIndexOf("@");-1!==i&&(c=c.slice(i+1));const o=c.lastIndexOf("]"),r=c.indexOf(":",-1===o?0:o);let f=c,u="";if(-1!==r){const e=c.slice(r+1);e.length>0&&/^[0-9]+$/.test(e)&&(f=c.slice(0,r),u=e)}return f=f.toLowerCase(),0===f.length?null:u.length>0?`${l}://${f}:${u}`:`${l}://${f}`}
@@ -0,0 +1,49 @@
1
+ import { Asset, Break, BreakFormat } from '../types/BreakManifest';
2
+ import { AdBreakStatus, OptiViewAdsEventMap, OptiViewAdsEventType } from '../types/Events';
3
+ import { BreakTransitionTimer } from '../services/BreakTransitionTimer';
4
+ export type EmitEvent = <T extends OptiViewAdsEventType>(event: T, data: OptiViewAdsEventMap[T]) => void;
5
+ export interface AdBreakStatusTrackerDeps {
6
+ emit: EmitEvent;
7
+ effectiveFormatOf: (breakInfo: Break) => BreakFormat | undefined;
8
+ secondsUntilBreak: (breakId: string) => number | undefined;
9
+ breakTransitionTimer: () => BreakTransitionTimer | undefined;
10
+ }
11
+ /** Playback-gated `AdBreakStatus` state machine behind `getAdBreakStatus()` and the `adbreakstatus` event. */
12
+ export declare class AdBreakStatusTracker {
13
+ private readonly deps;
14
+ adBreakStatus: AdBreakStatus;
15
+ activeBreak: Break | null;
16
+ activeBreakRemainingAtStart: number | null;
17
+ activeBreakStartTimestamp: number | null;
18
+ activeBreakElapsedSec: number;
19
+ currentAdStartElapsedSec: number;
20
+ currentAdIndex: number | null;
21
+ currentAdTotal: number | null;
22
+ currentAdTime: number | null;
23
+ currentAdDuration: number | null;
24
+ activeBreakWaiting: boolean;
25
+ activeBreakPlaying: boolean;
26
+ constructor(deps: AdBreakStatusTrackerDeps);
27
+ /** Current playback-gated ad break status; for polling when an `adbreakstatus` event was missed. */
28
+ getAdBreakStatus(): AdBreakStatus;
29
+ /** Pre-break warning: emits `adbreakstatus` with `phase: 'upcoming'`. */
30
+ onBreakWarning(breakInfo: Break, warningSec: number, secondsUntil: number): void;
31
+ /** Promote a break to `phase: 'active'`; `ticking` starts `false` until playback events toggle it. */
32
+ setAdBreakStatusActive(breakInfo: Break, breakRemainingSec: number): void;
33
+ /** Mark the active break visible and playing: the first-frame signal for held pre-rolls, the first `playing` otherwise. */
34
+ setAdBreakVisible(breakInfo: Break): void;
35
+ onAdStarted(breakInfo: Break, _asset: Asset, index: number, total: number): void;
36
+ onAdEnded(breakInfo: Break): void;
37
+ onAdTimeUpdated(breakInfo: Break, currentTime: number, duration: number): void;
38
+ onAdPlaybackWaiting(): void;
39
+ onAdPlaybackPlaying(): void;
40
+ private buildActiveAdBreakStatus;
41
+ /** Remaining break seconds; falls back to wall-clock for formats without per-ad `timeupdate` (e.g. backdrop). */
42
+ private computeBreakRemainingSec;
43
+ /** Ad progress is authoritative once it arrives; before then (IMA fetch, backdrop/image breaks) wall-clock keeps the countdown moving. */
44
+ currentBreakElapsedSec(): number;
45
+ resetAdBreakStatus(): void;
46
+ private emitAdBreakStatus;
47
+ /** Only compares the scalar fields that affect UI rendering; `break` identity is ignored. */
48
+ private statusesEqual;
49
+ }
@@ -0,0 +1 @@
1
+ export class AdBreakStatusTracker{constructor(t){this.deps=t,this.adBreakStatus={phase:"idle",break:null,ticking:!1},this.activeBreak=null,this.activeBreakRemainingAtStart=null,this.activeBreakStartTimestamp=null,this.activeBreakElapsedSec=0,this.currentAdStartElapsedSec=0,this.currentAdIndex=null,this.currentAdTotal=null,this.currentAdTime=null,this.currentAdDuration=null,this.activeBreakWaiting=!1,this.activeBreakPlaying=!1}getAdBreakStatus(){if("active"===this.adBreakStatus.phase)return{...this.adBreakStatus,breakRemainingSec:this.computeBreakRemainingSec()};if("upcoming"===this.adBreakStatus.phase&&this.activeBreak){const t=this.deps.secondsUntilBreak(this.activeBreak.id);return{...this.adBreakStatus,secondsUntilBreak:t}}return{...this.adBreakStatus}}onBreakWarning(t,e,i){this.activeBreak=t,this.emitAdBreakStatus({phase:"upcoming",break:t,format:this.deps.effectiveFormatOf(t),secondsUntilBreak:i,ticking:!1,warningSec:e})}setAdBreakStatusActive(t,e){this.activeBreak=t,this.activeBreakRemainingAtStart=e,this.activeBreakStartTimestamp=Date.now(),this.activeBreakElapsedSec=0,this.currentAdStartElapsedSec=0,this.currentAdIndex=null,this.currentAdTotal=null,this.currentAdTime=null,this.currentAdDuration=null,this.activeBreakWaiting=!1,this.activeBreakPlaying=!1,this.emitAdBreakStatus({phase:"active",break:t,format:this.deps.effectiveFormatOf(t),breakRemainingSec:this.activeBreakRemainingAtStart,ticking:!1})}setAdBreakVisible(t){this.activeBreakWaiting=!1,this.activeBreakPlaying=!0,this.emitAdBreakStatus({...this.buildActiveAdBreakStatus(t),ticking:!0})}onAdStarted(t,e,i,a){this.currentAdIndex=i,this.currentAdTotal=a,this.currentAdTime=null,this.currentAdDuration=null,this.currentAdStartElapsedSec=this.currentBreakElapsedSec(),this.activeBreakElapsedSec=this.currentAdStartElapsedSec,this.activeBreakWaiting=!1,this.activeBreakPlaying=!0,this.deps.breakTransitionTimer()?.settle("into-break","ad-to-ad","break-to-break"),this.emitAdBreakStatus({...this.buildActiveAdBreakStatus(t),adIndex:i,totalAds:a,adsRemaining:a-i,ticking:!0})}onAdEnded(t){null!==this.currentAdDuration&&Number.isFinite(this.currentAdDuration)?this.activeBreakElapsedSec=this.currentAdStartElapsedSec+this.currentAdDuration:null!==this.currentAdTime&&(this.activeBreakElapsedSec=this.currentAdStartElapsedSec+this.currentAdTime),this.currentAdIndex=null,this.currentAdTime=null,this.currentAdDuration=null,this.activeBreakPlaying=!1,this.emitAdBreakStatus({...this.buildActiveAdBreakStatus(t),adsRemaining:0,breakRemainingSec:this.computeBreakRemainingSec(),ticking:!1})}onAdTimeUpdated(t,e,i){this.currentAdTime=e,this.currentAdDuration=i,this.activeBreakElapsedSec=this.currentAdStartElapsedSec+e,this.activeBreakWaiting=!1,this.activeBreakPlaying=!0,this.emitAdBreakStatus({...this.buildActiveAdBreakStatus(t),breakRemainingSec:this.computeBreakRemainingSec(),ticking:!0})}onAdPlaybackWaiting(){this.activeBreakWaiting=!0,this.activeBreakPlaying=!1,this.activeBreak&&this.emitAdBreakStatus({...this.buildActiveAdBreakStatus(this.activeBreak),ticking:!1})}onAdPlaybackPlaying(){this.activeBreakWaiting=!1,this.activeBreakPlaying=!0,this.activeBreak&&this.emitAdBreakStatus({...this.buildActiveAdBreakStatus(this.activeBreak),ticking:!0})}buildActiveAdBreakStatus(t){return{phase:"active",break:t,format:this.deps.effectiveFormatOf(t),adIndex:this.currentAdIndex??void 0,totalAds:this.currentAdTotal??void 0,adsRemaining:null!==this.currentAdIndex&&null!==this.currentAdTotal?this.currentAdTotal-this.currentAdIndex:void 0,breakRemainingSec:this.computeBreakRemainingSec(),ticking:this.activeBreakPlaying&&!this.activeBreakWaiting}}computeBreakRemainingSec(){if(null!==this.activeBreakRemainingAtStart)return Math.max(0,this.activeBreakRemainingAtStart-this.currentBreakElapsedSec())}currentBreakElapsedSec(){let t=this.activeBreakElapsedSec;return 0===t&&null!==this.activeBreakStartTimestamp&&null===this.currentAdTime&&(t=(Date.now()-this.activeBreakStartTimestamp)/1e3),t}resetAdBreakStatus(){this.activeBreak=null,this.activeBreakRemainingAtStart=null,this.activeBreakStartTimestamp=null,this.activeBreakElapsedSec=0,this.currentAdStartElapsedSec=0,this.currentAdIndex=null,this.currentAdTotal=null,this.currentAdTime=null,this.currentAdDuration=null,this.activeBreakWaiting=!1,this.activeBreakPlaying=!1,this.emitAdBreakStatus({phase:"idle",break:null,ticking:!1})}emitAdBreakStatus(t){this.statusesEqual(this.adBreakStatus,t)||(this.adBreakStatus=t,this.deps.emit("adbreakstatus",{type:"adbreakstatus",timestamp:Date.now(),status:t}))}statusesEqual(t,e){return t.phase===e.phase&&t.format===e.format&&t.secondsUntilBreak===e.secondsUntilBreak&&t.breakRemainingSec===e.breakRemainingSec&&t.adIndex===e.adIndex&&t.totalAds===e.totalAds&&t.adsRemaining===e.adsRemaining&&t.ticking===e.ticking&&t.warningSec===e.warningSec&&t.break?.id===e.break?.id}}
@@ -0,0 +1,62 @@
1
+ import { Break, BreakFormat } from '../types/BreakManifest';
2
+ import { BreakScheduler, BreakStartContext } from '../services/BreakScheduler';
3
+ import { ManifestService } from '../services/ManifestService';
4
+ import { AdPlayerController } from '../services/AdPlayerController';
5
+ import { BreakTransitionTimer } from '../services/BreakTransitionTimer';
6
+ import { CmcdAdReporter } from '../services/CmcdAdReporter';
7
+ import { KnownDiagnosticCode } from '../diagnostics/errorCodes';
8
+ import { AdBreakStatusTracker, EmitEvent } from './AdBreakStatusTracker';
9
+ import { SessionState } from './SessionState';
10
+ export type Diagnose = (code: KnownDiagnosticCode, message: string, context?: Record<string, unknown>) => void;
11
+ export type Log = (...args: unknown[]) => void;
12
+ export interface BreakLifecycleDeps {
13
+ adPlayerController: AdPlayerController;
14
+ breakScheduler: BreakScheduler;
15
+ manifestService: ManifestService;
16
+ breakTransitionTimer: BreakTransitionTimer | undefined;
17
+ cmcdAdReporter: CmcdAdReporter;
18
+ adBreakStatusTracker: AdBreakStatusTracker;
19
+ emit: EmitEvent;
20
+ diagnose: Diagnose;
21
+ log: Log;
22
+ effectiveFormatOf: (breakInfo: Break) => BreakFormat | undefined;
23
+ onBreakSettled?: () => void;
24
+ }
25
+ /** Scheduler and ad-player callbacks for one SGAI break: event emission, status updates, teardown reporting. */
26
+ export declare class BreakLifecycle {
27
+ private readonly state;
28
+ private readonly deps;
29
+ private breakBeginEmittedId;
30
+ private pendingAdBreakBegin;
31
+ constructor(state: SessionState, deps: BreakLifecycleDeps);
32
+ /**
33
+ * Emit `adbreakbegin` at most once per break id. Routing every begin through here, plus a flush
34
+ * before any terminal event, keeps the break bracketed even when the ad errors before its first frame.
35
+ */
36
+ emitAdBreakBegin(brk: Break, context?: BreakStartContext): void;
37
+ /** Flush a deferred (first-frame-gated) `adbreakbegin`, if one is pending. */
38
+ flushPendingAdBreakBegin(): void;
39
+ onBreakStart(breakInfo: Break, context: BreakStartContext): Promise<void>;
40
+ /** A chained break begins (overlay held, content not resumed): emit `adbreakbegin`, promote it to ACTIVE, keep polling. */
41
+ onChainedBreakBegin(next: Break): void;
42
+ onBreakEnd(breakInfo: Break): void;
43
+ /**
44
+ * A content-covering break ended before its content pause landed, so no player transition
45
+ * announces the resume; emit the content `playing` the resumed path would have produced.
46
+ * Only the deferred-pause renderers (Kotlin/Swift) signal this; kept in the TS reference for lockstep.
47
+ */
48
+ onBreakEndedWithoutContentPause(): void;
49
+ /**
50
+ * The ACTIVE break disappeared from a polled manifest (the manifest spec's "early return").
51
+ * Ends it through the controller's abort path: balanced `adend` + `adbreakend`, resume at
52
+ * `break.start + seconds played`, chaining suppressed. If the removal races the natural end,
53
+ * the scheduler entry is completed so it cannot linger ACTIVE.
54
+ */
55
+ onBreakAborted(breakInfo: Break): void;
56
+ /**
57
+ * Report a break still in flight at teardown. Web aborts it (`stopActiveBreak`), but an
58
+ * `adbegin` already emitted never gets its `adend` — the same shape a stalled ad produces.
59
+ * `unbalancedAdBegin` tells them apart. Must run before `resetAdBreakStatus()` clears the evidence.
60
+ */
61
+ reportAbandonedBreak(cause: string): void;
62
+ }
@@ -0,0 +1 @@
1
+ import{BreakFormat as e}from"../types/BreakManifest";import{playbackErrorContext as t}from"../diagnostics/playbackErrorContext";export class BreakLifecycle{constructor(e,t){this.state=e,this.deps=t,this.breakBeginEmittedId=null,this.pendingAdBreakBegin=null}emitAdBreakBegin(e,t){this.breakBeginEmittedId!==e.id&&(this.breakBeginEmittedId=e.id,this.pendingAdBreakBegin=null,this.deps.emit("adbreakbegin",{type:"adbreakbegin",timestamp:Date.now(),break:e,format:this.deps.effectiveFormatOf(e),elapsedSec:t?.elapsedSec??0,remainingSec:t?.remainingSec??e.duration??0,...t?.tuneIn?{tuneIn:{elapsedSec:t.elapsedSec,remainingSec:t.remainingSec}}:{}}))}flushPendingAdBreakBegin(){this.pendingAdBreakBegin?.()}async onBreakStart(a,i){if(this.state.snapbackSeekInFlight=!1,this.deps.adPlayerController.isAdPlaying())return this.deps.diagnose("DA-BREAK-SUPPRESSED-OVERLAP",`Break suppressed (ad already playing): ${a.id}`,{breakId:a.id}),this.deps.breakScheduler.completeBreak(a.id),void this.deps.onBreakSettled?.();this.deps.adPlayerController.diagnoseUnknownVariantFormats(a);const d=this.deps.adPlayerController.breakNothingToPlayReason(a);if(d)return this.deps.log("Break not begun (no playable variant):",a.id,d),this.deps.diagnose("DA-BREAK-NO-PLAYABLE-ASSET",`break ${a.id} has no playable variant (${d}); it will not begin.`,{breakId:a.id,reason:d}),this.deps.emit("aderror",{type:"aderror",timestamp:Date.now(),source:"ad",break:a,format:this.deps.effectiveFormatOf(a),error:new Error(`Break ${a.id} has no playable variant (${d})`)}),this.deps.breakScheduler.completeBreak(a.id),void this.deps.onBreakSettled?.();this.deps.log("Break starting:",a.id,i.tuneIn?"(tune-in)":""),this.deps.breakTransitionTimer?.mark("into-break",a.id),this.breakBeginEmittedId=null,this.pendingAdBreakBegin=null;const r=this.state.heldImmediatePreRollId===a.id,s=this.deps.effectiveFormatOf(a),n=s===e.SINGLE||s===e.LSHAPE_AD,o=r&&n;r&&(this.state.heldImmediatePreRollId=null),this.deps.adBreakStatusTracker.setAdBreakStatusActive(a,void 0!==i.remainingSec?i.remainingSec:a.duration),o||this.emitAdBreakBegin(a,i),this.deps.manifestService.setActive(!0),this.deps.breakScheduler.claimChain(a.id);const k={onAdStart:(e,t,a,i,d)=>{this.deps.adBreakStatusTracker.onAdStarted(e,t,a,i),this.deps.emit("adbegin",{type:"adbegin",timestamp:Date.now(),break:e,format:this.deps.effectiveFormatOf(e),asset:t,adIndex:a,totalAds:i,...d?.adId?{adId:d.adId}:{},...d?.creativeId?{creativeId:d.creativeId}:{},...void 0!==d?.podPosition?{podPosition:d.podPosition}:{}})},onAdEnd:(e,t,a,i,d)=>{this.deps.adBreakStatusTracker.onAdEnded(e),a+1<i&&this.deps.breakTransitionTimer?.mark("ad-to-ad",e.id),this.deps.emit("adend",{type:"adend",timestamp:Date.now(),break:e,format:this.deps.effectiveFormatOf(e),asset:t,adIndex:a,totalAds:i,...d?.adId?{adId:d.adId}:{},...d?.creativeId?{creativeId:d.creativeId}:{}})},onAdError:(e,a,i)=>{this.flushPendingAdBreakBegin(),this.deps.diagnose("DA-AD-PLAYBACK-ERROR",i.message,{breakId:e.id,assetId:a?.id,...t(i)}),this.deps.emit("aderror",{type:"aderror",timestamp:Date.now(),source:"ad",break:e,format:this.deps.effectiveFormatOf(e),...a?{asset:a}:{},error:i})},onAdTimeUpdate:(e,t,a,i)=>{this.deps.adBreakStatusTracker.onAdTimeUpdated(e,a,i),this.deps.emit("adtimeupdate",{type:"adtimeupdate",timestamp:Date.now(),break:e,format:this.deps.effectiveFormatOf(e),asset:t,currentTime:a,duration:i})},onAdWaiting:(e,t)=>{this.deps.adBreakStatusTracker.onAdPlaybackWaiting(),this.deps.emit("waiting",{type:"waiting",timestamp:Date.now(),source:"ad",break:e,asset:t})},onAdPlaying:(e,t)=>{this.deps.adBreakStatusTracker.onAdPlaybackPlaying(),this.deps.emit("playing",{type:"playing",timestamp:Date.now(),source:"ad",break:e,asset:t})},onBreakEnd:e=>{this.onBreakEnd(e)},onBreakEndedWithoutContentPause:()=>{this.onBreakEndedWithoutContentPause()},onChainedBreakBegin:e=>{this.onChainedBreakBegin(e)},onChainedBreakSkipped:e=>{this.deps.log("Chained break skipped (no playable variant):",e.id),this.deps.breakScheduler.completeBreak(e.id)},onContentResumeSeek:(e,t)=>{this.deps.breakScheduler.notifyResumeSeek(e,t)},onContentReloading:e=>{this.deps.breakScheduler.setContentReloading(e)}};o&&(this.pendingAdBreakBegin=()=>this.emitAdBreakBegin(a,i),k.onBreakVisible=e=>{this.deps.adBreakStatusTracker.setAdBreakVisible(e),this.flushPendingAdBreakBegin()}),await this.deps.adPlayerController.playBreak(a,k,{effectiveDurationSec:i.remainingSec,firstFrameGate:o})}onChainedBreakBegin(e){this.deps.log("Chained break starting:",e.id),this.deps.breakTransitionTimer?.mark("break-to-break",e.id),this.deps.breakScheduler.activateChainedBreak(e.id),this.deps.manifestService.setActive(!0),this.deps.adBreakStatusTracker.setAdBreakStatusActive(e,e.duration),this.emitAdBreakBegin(e)}onBreakEnd(e){this.deps.log("Break ended:",e.id),this.deps.breakTransitionTimer?.mark("out-of-break",e.id),this.deps.breakScheduler.completeBreak(e.id),this.deps.manifestService.setActive(!1),this.deps.adBreakStatusTracker.resetAdBreakStatus(),this.flushPendingAdBreakBegin(),this.deps.emit("adbreakend",{type:"adbreakend",timestamp:Date.now(),break:e,format:this.deps.effectiveFormatOf(e)}),this.deps.onBreakSettled?.()}onBreakEndedWithoutContentPause(){this.deps.breakTransitionTimer?.settle("out-of-break"),this.deps.emit("playing",{type:"playing",timestamp:Date.now(),source:"content"})}onBreakAborted(e){if(this.deps.adPlayerController.isAdPlaying()){const t=this.deps.adBreakStatusTracker.currentBreakElapsedSec();this.deps.log(`Early return — ending break '${e.id}' at ${t.toFixed(2)}s`),this.deps.cmcdAdReporter.onBreakAborted(e.id),this.deps.adPlayerController.abortBreak(t)}else this.deps.breakScheduler.completeBreak(e.id),this.deps.onBreakSettled?.()}reportAbandonedBreak(e){const t=this.deps.adBreakStatusTracker.activeBreak;if(!t)return;const a=this.deps.adBreakStatusTracker.currentAdIndex;this.deps.diagnose("DA-BREAK-ABANDONED",`Session torn down (${e}) while break ${t.id} was still in flight`+(null!==a?"; an ad began and will never end.":"."),{breakId:t.id,cause:e,phase:this.deps.adBreakStatusTracker.adBreakStatus.phase,unbalancedAdBegin:null!==a,...null!==a?{adIndex:a}:{},...null!==this.deps.adBreakStatusTracker.currentAdTotal?{totalAds:this.deps.adBreakStatusTracker.currentAdTotal}:{}})}}
@@ -0,0 +1,98 @@
1
+ import { PlayerAdapter } from '../interfaces/PlayerAdapter';
2
+ import { BreakManifest } from '../types/BreakManifest';
3
+ import { OptiViewAdsMode, SessionConfig, SsaiSessionConfig, StartSessionConfig } from '../types/OptiViewAdsConfig';
4
+ import { ManifestService } from '../services/ManifestService';
5
+ import { BreakScheduler } from '../services/BreakScheduler';
6
+ import { AdPlayerController } from '../services/AdPlayerController';
7
+ import { PauseAdController } from '../services/PauseAdController';
8
+ import { LensReporter } from '../services/LensReporter';
9
+ import { CmcdAdReporter } from '../services/CmcdAdReporter';
10
+ import { AdBreakStatusTracker, EmitEvent } from './AdBreakStatusTracker';
11
+ import { BreakLifecycle, Diagnose, Log } from './BreakLifecycle';
12
+ import { SessionState } from './SessionState';
13
+ export interface SessionStarterDeps {
14
+ player: PlayerAdapter;
15
+ debug: boolean;
16
+ mode: OptiViewAdsMode;
17
+ hasExplicitMode: boolean;
18
+ continueContentDuringBreak: boolean;
19
+ manifestService: ManifestService | undefined;
20
+ breakScheduler: BreakScheduler | undefined;
21
+ adPlayerController: AdPlayerController | undefined;
22
+ pauseAdController: PauseAdController | undefined;
23
+ breakLifecycle: BreakLifecycle | undefined;
24
+ cmcdAdReporter: CmcdAdReporter;
25
+ lensReporter: LensReporter;
26
+ adBreakStatusTracker: AdBreakStatusTracker;
27
+ emit: EmitEvent;
28
+ diagnose: Diagnose;
29
+ log: Log;
30
+ endSessionInternal: (cause: string) => void;
31
+ notedManifestBreakIds: (manifest: {
32
+ breaks: Array<{
33
+ id: string;
34
+ }>;
35
+ }) => string[];
36
+ }
37
+ /** startSession() flow: validation, delivery-mode steering, GAM/SSAI start, scheduler and polling start. */
38
+ export declare class SessionStarter {
39
+ private readonly state;
40
+ private readonly deps;
41
+ constructor(state: SessionState, deps: SessionStarterDeps);
42
+ /**
43
+ * Start monetization for a piece of content: begins manifest polling and (if configured)
44
+ * GAM session initialization. Ends any previously active session first.
45
+ */
46
+ startSession(config: StartSessionConfig): Promise<void>;
47
+ /**
48
+ * Content already playing when the session starts emits no further `playing`; the pre-roll
49
+ * anchor (spec `event: 'start'`) is then the session start itself.
50
+ */
51
+ private markPlaybackStartIfPlaying;
52
+ /**
53
+ * Tear down a session that failed to start and report it. `hostOk`/`hostFail` say whether this
54
+ * host was serving this client moments ago — a backend to escalate vs a network path to fix.
55
+ */
56
+ failSessionStart(error: unknown): Error;
57
+ /**
58
+ * Resolve the delivery mode once, on the first manifest fetch, and pin it. Manifest `delivery`
59
+ * rules are evaluated against the device type; an explicit constructor `mode` wins.
60
+ * Absent/unmatched rules and unrecognized values resolve to sgai.
61
+ */
62
+ resolveSessionDeliveryMode(manifest: BreakManifest, source?: 'manifest' | 'channel-switch'): OptiViewAdsMode;
63
+ /**
64
+ * Manifest-steered ssai: play the DAI stitched stream through the content player, keep
65
+ * polling the break manifest, and schedule only event-triggered breaks client-side.
66
+ * Throws {@link SsaiUnavailableError} when no vendor configuration supports it (no silent sgai fallback).
67
+ */
68
+ startSteeredSsaiSession(manifest: BreakManifest, config: SessionConfig, startStep: (step: string) => void): Promise<void>;
69
+ private createSsaiController;
70
+ /** Start the break scheduler with the SDK's standard callbacks (both sgai and steered ssai). */
71
+ startBreakScheduler(): void;
72
+ /** Start manifest polling. Ordinary updates keep the current delivery mode. */
73
+ startManifestPolling(): void;
74
+ applyPendingChannelSwitch(): void;
75
+ private applyManifestUpdate;
76
+ private diagnoseIgnoredDeliveryModeChange;
77
+ /**
78
+ * A poll returned a manifest of another channel: re-align the ad setup after the current break
79
+ * settles (the latest poll wins). Scheduler ledgers are rebuilt, GAM is re-opened when its
80
+ * identity changes, CMCD/Lens roll to the new channel, and startup/runtime asset-parameter
81
+ * layers are re-applied. Delivery mode is resolved again for the new channel. Before playback starts,
82
+ * the new manifest's `adStartDelay` applies; afterwards, the delay in effect stays.
83
+ */
84
+ private switchChannel;
85
+ private emitChannelChange;
86
+ private applySameModeChannelSwitch;
87
+ private switchToSteeredSsai;
88
+ private switchToSgai;
89
+ /**
90
+ * Resolve the GAM stream identity from the manifest and open the IMA/DAI session. GAM failure
91
+ * is non-fatal: non-GAM breaks still schedule, only the GAM assets are skipped.
92
+ */
93
+ startGamForManifest(manifest: BreakManifest, sessionParams: Record<string, string> | undefined, startStep: (step: string) => void): Promise<void>;
94
+ /** SSAI session: create the IMA DAI stream, load the stitched master, wire timed-metadata forwarding. Throws on failure. */
95
+ startSsaiSession(config: SsaiSessionConfig, generation: number): Promise<void>;
96
+ /** Whether a startSession() begun at {@link generation} was invalidated by a later end/destroy/restart. */
97
+ isStaleStart(generation: number): boolean;
98
+ }
@@ -0,0 +1 @@
1
+ import{getPreRollDelay as e}from"../types/BreakManifest";import{manifestHasGamAssets as s,resolveGamVendorConfiguration as t}from"../services/resolveGamVendorConfiguration";import{SsaiController as i}from"../services/SsaiController";import{resolveUrlOrigin as a}from"../services/urlOrigin";import{filterSsaiClientBreaks as r,resolveStitchedStreamVendor as n,SsaiUnavailableError as o}from"../services/resolveDeliveryMode";import{detectDeviceType as d}from"../services/detectDeviceType";import{decideDeliveryMode as l,detectChannelSwitch as h,gamPodIdentityChanged as c}from"../services/channelSwitch";function p(e){return"stitcherUrl"in e}function S(e){return"string"==typeof e&&""!==e.trim()}export class SessionStarter{constructor(e,s){this.state=e,this.deps=s}async startSession(s){if("ssai"===this.deps.mode){if(!p(s)||!S(s.stitcherUrl))throw new Error('[OptiViewAds] mode "ssai" requires a stitcherUrl in startSession().')}else if(p(s)||!S(s.manifestUrl))throw new Error("[OptiViewAds] startSession requires a manifestUrl (the full break-manifest URL).");this.state.sessionActive&&(this.deps.log("Ending previous session before starting new one"),this.deps.endSessionInternal("session-restart"));const t=this.state.sessionGeneration;if(this.state.sessionActive=!0,this.deps.lensReporter.startSession(),p(s))return s.adInsertionType&&this.deps.diagnose("DA-INSERTION-TYPE-IGNORED-SSAI",`adInsertionType "${s.adInsertionType}" ignored in SSAI mode; the stitcher controls ad insertion.`,{adInsertionType:s.adInsertionType}),void await this.startSsaiSession(s,t);const i=s.manifestUrl;this.state.sessionConfig=s,this.state.manifestOrigin=a(i),this.deps.manifestService.setManifestUrl(i),this.deps.log("Session starting, manifest URL:",i);const r=Date.now(),n=e=>{this.deps.diagnose("DA-SESSION-STARTING",`Session start: ${e}`,{step:e,manifestUrl:i,elapsedMs:Date.now()-r})};n("begin");const o=s.assetParameters;let d;this.deps.adPlayerController.setSessionAssetParameters(o),this.deps.adPlayerController.setSessionAssetParameterMacros(s.assetParameterMacros),this.state.configSummary.gam&&(this.state.configSummary.gam={...this.state.configSummary.gam,hasAssetParameters:!!o}),n("manifest-fetch");try{d=await this.deps.manifestService.fetchManifest()}catch(e){if(this.isStaleStart(t))return;throw this.failSessionStart(e)}if(this.deps.cmcdAdReporter.onSessionStart(d),!this.isStaleStart(t))if(this.state.sessionManifest=d,"ssai"!==this.resolveSessionDeliveryMode(d))try{if(await this.startGamForManifest(d,o,n),this.isStaleStart(t))return;const a=s.adInsertionType;this.deps.adPlayerController.setBreakResumePolicy(a,d.timebase,this.deps.continueContentDuringBreak),this.deps.diagnose("DA-INSERTION-TYPE-RESOLVED",void 0===a?`Ad insertion type: unset — the manifest's resumeOffset drives the resume point (timebase: ${d.timebase})`:`Ad insertion type: ${a}, client-set (timebase: ${d.timebase})`,{adInsertionType:a??null,source:void 0===a?"unset":"client",timebase:d.timebase}),n("scheduling"),this.deps.breakScheduler.updateManifest(d),this.deps.pauseAdController.updateManifest(d),this.state.pauseAdTickInterval=setInterval(()=>this.deps.pauseAdController?.tick(),250);const r=(d.adStartDelay??0)>0?void 0:d.breaks.find(s=>0===e(s));r?(this.deps.adPlayerController.holdContentForPreRoll(r),this.state.heldImmediatePreRollId=r.id,this.deps.breakScheduler.markPlaybackStart()):this.markPlaybackStartIfPlaying(),this.startBreakScheduler(),this.startManifestPolling(),this.deps.diagnose("DA-SESSION-STARTED",`Session active for manifest ${i}`,{manifestUrl:i}),this.deps.log("Session active")}catch(e){if(this.isStaleStart(t))return;throw this.failSessionStart(e)}else await this.startSteeredSsaiSession(d,s,n)}markPlaybackStartIfPlaying(){this.deps.player.paused||this.deps.breakScheduler.markPlaybackStart()}failSessionStart(e){const s=e instanceof Error?e:new Error(String(e)),t=this.deps.manifestService?.takeLastFailure();return this.deps.diagnose("DA-MANIFEST-FETCH-FAILED",s.message,t?{...t}:void 0),this.state.sessionActive=!1,this.deps.cmcdAdReporter.onSessionEnd(),this.deps.lensReporter.endSession(),s}resolveSessionDeliveryMode(e,s="manifest"){const t=this.state.sessionDeliveryMode,i=l(e,d(),this.deps.hasExplicitMode?this.deps.mode:null,s);return null!==i.unknownMode&&this.deps.diagnose("DA-DELIVERY-MODE-UNKNOWN",`Delivery rule ${i.ruleIndex} carries unrecognized mode "${i.unknownMode}"; using sgai.`,{unknownMode:i.unknownMode,ruleIndex:i.ruleIndex}),this.deps.hasExplicitMode&&i.manifestMode!==i.mode&&this.deps.diagnose("DA-DELIVERY-MODE-OVERRIDDEN",`Manifest delivery rules resolve "${i.manifestMode}" but the explicit config mode "${i.mode}" takes precedence.`,{manifestMode:i.manifestMode,configuredMode:i.mode}),this.state.sessionDeliveryMode=i.mode,this.deps.diagnose("DA-DELIVERY-MODE-RESOLVED",`Delivery mode: ${i.mode}`,{mode:i.mode,ruleIndex:i.ruleIndex,source:i.source,..."channel-switch"===s?{previousMode:t}:{}}),i.mode}async startSteeredSsaiSession(e,s,t){const i=n(e.vendorConfiguration);if(!i){const e="Manifest selected ssai delivery but no vendor configuration provides a stitched stream (no gam entry with an assetKey).";throw this.deps.diagnose("DA-SSAI-UNAVAILABLE",e),this.deps.manifestService.reset(),this.state.sessionActive=!1,new o(e)}try{t("ssai-live-stream"),this.state.steeredSsaiActive=!0,this.state.ssaiController=this.createSsaiController(i.networkCode),await this.state.ssaiController.startSteered(i,s.assetParameters,s.assetParameterMacros),t("scheduling");const a=r(e);this.deps.adPlayerController.setBreakResumePolicy(s.adInsertionType,e.timebase,!1),this.deps.breakScheduler.updateManifest(a),this.deps.pauseAdController.updateManifest(a),this.state.pauseAdTickInterval=setInterval(()=>this.deps.pauseAdController?.tick(),250),this.markPlaybackStartIfPlaying(),this.startBreakScheduler(),this.startManifestPolling(),this.deps.diagnose("DA-SESSION-STARTED",`SSAI session active (manifest-steered) for ${s.manifestUrl}`,{manifestUrl:s.manifestUrl}),this.deps.log("SSAI session active (manifest-steered)")}catch(e){const s=e instanceof Error?e:new Error(String(e));throw this.deps.diagnose("DA-SSAI-SESSION-FAILED",s.message),this.state.steeredSsaiActive=!1,this.deps.manifestService.reset(),this.state.sessionActive=!1,s}}createSsaiController(e){return new i(this.deps.player,{gam:this.state.steeredSsaiOptions?.streamActivityMonitorId?{streamActivityMonitorId:this.state.steeredSsaiOptions.streamActivityMonitorId}:{},ssai:{networkCode:e},maxBitrate:this.state.steeredSsaiOptions?.maxBitrate,autoplay:this.state.steeredSsaiOptions?.autoplay,debug:this.deps.debug},{emit:(e,s)=>this.deps.emit(e,s),diagnose:(e,s,t)=>this.deps.diagnose(e,s,t),log:(...e)=>this.deps.log(...e)})}startBreakScheduler(){this.deps.breakScheduler.start((e,s)=>{this.deps.breakLifecycle.onBreakStart(e,s)},e=>!this.deps.adPlayerController.isAdPlaying()&&(this.deps.adPlayerController.preload(e).catch(s=>{this.deps.diagnose("DA-PRELOAD-FAILED",s instanceof Error?s.message:String(s),{breakId:e.id})}),!0),(e,s,t)=>{this.deps.adBreakStatusTracker.onBreakWarning(e,s,t)},e=>{this.deps.breakLifecycle.onBreakAborted(e)})}startManifestPolling(){this.deps.manifestService.startPolling(e=>{if(!this.state.sessionActive)return;const s=this.deps.notedManifestBreakIds(e);this.deps.diagnose("DA-MANIFEST-POLLED","Manifest polled",{breaks:e.breaks.length,newBreakIds:s,pollingIntervalSeconds:this.deps.manifestService.currentPollingIntervalSeconds});const t=this.state.sessionManifest,i=t?h(t,e):null;if(this.state.pendingChannelSwitch||i&&this.deps.breakScheduler.getActiveBreak())return this.state.pendingChannelSwitch=e,void this.deps.log("Channel switch deferred until the active break ends");i?this.switchChannel(e,i):this.applyManifestUpdate(e)},e=>{this.state.sessionActive&&this.deps.diagnose("DA-MANIFEST-POLL-FAILED",e.message)})}applyPendingChannelSwitch(){if(!this.state.sessionActive||!this.state.pendingChannelSwitch)return;const e=this.state.pendingChannelSwitch;this.state.pendingChannelSwitch=null;const s=h(this.state.sessionManifest,e);s?this.switchChannel(e,s):this.applyManifestUpdate(e)}applyManifestUpdate(e){this.diagnoseIgnoredDeliveryModeChange(e);const s=t(e);this.state.steeredSsaiActive?this.state.ssaiController?.setManifestAssetParameters(s.ssai?.assetParameters):this.deps.adPlayerController.setManifestAssetParameters(s.sgaiAssetParameters);const i=this.state.steeredSsaiActive?r(e):e;this.deps.breakScheduler.updateManifest(i),this.deps.pauseAdController.updateManifest(i)}diagnoseIgnoredDeliveryModeChange(e){if(!this.state.sessionDeliveryMode)return;const s=l(e,d(),null,"manifest");s.manifestMode!==this.state.sessionDeliveryMode&&this.deps.diagnose("DA-DELIVERY-MODE-CHANGE-IGNORED",`Polled manifest resolves delivery mode "${s.manifestMode}" but "${this.state.sessionDeliveryMode}" is pinned for this session.`,{polledMode:s.manifestMode,pinnedMode:this.state.sessionDeliveryMode})}async switchChannel(e,s){if(this.state.channelSwitchInFlight)return;const t=this.state.sessionConfig,i=this.state.sessionManifest,a={previousChannelId:s.previousChannelId,channelId:s.channelId,reason:s.reason,manifestUrl:t.manifestUrl};this.deps.diagnose("DA-MANIFEST-CHANNEL-CHANGED",`Manifest channel changed (${s.reason}): ${s.previousChannelId??"none"} -> ${s.channelId??"none"}`,a),this.state.sessionManifest=e,this.state.channelSwitchInFlight=!0;const r=this.state.sessionGeneration,n=this.state.sessionDeliveryMode??"sgai";try{let s=this.resolveSessionDeliveryMode(e,"channel-switch");if(this.deps.cmcdAdReporter.onSessionEnd(),this.deps.cmcdAdReporter.onSessionStart(e),this.deps.lensReporter.endSession(),this.deps.lensReporter.startSession(),"ssai"===s&&"sgai"===n){const a=await this.switchToSteeredSsai(e,t);if(this.isStaleStart(r))return;a||(s="sgai",this.state.sessionDeliveryMode="sgai",await this.applySameModeChannelSwitch(i,e,t,r))}else"sgai"===s&&"ssai"===n?await this.switchToSgai(e,t,r):await this.applySameModeChannelSwitch(i,e,t,r);if(this.isStaleStart(r))return}finally{this.state.channelSwitchInFlight=!1}this.emitChannelChange({...a,previousDeliveryMode:n,deliveryMode:this.state.sessionDeliveryMode??n})}emitChannelChange(e){this.deps.emit("adchannelchange",{type:"adchannelchange",timestamp:Date.now(),...e})}async applySameModeChannelSwitch(e,s,i,a){const n=this.state.steeredSsaiActive,o=n?r(s):s;if(this.deps.breakScheduler.resetForChannelSwitch(o),this.deps.pauseAdController.updateManifest(o),this.deps.adPlayerController.setBreakResumePolicy(i.adInsertionType,s.timebase,this.deps.continueContentDuringBreak),!n&&c(e,s)){const e=this.deps.adPlayerController.updatedAssetParameters;if(this.deps.adPlayerController.endGamSession(),this.deps.adPlayerController.setSessionAssetParameters(i.assetParameters),this.deps.adPlayerController.setSessionAssetParameterMacros(i.assetParameterMacros),await this.startGamForManifest(s,i.assetParameters,e=>{this.deps.diagnose("DA-SESSION-STARTING",`Channel switch: ${e}`,{step:e,manifestUrl:i.manifestUrl})}),this.isStaleStart(a))return;e&&this.deps.adPlayerController.updateAssetParameters(e)}else if(n){const e=t(s);this.state.ssaiController?.setManifestAssetParameters(e.ssai?.assetParameters)}else{const e=t(s);this.deps.adPlayerController.setManifestAssetParameters(e.sgaiAssetParameters)}}async switchToSteeredSsai(e,s){const t=n(e.vendorConfiguration);if(!t)return this.deps.diagnose("DA-SSAI-UNAVAILABLE","Channel switch resolved ssai delivery but no vendor configuration provides a stitched stream; the session stays in sgai.",{manifestUrl:s.manifestUrl,previousMode:"sgai"}),!1;const i=this.deps.adPlayerController,a=i.updatedAssetParameters;i.stopActiveBreak(),i.releaseContentHold(),i.endGamSession(),i.setSessionAssetParameters(s.assetParameters),i.setSessionAssetParameterMacros(s.assetParameterMacros),a&&i.updateAssetParameters(a);const o=this.createSsaiController(t.networkCode);this.state.ssaiController=o,this.state.steeredSsaiActive=!0,this.deps.diagnose("DA-SESSION-STARTING","Channel switch: ssai-live-stream",{step:"ssai-live-stream",manifestUrl:s.manifestUrl});try{await o.startSteered(t,s.assetParameters,s.assetParameterMacros),a&&o.updateAssetParameters(a);const n=r(e);return this.deps.breakScheduler.resetForChannelSwitch(n),this.deps.pauseAdController.updateManifest(n),i.setBreakResumePolicy(s.adInsertionType,e.timebase,!1),!0}catch(e){const s=e instanceof Error?e.message:String(e);return this.deps.diagnose("DA-SSAI-SESSION-FAILED",s),o.stop(),delete this.state.ssaiController,this.state.steeredSsaiActive=!1,!1}}async switchToSgai(e,s,t){this.state.ssaiController?.stop(),delete this.state.ssaiController,this.state.steeredSsaiActive=!1,this.deps.breakScheduler.resetForChannelSwitch(e),this.deps.pauseAdController.updateManifest(e);const i=this.deps.adPlayerController;i.setBreakResumePolicy(s.adInsertionType,e.timebase,this.deps.continueContentDuringBreak);const a=i.updatedAssetParameters;i.endGamSession(),i.setSessionAssetParameters(s.assetParameters),i.setSessionAssetParameterMacros(s.assetParameterMacros),await this.startGamForManifest(e,s.assetParameters,e=>{this.deps.diagnose("DA-SESSION-STARTING",`Channel switch: ${e}`,{step:e,manifestUrl:s.manifestUrl})}),this.isStaleStart(t)||a&&i.updateAssetParameters(a)}async startGamForManifest(e,i,a){const r=t(e);if(this.deps.adPlayerController.setManifestAssetParameters(r.sgaiAssetParameters),r.unknownTypes.length>0&&this.deps.diagnose("DA-VENDOR-CONFIG-UNKNOWN-TYPE",`Ignored ${r.unknownTypes.length} vendorConfiguration `+(1===r.unknownTypes.length?"entry":"entries")+" with unrecognised type: "+`${r.unknownTypes.join(", ")}.`,{types:r.unknownTypes}),this.deps.adPlayerController.isGamEnabled())if(r.identity){this.deps.diagnose("DA-GAM-CONFIG-RESOLVED",`GAM session identity: network ${r.identity.networkCode}, custom asset ${r.identity.customAssetKey}`,{networkCode:r.identity.networkCode,customAssetKey:r.identity.customAssetKey,channelId:e.channelId,duplicateEntriesIgnored:r.duplicateGam});try{a("gam-init"),await this.deps.adPlayerController.startGamSession(r.identity,i),this.deps.log("GAM session started")}catch(e){this.deps.diagnose("DA-GAM-SESSION-FAILED",e instanceof Error?e.message:String(e))}}else s(e)&&this.deps.diagnose("DA-GAM-CONFIG-MISSING",'The break manifest carries GAM pod assets but no usable "gam" vendorConfiguration entry; those assets will be skipped.',{reason:r.reason});else s(e)&&this.deps.diagnose("DA-GAM-CONFIG-MISSING","The break manifest carries GAM pod assets but GAM is disabled for this session (gam.enabled === false); those assets will be skipped.",{reason:"gam-disabled"})}async startSsaiSession(e,s){try{if(this.state.stitcherOrigin=a(e.stitcherUrl),this.state.configSummary.gam&&(this.state.configSummary.gam={...this.state.configSummary.gam,hasAssetParameters:!!e.assetParameters}),await this.state.ssaiController.start(e),this.isStaleStart(s))return;this.deps.diagnose("DA-SESSION-STARTED",`SSAI session active for ${e.stitcherUrl}`,{stitcherUrl:e.stitcherUrl}),this.deps.log("SSAI session active")}catch(e){if(this.isStaleStart(s))return;const t=e instanceof Error?e:new Error(String(e));throw this.deps.diagnose("DA-SSAI-SESSION-FAILED",t.message),this.state.sessionActive=!1,this.deps.cmcdAdReporter.onSessionEnd(),this.deps.lensReporter.endSession(),t}}isStaleStart(e){return e!==this.state.sessionGeneration&&(this.deps.log("Session start aborted — the session ended or was replaced while starting"),!0)}}
@@ -0,0 +1,37 @@
1
+ import { OptiViewAdsMode, SessionConfig } from '../types/OptiViewAdsConfig';
2
+ import { BreakManifest } from '../types/BreakManifest';
3
+ import { SsaiController } from '../services/SsaiController';
4
+ import { ConfigSummary } from '../diagnostics/DiagnosticEvent';
5
+ /** Mutable session state owned by `OptiViewAds` and shared by reference with the `session/` modules. */
6
+ export declare class SessionState {
7
+ sessionActive: boolean;
8
+ /**
9
+ * Bumped by `endSessionInternal`. startSession() re-checks it after every await: a
10
+ * mismatch means the session was ended or replaced mid-flight. A boolean cannot express
11
+ * "replaced" — the new session sets it back to true before the stale fetch resolves.
12
+ */
13
+ sessionGeneration: number;
14
+ sessionDeliveryMode: OptiViewAdsMode | null;
15
+ steeredSsaiActive: boolean;
16
+ steeredSsaiOptions: {
17
+ maxBitrate: number | undefined;
18
+ autoplay: boolean;
19
+ streamActivityMonitorId: string | undefined;
20
+ } | null;
21
+ ssaiController?: SsaiController;
22
+ pauseAdTickInterval: ReturnType<typeof setInterval> | null;
23
+ configSummary: ConfigSummary;
24
+ /**
25
+ * Origin of the polled URL, derived per startSession() and merged into the config summary
26
+ * (captured at construction, before any URL exists). Kept after endSession() for later reports.
27
+ */
28
+ manifestOrigin: string | null;
29
+ stitcherOrigin: string | null;
30
+ heldImmediatePreRollId: string | null;
31
+ snapbackSeekInFlight: boolean;
32
+ sessionConfig: SessionConfig | null;
33
+ sessionManifest: BreakManifest | null;
34
+ channelSwitchInFlight: boolean;
35
+ pendingChannelSwitch: BreakManifest | null;
36
+ clearSessionIdentity(): void;
37
+ }
@@ -0,0 +1 @@
1
+ export class SessionState{constructor(){this.sessionActive=!1,this.sessionGeneration=0,this.sessionDeliveryMode=null,this.steeredSsaiActive=!1,this.steeredSsaiOptions=null,this.pauseAdTickInterval=null,this.manifestOrigin=null,this.stitcherOrigin=null,this.heldImmediatePreRollId=null,this.snapbackSeekInFlight=!1,this.sessionConfig=null,this.sessionManifest=null,this.channelSwitchInFlight=!1,this.pendingChannelSwitch=null}clearSessionIdentity(){this.sessionDeliveryMode=null,this.sessionManifest=null,this.channelSwitchInFlight=!1,this.pendingChannelSwitch=null}}