@dolby-optiview/ads-sdk 1.1.0 → 1.2.0
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/ai/reference/error-codes.md +26 -3
- package/ai/reference/features-overview.md +9 -0
- package/ai/reference/knowledge-base.md +289 -16
- package/dist/NativeVideoAdapter.d.ts +4 -0
- package/dist/NativeVideoAdapter.js +1 -1
- package/dist/nativeMediaLoadError.d.ts +14 -0
- package/dist/nativeMediaLoadError.js +1 -0
- package/package.json +3 -3
|
@@ -25,15 +25,18 @@ of the public contract and are never renamed.
|
|
|
25
25
|
| `DA-ANVATO-CUE-OBSERVED` | break | info | ptsSource: 'anvatoCue' — an Anvato break-signaling (type=cue) frame was observed in the stream; context carries the cue's pts, the media time it landed at, whether the tracker recorded it, and the raw payload — so a manifest-PTS vs in-stream-PTS misalignment is observable in the field. |
|
|
26
26
|
| `DA-ANVATO-CUE-SEGMENT-ANCHORED` | break | warn | ptsSource: 'anvatoCue' — an Anvato emsg cue arrived without a ptsOffset from the adapter, so its break is scheduled at the cue's carrying segment start, which can be up to one segment early. Emitted once per session; context: pts, mediaTime. |
|
|
27
27
|
| `DA-ANVATO-CUE-OFFSET-REJECTED` | break | warn | ptsSource: 'anvatoCue' — the adapter's ptsOffset placed an Anvato cue outside its carrying segment's window (more than 0.1s before or 15s after the cue startTime), so the cue's startTime is used instead. Emitted once per cue pts; context: pts, startTime, ptsOffset, mappedTime. |
|
|
28
|
+
| `DA-ANVATO-CUE-END-MATCHED` | break | info | ptsSource: 'anvatoCue' — the active break's Anvato type=cue_end was observed and its length on the stream clock (cue_end pts minus the break start pts) is within 2s of the break duration. Emitted once per break; context: breakId, mediaTime (where the player reported the cue_end), lengthSec, durationSec. |
|
|
29
|
+
| `DA-ANVATO-CUE-END-REJECTED` | break | warn | ptsSource: 'anvatoCue' — an Anvato type=cue_end was observed for the active break but its stream-clock length differs from the break duration by more than 2s, so it is ignored and the resume keeps its computed target. Emitted once per break; context: breakId, mediaTime, lengthSec, durationSec. |
|
|
28
30
|
| `DA-PRELOAD-FAILED` | preload | warn | Ad preload failed; the asset will be loaded on demand at break start. |
|
|
29
31
|
| `DA-PRELOAD-MODE-RESOLVED` | preload | info | The configured ad preload mode was resolved for this session; context carries both the configured and the resolved mode. |
|
|
30
|
-
| `DA-PRELOAD-PATH` | preload | info | The preload path actually taken for a break's ad (parallel buffer / detached prefetch / none) and the creative container (stream vs progressive). |
|
|
32
|
+
| `DA-PRELOAD-PATH` | preload | info | The preload path actually taken for a break's ad (parallel buffer / detached prefetch / first-frame warm-up on the ad player / none) and the creative container (stream vs progressive). |
|
|
31
33
|
| `DA-BREAK-EARLY-RETURN` | break | info | The active break was removed from a polled manifest (early return); the break ends immediately and content resumes at break start plus the seconds already played. |
|
|
32
34
|
| `DA-BREAK-WINDOW-ESCAPED` | break | warn | An active wallclock break outlived its window on the stream clock (ad stall, bogus trigger position, or a re-fired completed break); the SDK ended it and returned to content. context.overrunSec says by how much. |
|
|
33
35
|
| `DA-BREAK-EVENT-UNSUPPORTED` | break | warn | A break is triggered by a spec event this SDK version does not schedule on this platform (web schedules `start`, `pause` and `end`; Android and iOS do not schedule `end` yet); the break was ignored. context.event names the event. |
|
|
34
36
|
| `DA-BREAK-SUPPRESSED-OVERLAP` | break | warn | A break was suppressed because an ad was already playing. |
|
|
35
37
|
| `DA-BREAK-TRANSITION` | break | info | Measured duration of a playback transition into an ad break, between two ads, between chained breaks, or back to content. |
|
|
36
38
|
| `DA-AD-SURFACE-REVEALED` | break | info | The ad surface became visible: how long it was held back waiting for the ad's first frame, and whether it was revealed because that frame arrived or because the wait timed out. |
|
|
39
|
+
| `DA-BREAK-CUT-LATE` | break | warn | timebase: 'wallclock', all platforms — content was cut more than 0.5s after the break's manifest start on the stream's Program Date Time, so the viewer saw the stream's own break (slate) instead of the ad until then; typically the ad's first frame was not ready at the cut and content kept playing under the cross-fade. The replaced window is unaffected: the resume anchor is the content time of the manifest start whatever the cut landed on. Context carries breakId, lateSec (cut minus start on the PDT axis), start (the manifest start), cutPdt (the PDT at the cut), cutAt (currentTime at the cut) and anchor; iOS/Android add waitedForAdMs when the cut waited for the ad surface. |
|
|
37
40
|
| `DA-PREROLL-CONTENT-HELD` | break | info | Content playback was held at session start until a pending pre-roll began, so no content is shown before the ad. |
|
|
38
41
|
| `DA-PREROLL-CONTENT-WAIT-TIMEOUT` | break | info | A pending pre-roll began after content readiness was not reported within the wait timeout. |
|
|
39
42
|
| `DA-PREROLL-CANCELLED-CONTENT-ERROR` | break | warn | A pending pre-roll was cancelled because the content failed to load before it could begin. |
|
|
@@ -45,14 +48,21 @@ of the public contract and are never renamed.
|
|
|
45
48
|
| `DA-CONTENT-ELEMENT-SWAPPED` | break | info | The content player replaced its <video> element; the SDK re-bound its presentation listeners to the new one. |
|
|
46
49
|
| `DA-PIP-FORMAT-OVERRIDE` | break | info | The content player is in picture-in-picture, so the break's declared format was overruled and it plays as a single fullscreen ad. |
|
|
47
50
|
| `DA-PIP-ADOPTION-STATE` | break | info | Native PiP reconciliation deferred or reused a layout/handoff decision. Context carries reason, active, adPlaying, applied, and asset/break IDs when available; this is not evidence that a format override occurred. |
|
|
51
|
+
| `DA-PIP-HANDOFF` | break | info | The picture-in-picture handoff route chosen for the ad: 'layer-swap' (the presented AVPlayerLayer is re-pointed at the ad player) or 'borrow' (the ad plays through the content player). context: route, layers. iOS only. |
|
|
52
|
+
| `DA-PIP-LAYER-SWAP` | break | debug | Read-back check after a PiP layer-swap: whether the presented layer still points at the ad player after the re-point. context: held, layers. iOS only. |
|
|
53
|
+
| `DA-PIP-PRESENTED` | break | debug | Status trace of the player on the PiP-presented layer during a layer-swap: owner (ad/content/other) and time-control-status transitions, plus ad-item ends observed while presented. context: owner/status, or errored/reachedEnd/adStatus. iOS only. |
|
|
48
54
|
| `DA-CHAIN-RESOLVER-ERROR` | chaining | warn | The chained-successor resolver threw; chaining was skipped for this break. |
|
|
49
55
|
| `DA-GAM-NO-ASSET-KEY` | gam | warn | Retired: the GAM stream identity now comes from the manifest's vendorConfiguration. Kept declared so the code is never reused; see DA-GAM-CONFIG-MISSING. |
|
|
50
56
|
| `DA-GAM-SESSION-FAILED` | gam | error | The GAM/IMA session failed to initialize; GAM breaks will not serve. |
|
|
57
|
+
| `DA-GAM-START-DEFERRED` | gam | info | The SGAI IMA stream request is deferred until the configured ad start delay has been played; context includes adStartDelaySec. |
|
|
51
58
|
| `DA-GAM-CONFIG-RESOLVED` | gam | info | The GAM stream identity for this session was resolved from the manifest's vendorConfiguration. |
|
|
52
59
|
| `DA-GAM-CONFIG-MISSING` | gam | error | The break manifest carries no usable "gam" vendorConfiguration entry; GAM assets are skipped. |
|
|
53
60
|
| `DA-GAM-POD-IDENTITY-MISMATCH` | gam | error | A GAM pod asset's vendorParameters name a different network code or custom asset key than the stream session was created with; the asset is skipped because the pod request cannot serve. |
|
|
54
61
|
| `DA-GAM-POD-MID-BREAK-END` | gam | warn | IMA reported the ad break ended before the pod's last ad while pod stream remains; per-ad reporting continues until the pod ends. |
|
|
62
|
+
| `DA-GAM-DUPLICATE-METADATA` | gam | info | The stream carries the same ad marker more than once at the same time (for example in both the audio and video renditions of an fMP4 stream); the duplicate was not passed to IMA, so each ad event fires once. Reported once per stream session. |
|
|
55
63
|
| `DA-VENDOR-CONFIG-UNKNOWN-TYPE` | manifest | info | One or more vendorConfiguration entries declare a type this SDK version does not recognise and were ignored. |
|
|
64
|
+
| `DA-EXTERNAL-VENDOR-INVALID` | manifest | warn | External vendorConfiguration entries were ignored because provider was blank or assetParameters was not an object. context: count. |
|
|
65
|
+
| `DA-EXTERNAL-VENDOR-DUPLICATE` | manifest | warn | External vendorConfiguration entries repeated a provider; the first valid entry was used. context: providers. |
|
|
56
66
|
| `DA-CONTENT-PLAYBACK-ERROR` | playback | error | The content player reported a playback error. |
|
|
57
67
|
| `DA-CONTENT-RESUME-STALLED` | playback | warn | Content did not resume within the watchdog window after a break that paused it ended; the SDK re-issued play() once. iOS only for now. |
|
|
58
68
|
| `DA-CONTENT-RECOVERY-MODE-RESOLVED` | playback | info | The resolved post-break content recovery mode for the session (recreate or none); context carries configured and resolved. |
|
|
@@ -66,7 +76,10 @@ of the public contract and are never renamed.
|
|
|
66
76
|
| `DA-AD-START-TIMEOUT` | playback | error | The ad did not load, render a first frame or start within its start budget; reported as CMCD adec. |
|
|
67
77
|
| `DA-AD-START-INTERRUPTED` | playback | warn | The ad element's play() was interrupted by a browser-initiated pause before the ad started (Chromium AbortError, seen on Samsung Tizen); the SDK re-issued play() once. context: breakId, error. |
|
|
68
78
|
| `DA-AD-DURATION-PREPARED` | playback | info | The ad media for a break was prepared and its real playable duration is now known, BEFORE the first frame. context: breakId, assetId, index, total, preparedDurationSec (the loaded media's own duration — on a GAM pod, the stitched pod's length), declaredDurationSec (what the manifest said about this asset, when it said anything) and breakDurationSec (the slot the break declares). Emitted per asset, so a multi-asset break reports one per asset and the break total is their sum. The three together separate a short break caused by a short POD from one caused by a short request or a late join. |
|
|
69
|
-
| `DA-DELIVERY-MODE-RESOLVED` | session | info | Delivery mode (sgai/ssai) resolved for the session at start and again on an ads-channel switch from the manifest's delivery rules (or the explicit client config). |
|
|
79
|
+
| `DA-DELIVERY-MODE-RESOLVED` | session | info | Delivery mode (sgai/ssai) resolved for the session at start and again on an ads-channel switch from the manifest's delivery rules (or the explicit client config), with the detected device profile. |
|
|
80
|
+
| `DA-TARGETING-VALUE-UNKNOWN` | manifest | warn | A targeting value is not recognized and the targeted entry does not match. |
|
|
81
|
+
| `DA-TARGETING-FIELD-UNKNOWN` | manifest | warn | A targeting field is not recognized and the targeted entry does not match. |
|
|
82
|
+
| `DA-TARGETING-INVALID` | manifest | warn | A targeting constraint is malformed or incomplete and the targeted entry does not match. |
|
|
70
83
|
| `DA-DELIVERY-MODE-UNKNOWN` | session | warn | The matching delivery rule carried an unrecognized mode value; sgai is used (forward compatibility). |
|
|
71
84
|
| `DA-DELIVERY-MODE-OVERRIDDEN` | session | info | The manifest's delivery rules resolved a different mode, but the explicit client config takes precedence. |
|
|
72
85
|
| `DA-DELIVERY-MODE-CHANGE-IGNORED` | session | warn | An ordinary manifest poll resolves a different delivery mode; the mode pinned for the session stays in force. An ads-channel switch resolves the mode again. |
|
|
@@ -103,8 +116,18 @@ of the public contract and are never renamed.
|
|
|
103
116
|
| `DA-LIVE-START-PUSHBACK-LANDED` | playback | info | Measured initial-live seek landing. Context includes the requested target and targetDurationSec, landed, errorSec, liveEdgeAtLanding, landedOffsetSec and landedOffsetTargetDurations. A timeout or failed seek reports landed=null, never an inferred success. |
|
|
104
117
|
| `DA-RESUME-SEEK` | playback | info | Content was resume-seeked to the resolved post-break position (insertion type policy). |
|
|
105
118
|
| `DA-RESUME-UNADDRESSABLE` | playback | warn | A break played through the content player (picture-in-picture), so content came back on a reloaded source whose timeline has its own origin, and that source published neither a program-date-time nor a seekable range to re-address the resume point against. Content stays where the reload left it — near the live edge — rather than being seeked to a position that does not exist on the new timeline. Context carries breakId, target (the position on the pre-borrow timeline) and offset. |
|
|
106
|
-
| `DA-RESUME-LANDED` | playback | info | Where the post-break resume-seek actually landed, measured on the seeked event rather than inferred: context carries breakId, target (the position requested), landed and errorSec (landed - target; negative means the player settled EARLIER than asked). landed is null with a reason when no seeked event arrived, which is normal for a seek to the position the playhead already holds. Report-only: the SDK does not correct the position. |
|
|
119
|
+
| `DA-RESUME-LANDED` | playback | info | Where the post-break resume-seek actually landed, measured on the seeked event rather than inferred: context carries breakId, target (the position requested), landed and errorSec (landed - target; negative means the player settled EARLIER than asked). errorSec only says whether the SEEK did what it was asked: when the live-window clamp moved the seek, policyTarget is the resume point before the clamp and policyErrorSec (landed - policyTarget) is how far the viewer really is from it. landed is null with a reason when no seeked event arrived, which is normal for a seek to the position the playhead already holds. Report-only: the SDK does not correct the position. |
|
|
120
|
+
| `DA-RESUME-PREPARED` | playback | info | Android, live break that resumes at its end — the paused content was parked exactly on the resume point before the hand-back, so the hand-back only plays (DA-RESUME-SEEK then reports prepared=true and issues no seek). Context carries breakId, target, source (cue_end when the stream's matched type=cue_end was known, else cue+duration) leadMs (how long before the expected hand-back it was parked) and refresh (true when content parked long before the hand-back was seeked onto the same point again, 4.5s before it). A later cue_end parks content again. |
|
|
107
121
|
| `DA-RESUME-PREPOSITIONED` | playback | info | The break resumed without a seek: content was moved onto its resume position DURING the break, so the transition needed no jump. Context carries breakId, target, prepositionedTo, landed, shortfallSec and exact. exact=true means it reached the policy target. exact=false means the live window never published those frames, so content was moved as close as exists — the viewer lands short, but arrives already in place rather than via a visible seek at the cut. |
|
|
122
|
+
| `DA-RESUME-PTS-MAP` | playback | info | ptsSource: 'anvatoCue', iOS/tvOS and web — the live resume point was mapped onto currentTime through the stream's own clock: the Anvato type=beacon tags delivered since the run-up's last seek give the currentTime ↔ stream-clock offset, and the resume point (the matched cue_end, else the cue plus the break duration) is converted with it. Reported when a mapping is first available after a seek and whenever the mapped target moves by 0.1s or more. Context carries breakId, target, landingStreamSec, landingSource (cue_end or cue+duration), offsetSec, beacons (beacons averaged), generation (how often the mapping started over since the session began), currentTime, and computedTarget with deltaSec (target - computedTarget) when a currentTime-based target exists. |
|
|
123
|
+
| `DA-RESUME-PTS-REMAP` | playback | warn | ptsSource: 'anvatoCue', iOS/tvOS and web — a stream beacon disagreed with the currentTime ↔ stream-clock mapping by more than 0.2s, so the player re-anchored currentTime (a variant switch or a buffer restart); the mapping starts over from that beacon and the resume point is mapped again. Reported during the resume run-up and after the hand-back. Context carries breakId, fromOffsetSec, toOffsetSec, deltaSec, currentTime and stage (run-up or after-hand-back). |
|
|
124
|
+
| `DA-RESUME-PTS-CORRECTED` | playback | info | ptsSource: 'anvatoCue', iOS/tvOS and web — once per break at the hand-back, on a stream that carries Anvato beacons: whether the stream clock placed the live resume point, and by how much it moved it. applied=true: streamClockTarget is the resume point mapped through the beacons, correctionSec is streamClockTarget - computedTarget (the currentTime target, cue plus duration), and currentTimeShiftSec is how far the same stream instant moved in currentTime between the pause and the hand-back (negative: it now sits earlier), null when no beacon mapped the stream before the pause. Context also carries breakId, landingSource (cue_end or cue+duration), landingStreamSec, offsetSec, beacons, remaps (DA-RESUME-PTS-REMAP during the break) and cueEndTarget when a cue_end was matched. applied=false with a reason means no beacon arrived after the run-up's last seek, so the cue_end or currentTime target applied. |
|
|
125
|
+
| `DA-RESUME-PTS-LANDED` | playback | info | ptsSource: 'anvatoCue', iOS/tvOS and web — where a stream-clock resume landed, measured on the stream's own clock from the beacons delivered after the hand-back: landedStreamSec is the position content resumed from (resumedAt, the seek landing or the playhead at a seek-free hand-back) converted with that mapping, and errorSec is landedStreamSec - landingStreamSec (negative: content resumed before the stream's resume point, so slate is shown). Context carries breakId, landingStreamSec, landingSource, resumedAt, sinceHandBackSec and, when measured, landedStreamSec, errorSec, offsetSec and beacons; reason when no beacon arrived within 15s. Report-only: the SDK does not correct the position from it. |
|
|
126
|
+
| `DA-RESUME-PDT-MAP` | playback | info | timebase: 'wallclock', all platforms — the live resume point (the manifest start plus duration, a wallclock instant) was mapped onto currentTime through the Program Date Time the content player reports now, so a currentTime re-anchored by a seek during the break cannot move it. Reported when the run-up first reads the target and whenever the mapped target moves by 0.1s or more (remapped=true: currentTime moved against the stream). Context carries breakId, target, landingPdt (the break end, ISO-8601), pdtOffsetSec (PDT minus currentTime, seconds), currentTime, remapped, and computedTarget with deltaSec (target - computedTarget) when an anchor-based target exists. |
|
|
127
|
+
| `DA-RESUME-PDT-CORRECTED` | playback | info | timebase: 'wallclock', all platforms — once per break at the hand-back of a break anchored on the Program Date Time: whether the stream clock placed the live resume point, and by how much it moved it. applied=true: pdtTarget is the break end mapped through the PDT the player reports at the hand-back, correctionSec is pdtTarget - computedTarget (the anchor plus the offset) and currentTimeShiftSec is how far the same stream instant moved in currentTime between the cut and the hand-back (negative: it now sits earlier). applied=false with a reason means no PDT sample was available at the hand-back, so the anchor-based target applied. Context also carries breakId and computedTarget. |
|
|
128
|
+
| `DA-RESUME-PDT-LANDED` | playback | info | timebase: 'wallclock', all platforms — where a Program-Date-Time resume landed, measured on the stream's own clock from the first position sample after the hand-back: errorSec is the PDT content resumed at minus the break end (negative: content resumed before the break end, so the stream's own break is shown; positive: post-break content was skipped). Context carries breakId, landingPdt (the break end), landedPdt, landedAt (currentTime) and sinceHandBackSec; reason when no sample carried a PDT within 2s. Report-only: the SDK does not correct the position from it. |
|
|
129
|
+
| `DA-RESUME-CUE-END` | playback | info | ptsSource: 'anvatoCue', iOS/tvOS, web and Android — the stream's matched type=cue_end replaced the computed resume target: the slate under the ad ends there, not at cue plus the advertised duration (150.206s for a 150s break on NFL Channel), and the player can remap its timeline while content is held, so the cue_end is reported in the current mapping. Context carries breakId, computedTarget, cueEndTarget, deltaSec (cueEndTarget - computedTarget), stage (hold, playing or hand-back; Android: always hand-back) and corrected (whether an in-buffer correction seek ran). Only a policy that resumes at the break end is replaced; otherwise applied=false and only the delta is reported. |
|
|
130
|
+
| `DA-RESUME-CUE-END-LATE` | playback | info | ptsSource: 'anvatoCue', iOS/tvOS and Android — the break's type=cue_end was matched only after the hand-back. When it is more than 1s ahead of the playhead and buffered, content is seeked forward to it once (corrected=true); content is never seeked backward. Context carries breakId, cueEndTarget, currentTime, deltaSec (cueEndTarget - currentTime) and corrected. |
|
|
108
131
|
| `DA-RESUME-CATCHUP` | playback | info | Content began playing behind the ad to reach its resume point. Context carries breakId, startPos, target, remainingAdSec, publishedEnd and clampedStart — clampedStart true means the stream had not published the ideal start position, so playback began late and the alignment loop must make up the difference (the zero-live-headroom case). Its ABSENCE on a live break that resumed short means catch-up never ran at all, which is a different fault from it running and falling short. |
|
|
109
132
|
| `DA-RESUME-TARGET-CLAMPED` | playback | warn | The computed resume position sat outside the held live window and was clamped to the nearest end of it; context carries breakId, requested, clamped, shortfallSec and bound — 'end' when the target is ahead of the published edge, 'start' when it is behind the live window start (already evicted) — applied only when the gap is at most half the held window, otherwise DA-RESUME-START-CLAMP-SKIPPED is emitted instead. Distinct from DA-RESUME-OFFSET-CLAMPED, which shortens the OFFSET to the ad seconds actually played. |
|
|
110
133
|
| `DA-RESUME-START-CLAMP-SKIPPED` | playback | info | The resume target sat behind the element's live seekable start by more than half the held window, which cannot be DVR eviction (the element's seekable range disagrees with the adapter timeline); the target was left unchanged and the engine resolved the seek itself. Context carries breakId, requested, seekableStart, behindSec, windowSec and maxBehindSec. |
|
|
@@ -146,6 +146,15 @@ are preserved; the delivery mode pinned at session start is kept. The applicatio
|
|
|
146
146
|
receives one `adchannelchange` event (`previousChannelId`, `channelId`, `reason`,
|
|
147
147
|
`manifestUrl`) after the re-alignment. Web SDK only for now.
|
|
148
148
|
|
|
149
|
+
## External provider parameters
|
|
150
|
+
|
|
151
|
+
Use `getExternalVendorConfiguration()` to read the current macro-resolved
|
|
152
|
+
parameters for every external provider, or pass an exact provider name such as
|
|
153
|
+
`mediakind` to read one. Read the values just before calling the external
|
|
154
|
+
system. `externalvendorconfigurationchange` fires when an adopted manifest
|
|
155
|
+
adds, removes, or changes a provider's parameter template, including the first
|
|
156
|
+
manifest. A macro-value change alone does not emit the event.
|
|
157
|
+
|
|
149
158
|
## Diagnostics
|
|
150
159
|
|
|
151
160
|
The SDK emits structured `DiagnosticEvent`s (via `onDiagnostic`) and can export
|
|
@@ -72,6 +72,16 @@ also restart with the new manifest's ids once the switch is applied.
|
|
|
72
72
|
**Fix:** Nothing when the backend re-pointed the stream on purpose. Repeated
|
|
73
73
|
flips between channels indicate an unstable manifest source.
|
|
74
74
|
|
|
75
|
+
## External provider parameters
|
|
76
|
+
|
|
77
|
+
Call `getExternalVendorConfiguration()` to retrieve all current provider
|
|
78
|
+
configurations, or pass a provider name such as `mediakind` for one
|
|
79
|
+
configuration. The returned `assetParameters` include current macro values.
|
|
80
|
+
Read them immediately before passing them to the external system.
|
|
81
|
+
`externalvendorconfigurationchange` is emitted when a newly adopted manifest
|
|
82
|
+
adds, removes, or changes a provider's template, including the first manifest;
|
|
83
|
+
macro-value changes alone do not emit it.
|
|
84
|
+
|
|
75
85
|
## DA-MANIFEST-SIGNATURE-INVALID
|
|
76
86
|
|
|
77
87
|
**Cause:** The break-manifest response failed detached-JWS signature
|
|
@@ -158,6 +168,18 @@ difference — expected for a viewer riding the live edge. The end-of-break code
|
|
|
158
168
|
then carries `alignCorrections` and `alignCorrectionsMs`: shrinking values mean
|
|
159
169
|
the loop converged; similar-magnitude ones mean it chased without settling.
|
|
160
170
|
|
|
171
|
+
## DA-RESUME-PREPARED
|
|
172
|
+
|
|
173
|
+
**Cause:** Informational (Android, a live break that resumes at its end). The
|
|
174
|
+
paused content was parked exactly on its resume point before the hand-back, so
|
|
175
|
+
the hand-back only plays (`context`: `breakId`, `target`, `source` — `cue_end`
|
|
176
|
+
or `cue+duration` — `leadMs` before the expected hand-back, and `refresh`: content
|
|
177
|
+
parked long before the hand-back is seeked onto the same point again 4.5 s before
|
|
178
|
+
it); the following
|
|
179
|
+
`DA-RESUME-SEEK` reports `prepared: true` and issues no seek. **Fix:** None. A
|
|
180
|
+
`DA-RESUME-SEEK` with `prepared: false` on such a break means the resume point
|
|
181
|
+
was not known or published early enough, so the hand-back paid for a seek.
|
|
182
|
+
|
|
161
183
|
## DA-RESUME-PREPOSITIONED
|
|
162
184
|
|
|
163
185
|
**Cause:** Informational. Content was moved onto its resume position _while the
|
|
@@ -179,17 +201,22 @@ has no headroom and cannot be returned to the exact frame they left.
|
|
|
179
201
|
|
|
180
202
|
**Cause:** Informational. Reports where the post-break resume-seek actually
|
|
181
203
|
landed, measured on the `seeked` event rather than inferred from a later
|
|
182
|
-
sample (`context` carries `breakId`, `target`, `landed`, `errorSec
|
|
183
|
-
`errorSec` is `landed - target
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
204
|
+
sample (`context` carries `breakId`, `target`, `landed`, `errorSec`, and
|
|
205
|
+
`policyTarget` / `policyErrorSec`). `errorSec` is `landed - target`: it only
|
|
206
|
+
says whether the **seek** did what it was asked, so a **negative** value means
|
|
207
|
+
the player settled closer to the live edge than the SDK asked for.
|
|
208
|
+
`policyTarget` is the resume point before the live-window clamp, and
|
|
209
|
+
`policyErrorSec` (`landed - policyTarget`) is how far the viewer really is from
|
|
210
|
+
it. **Fix:** None on its own — it is a measurement. A consistent negative
|
|
211
|
+
`errorSec` on live means the engine is overriding the seek with its own minimum
|
|
212
|
+
live offset. An `errorSec` near zero alongside `DA-RESUME-TARGET-CLAMPED` does
|
|
213
|
+
**not** mean the resume was on target: read `policyErrorSec`, which is the
|
|
214
|
+
seconds of the ad window (slate, on an SGAI feed) the viewer still sees —
|
|
215
|
+
typically a viewer riding the live edge with no headroom. Both values are in
|
|
216
|
+
the player's `currentTime`, so they cannot reveal a timeline remap. `landed:
|
|
217
|
+
null` with a `reason` is normal rather than a fault — a seek to the position
|
|
218
|
+
the playhead already holds fires no `seeked` event on several engines. The SDK
|
|
219
|
+
never corrects the landing position.
|
|
193
220
|
|
|
194
221
|
## DA-LIVE-START-PUSHBACK
|
|
195
222
|
|
|
@@ -498,7 +525,10 @@ early. Content then resumes the same amount early, showing the end of the slate.
|
|
|
498
525
|
Emitted once per session; `context` carries `pts` and the `mediaTime` used.
|
|
499
526
|
**Fix:** Use an adapter that supplies `ptsOffset` (THEOplayer web and Android,
|
|
500
527
|
Shaka). A custom adapter sets `ptsOffset` on emsg cues to
|
|
501
|
-
`Period@start - presentationTimeOffset / timescale` from the MPD
|
|
528
|
+
`Period@start - presentationTimeOffset / timescale` from the MPD, or, as the
|
|
529
|
+
THEOplayer Android adapter does, measures it from the stream's Anvato beacons
|
|
530
|
+
(`type=beacon&pts=…`): the median of `startTime - pts` over the beacons that
|
|
531
|
+
agree. On Android the first few seconds of a source have no beacon clock yet.
|
|
502
532
|
|
|
503
533
|
## DA-ANVATO-CUE-OFFSET-REJECTED
|
|
504
534
|
|
|
@@ -510,6 +540,154 @@ SDK ignored it and used the segment-start `startTime`. `context` carries `pts`,
|
|
|
510
540
|
player time for media time `t` must be `t + ptsOffset`. A multi-Period MPD or a
|
|
511
541
|
player that counts time from `availabilityStartTime` can produce a wrong offset.
|
|
512
542
|
|
|
543
|
+
## DA-ANVATO-CUE-END-MATCHED
|
|
544
|
+
|
|
545
|
+
**Cause:** Informational (`ptsSource: 'anvatoCue'`). The stream's own end of the
|
|
546
|
+
active break (Anvato `type=cue_end`) was observed, and its length on the stream
|
|
547
|
+
clock is within 2s of the break duration. `context` carries `breakId`,
|
|
548
|
+
`mediaTime` (where the player reported it), `lengthSec` and `durationSec`.
|
|
549
|
+
**Fix:** None needed. On iOS/tvOS and Android this marker is the live resume point.
|
|
550
|
+
|
|
551
|
+
## DA-ANVATO-CUE-END-REJECTED
|
|
552
|
+
|
|
553
|
+
**Cause:** Warning (`ptsSource: 'anvatoCue'`). A `type=cue_end` was observed for
|
|
554
|
+
the active break, but its stream-clock length differs from the break duration by
|
|
555
|
+
more than 2s, so it is ignored and the computed resume point is kept.
|
|
556
|
+
**Fix:** Compare `context.lengthSec` with `context.durationSec` and check that
|
|
557
|
+
the manifest duration matches what the broadcaster signals for that break.
|
|
558
|
+
|
|
559
|
+
## DA-RESUME-CUE-END
|
|
560
|
+
|
|
561
|
+
**Cause:** Informational (iOS/tvOS, web and Android, `ptsSource: 'anvatoCue'`).
|
|
562
|
+
The matched `cue_end` replaced the computed live resume target. The slate under
|
|
563
|
+
the ad ends there, not at the cue plus the advertised duration (150.206s for a
|
|
564
|
+
150s break on NFL Channel), and the player can move its timeline while content
|
|
565
|
+
is held behind the ad, so the `cue_end` is reported in the current mapping.
|
|
566
|
+
`context` carries `breakId`, `computedTarget`, `cueEndTarget`, `deltaSec`,
|
|
567
|
+
`stage` (`hold`, `playing` or `hand-back`; Android always `hand-back`), `applied`
|
|
568
|
+
and `corrected`. **Fix:** None needed. A `deltaSec` of a fraction of a second is
|
|
569
|
+
the splice segment at the break end; a large one means the marker prevented a
|
|
570
|
+
resume inside the slate. `applied: false` means the resume policy (early
|
|
571
|
+
return, `insertion`, or a `resumeOffset` different from the duration) keeps its
|
|
572
|
+
own target.
|
|
573
|
+
|
|
574
|
+
## DA-RESUME-CUE-END-LATE
|
|
575
|
+
|
|
576
|
+
**Cause:** Informational (iOS/tvOS and Android, `ptsSource: 'anvatoCue'`). The
|
|
577
|
+
`cue_end` arrived only after content was handed back. `corrected: true` means
|
|
578
|
+
content was more than 1s short and buffered, and was moved forward onto it;
|
|
579
|
+
content is never moved backward. `context` carries `breakId`, `cueEndTarget`,
|
|
580
|
+
`currentTime`, `deltaSec` and `corrected`. **Fix:** None for a single break.
|
|
581
|
+
Frequent late arrivals with `AVPlayer` suggest the metadata look-ahead is not
|
|
582
|
+
delivering; on Android, the Media3 adapter delivers markers at presentation only,
|
|
583
|
+
so it always arrives late there.
|
|
584
|
+
|
|
585
|
+
## DA-RESUME-PTS-MAP
|
|
586
|
+
|
|
587
|
+
**Cause:** Informational (iOS/tvOS and web, `ptsSource: 'anvatoCue'`). The live resume
|
|
588
|
+
point was placed through the stream's own clock: the Anvato `type=beacon` tags
|
|
589
|
+
delivered since the run-up's last seek give the currentTime offset, and the
|
|
590
|
+
resume point (`landingSource`: the matched `cue_end`, else `cue+duration`) is
|
|
591
|
+
converted with it. `context` carries `breakId`, `target`, `landingStreamSec`,
|
|
592
|
+
`landingSource`, `offsetSec`, `beacons`, `generation` (how often the mapping
|
|
593
|
+
started over), `currentTime`, and
|
|
594
|
+
`computedTarget` with `deltaSec`. **Fix:** None needed. A large `deltaSec` is
|
|
595
|
+
the player's timeline having moved during the break, which this mapping
|
|
596
|
+
corrects. A stream without beacons never reports it and keeps the currentTime
|
|
597
|
+
resume.
|
|
598
|
+
|
|
599
|
+
## DA-RESUME-PTS-REMAP
|
|
600
|
+
|
|
601
|
+
**Cause:** Warning (iOS/tvOS and web, `ptsSource: 'anvatoCue'`). A beacon showed the
|
|
602
|
+
player re-anchored currentTime against the stream clock by more than 0.2s,
|
|
603
|
+
typically at a variant switch or a buffer restart. The mapping starts over from
|
|
604
|
+
that beacon. `context` carries `breakId`, `fromOffsetSec`, `toOffsetSec`,
|
|
605
|
+
`deltaSec`, `currentTime` and `stage` (`run-up` or `after-hand-back`).
|
|
606
|
+
**Fix:** None during the run-up: the resume point is mapped again from the next
|
|
607
|
+
beacons. Frequent remaps after the hand-back point to the engine re-anchoring
|
|
608
|
+
content on its own.
|
|
609
|
+
|
|
610
|
+
## DA-RESUME-PTS-CORRECTED
|
|
611
|
+
|
|
612
|
+
**Cause:** Informational (iOS/tvOS and web, `ptsSource: 'anvatoCue'`). Once per break
|
|
613
|
+
at the hand-back, on a stream with Anvato beacons: whether the stream clock
|
|
614
|
+
placed the resume point (`applied`) and by how much. `correctionSec` is the
|
|
615
|
+
stream-clock target minus the currentTime target (`computedTarget`, cue plus
|
|
616
|
+
duration); `currentTimeShiftSec` is how far the same stream instant moved in
|
|
617
|
+
currentTime during the break (negative: it now sits earlier). `context` also
|
|
618
|
+
carries `breakId`, `streamClockTarget`, `landingSource`, `landingStreamSec`,
|
|
619
|
+
`offsetSec`, `beacons`, `remaps` and `cueEndTarget`. **Fix:** None needed. A
|
|
620
|
+
large `currentTimeShiftSec` is a player timeline re-anchor the stream clock
|
|
621
|
+
corrected. `applied: false` with a `reason` means no beacon arrived after the
|
|
622
|
+
run-up's last seek; frequent occurrences point at beacons not reaching the
|
|
623
|
+
player.
|
|
624
|
+
|
|
625
|
+
## DA-RESUME-PTS-LANDED
|
|
626
|
+
|
|
627
|
+
**Cause:** Informational (iOS/tvOS and web, `ptsSource: 'anvatoCue'`). Where a
|
|
628
|
+
stream-clock resume landed, on the stream's own clock, measured from the beacons
|
|
629
|
+
after the hand-back. `errorSec` is `landedStreamSec - landingStreamSec`:
|
|
630
|
+
negative means content resumed before the stream's resume point (that much
|
|
631
|
+
slate shown), positive means content was skipped. `context` carries `breakId`,
|
|
632
|
+
`landingStreamSec`, `landingSource`, `resumedAt`, `sinceHandBackSec` and, when
|
|
633
|
+
measured, `landedStreamSec`, `errorSec`, `offsetSec` and `beacons`; `reason`
|
|
634
|
+
when no beacon arrived within 15s. **Fix:** None for a single break. Unlike
|
|
635
|
+
`DA-RESUME-LANDED` it is not measured in currentTime, so a timeline remap
|
|
636
|
+
cannot hide in it.
|
|
637
|
+
|
|
638
|
+
## DA-RESUME-PDT-MAP
|
|
639
|
+
|
|
640
|
+
**Cause:** Informational (all platforms, `timebase: 'wallclock'`). The live resume
|
|
641
|
+
point — the manifest `start + duration` — was placed through the stream's
|
|
642
|
+
Program Date Time as the content player reports it now, so a currentTime
|
|
643
|
+
re-anchored by a seek during the break cannot move it. Reported when the run-up
|
|
644
|
+
first reads it and whenever it moves by 0.1s or more (`remapped: true`).
|
|
645
|
+
`context` carries `breakId`, `target`, `landingPdt`, `pdtOffsetSec`,
|
|
646
|
+
`currentTime`, `remapped`, `computedTarget` and `deltaSec`. **Fix:** None
|
|
647
|
+
needed. A large `deltaSec` is the player's timeline having moved during the
|
|
648
|
+
break, which this mapping corrects. A stream without Program Date Time never
|
|
649
|
+
reports it and keeps the pause-position resume.
|
|
650
|
+
|
|
651
|
+
## DA-RESUME-PDT-CORRECTED
|
|
652
|
+
|
|
653
|
+
**Cause:** Informational (all platforms, `timebase: 'wallclock'`). Once per break
|
|
654
|
+
at the hand-back of a break anchored on the Program Date Time: whether the
|
|
655
|
+
stream clock placed the resume (`applied`), `correctionSec` from the anchor
|
|
656
|
+
target (`computedTarget`) and `currentTimeShiftSec`, how far currentTime moved
|
|
657
|
+
against the stream during the break. `context` also carries `breakId`,
|
|
658
|
+
`pdtTarget` and, when not applied, `reason`. **Fix:** None needed. A large
|
|
659
|
+
`currentTimeShiftSec` is a player timeline re-anchor the stream clock corrected.
|
|
660
|
+
`applied: false` means no Program Date Time sample was available at the
|
|
661
|
+
hand-back; if frequent, check that the stream carries
|
|
662
|
+
`EXT-X-PROGRAM-DATE-TIME` and that the adapter exposes `programDateTime`.
|
|
663
|
+
|
|
664
|
+
## DA-RESUME-PDT-LANDED
|
|
665
|
+
|
|
666
|
+
**Cause:** Informational (all platforms, `timebase: 'wallclock'`). Where a
|
|
667
|
+
Program-Date-Time resume landed on the stream clock, from the first position
|
|
668
|
+
sample after the hand-back. `errorSec` is the landed Program Date Time minus
|
|
669
|
+
the break end: negative means content resumed before the break end (that much
|
|
670
|
+
of the stream's own break shown), positive means post-break content was
|
|
671
|
+
skipped. `context` carries `breakId`, `landingPdt`, `landedPdt`, `landedAt`,
|
|
672
|
+
`errorSec` and `sinceHandBackSec`; `reason` when no sample carried a Program
|
|
673
|
+
Date Time within 2s. **Fix:** None within 0.5s. A larger value next to a
|
|
674
|
+
`DA-RESUME-TARGET-CLAMPED` means the live window could not reach the resume
|
|
675
|
+
point.
|
|
676
|
+
|
|
677
|
+
## DA-BREAK-CUT-LATE
|
|
678
|
+
|
|
679
|
+
**Cause:** Warning (all platforms, `timebase: 'wallclock'`). Content was cut more
|
|
680
|
+
than 0.5s after the break's manifest start on the stream's Program Date Time
|
|
681
|
+
(`lateSec`), so the viewer saw the stream's own break instead of the ad until
|
|
682
|
+
then. The replaced window is not affected: the resume is anchored on the
|
|
683
|
+
manifest start whatever the cut landed on. `context` carries `breakId`,
|
|
684
|
+
`lateSec`, `start`, `cutPdt`, `cutAt`, `anchor` and, on iOS/tvOS and Android,
|
|
685
|
+
`waitedForAdMs` — how long the cut waited for the ad's first frame under the
|
|
686
|
+
cross-fade. **Fix:** Check that the break was prepared 8s ahead
|
|
687
|
+
(`DA-PRELOAD-PATH`; a shorter manifest publication lead leaves no time) and, on
|
|
688
|
+
iOS/tvOS, that the first-frame warm-up rendered a frame (`firstFrame: true`).
|
|
689
|
+
A value of several seconds points at the pod request or the creative.
|
|
690
|
+
|
|
513
691
|
## DA-PRELOAD-FAILED
|
|
514
692
|
|
|
515
693
|
**Cause:** Preloading the next ad failed; the asset will load on demand at break
|
|
@@ -531,7 +709,9 @@ from timings.
|
|
|
531
709
|
|
|
532
710
|
**Cause:** Informational, emitted per preloaded break. States the preload path
|
|
533
711
|
actually taken for the break's ad in `context.path`: `parallel` (buffered into
|
|
534
|
-
the attached ad player), `single-decoder` (cache warm + detached prefetch)
|
|
712
|
+
the attached ad player), `single-decoder` (cache warm + detached prefetch),
|
|
713
|
+
`first-frame` (iOS/tvOS: warmed up on the ad player itself to its first frame,
|
|
714
|
+
then paused on it; `firstFrame` says whether the frame rendered) or
|
|
535
715
|
`none` (no pre-break activity — cold-start baseline / opt-out).
|
|
536
716
|
`context.creative` classifies the creative container: `stream` (HLS/DASH,
|
|
537
717
|
MSE-eligible — real pre-buffering is possible) vs `progressive` (MP4/WebM,
|
|
@@ -912,6 +1092,45 @@ scenario, capture the diagnostic stream too: the bounded export may no longer re
|
|
|
912
1092
|
earlier override. Repeated starts of the same completed break point to content restoration
|
|
913
1093
|
being mistaken for a viewer rewind, not necessarily to a missing PiP entry.
|
|
914
1094
|
|
|
1095
|
+
## DA-PIP-HANDOFF
|
|
1096
|
+
|
|
1097
|
+
**Cause:** iOS only. Records which route the ad takes onto the picture-in-picture window.
|
|
1098
|
+
`context.route` is `layer-swap` — the default: the renderer finds the `AVPlayerLayer` the
|
|
1099
|
+
window presents under the content view and re-points its `.player` at the SDK's ad player,
|
|
1100
|
+
so the ad reaches the window with no source reload and content keeps its item — or
|
|
1101
|
+
`borrow`, the fallback for when no presented layer can be identified (no content view
|
|
1102
|
+
handed to the renderer, or an ambiguous layer tree), which plays the ad through the
|
|
1103
|
+
content player itself and restores its source afterwards. `context.layers` inventories
|
|
1104
|
+
the `AVPlayerLayer`s found and the player each carries. **Fix:** Informational. If you
|
|
1105
|
+
expected `layer-swap` but see `borrow`, check that the host actually attached its player
|
|
1106
|
+
surface to the view handed to the renderer before the break starts; a `borrow` on a
|
|
1107
|
+
stream the adapter cannot restore would already have been refused as
|
|
1108
|
+
`DA-SHARED-ELEMENT-UNSUPPORTED`. Non-fatal.
|
|
1109
|
+
|
|
1110
|
+
## DA-PIP-LAYER-SWAP
|
|
1111
|
+
|
|
1112
|
+
**Cause:** iOS only. Read-back logged a few hundred milliseconds after a PiP layer-swap:
|
|
1113
|
+
whether the presented layer still points at the ad player (`context.held`), plus a
|
|
1114
|
+
`layers` inventory. **Fix:** `held: false` means something reverted the re-point — the
|
|
1115
|
+
host player rebuilding its internal layer on a source change is the usual suspect, since
|
|
1116
|
+
the window was bound to the old layer object. The window then shows content while the ad
|
|
1117
|
+
plays inline; the `DA-PIP-PRESENTED` trace shows which player actually landed on the
|
|
1118
|
+
layer. Non-fatal, informational.
|
|
1119
|
+
|
|
1120
|
+
## DA-PIP-PRESENTED
|
|
1121
|
+
|
|
1122
|
+
**Cause:** iOS only. Debug trace of the exact signal AVKit's PiP chrome reacts to during
|
|
1123
|
+
a layer-swap: which player the presented layer points at (`owner`: `ad`, `content` or
|
|
1124
|
+
`other`) and its `timeControlStatus` transitions, plus ad-item ends observed while the
|
|
1125
|
+
layer was presented (`errored`, `reachedEnd`, `adStatus`). **Fix:** None — it is the
|
|
1126
|
+
timeline to read when a controls flash or a stalled window is reported. A `paused` or
|
|
1127
|
+
`waiting` status landing on the presented player is what re-raises the window chrome,
|
|
1128
|
+
which is why the swap waits for `readyToPlay` + `playing`, pauses content only after the
|
|
1129
|
+
flip, and defers the exit hand-back past the close morph. `reachedEnd: true` followed by
|
|
1130
|
+
`presented layer → content player` is the healthy last-asset path; `reachedEnd: false`
|
|
1131
|
+
is a forced break cut and intentionally keeps the ad on the layer until break end.
|
|
1132
|
+
Non-fatal.
|
|
1133
|
+
|
|
915
1134
|
## DA-IMAGE-LOAD-FAILED
|
|
916
1135
|
|
|
917
1136
|
**Cause:** An image the renderer needed — a `double`/`lshape_ad` companion, an
|
|
@@ -946,6 +1165,14 @@ Common causes: wrong `networkCode`/`customAssetKey`, IMA SDK not loaded, or no
|
|
|
946
1165
|
the IMA SDK is loaded, the `networkCode` and `customAssetKey` are correct, and your
|
|
947
1166
|
content adapter exposes `videoElement`. Inspect `context.message`.
|
|
948
1167
|
|
|
1168
|
+
## DA-GAM-START-DEFERRED
|
|
1169
|
+
|
|
1170
|
+
**Cause:** Informational. The SGAI IMA stream request was deferred because the
|
|
1171
|
+
manifest's positive `adStartDelay` has not yet been played. `context.adStartDelaySec`
|
|
1172
|
+
reports the configured delay. **Fix:** None needed. This confirms the ad-free start
|
|
1173
|
+
is active; content must advance for the delay to elapse. If IMA does not start after
|
|
1174
|
+
that, check `DA-GAM-SESSION-FAILED`.
|
|
1175
|
+
|
|
949
1176
|
## DA-GAM-CONFIG-RESOLVED
|
|
950
1177
|
|
|
951
1178
|
**Cause:** Informational. The GAM stream identity for this session was read from the
|
|
@@ -992,6 +1219,15 @@ stream ends; `adbreakend` fires once, at the real end of the pod.
|
|
|
992
1219
|
before the early end, and `context.remainingSec` the pod left. A recurring hit on one asset
|
|
993
1220
|
points at the pod's ad decisioning or IMA rather than the integration.
|
|
994
1221
|
|
|
1222
|
+
## DA-GAM-DUPLICATE-METADATA
|
|
1223
|
+
|
|
1224
|
+
**Cause:** Informational. The stream carried the same ad marker more than once at the
|
|
1225
|
+
same playback time (for example in both audio and video renditions of an fMP4 stream).
|
|
1226
|
+
The duplicate was not forwarded to IMA, so each ad lifecycle event fires once. The
|
|
1227
|
+
diagnostic is reported once per stream session; `context.startTime` identifies the cue
|
|
1228
|
+
time. **Fix:** No action is needed when duplicate markers are expected. Otherwise,
|
|
1229
|
+
remove duplicate markers from stream packaging.
|
|
1230
|
+
|
|
995
1231
|
## DA-VENDOR-CONFIG-UNKNOWN-TYPE
|
|
996
1232
|
|
|
997
1233
|
**Cause:** Informational. The manifest carried `vendorConfiguration` entries whose
|
|
@@ -1000,6 +1236,20 @@ forward-compatibility rule. `context.types` names them. **Fix:** None needed —
|
|
|
1000
1236
|
expected when a newer backend serves an older client. If the type is one you expect to
|
|
1001
1237
|
be supported, check the SDK version.
|
|
1002
1238
|
|
|
1239
|
+
## DA-EXTERNAL-VENDOR-INVALID
|
|
1240
|
+
|
|
1241
|
+
**Cause:** The manifest contained external `vendorConfiguration` entries with a
|
|
1242
|
+
blank provider or without an object-valued `assetParameters` map. Those entries were
|
|
1243
|
+
ignored; `context.count` gives the number rejected. **Fix:** Correct the provider and
|
|
1244
|
+
parameter map in the manifest. Non-string parameter values are discarded.
|
|
1245
|
+
|
|
1246
|
+
## DA-EXTERNAL-VENDOR-DUPLICATE
|
|
1247
|
+
|
|
1248
|
+
**Cause:** The manifest contained multiple valid external `vendorConfiguration`
|
|
1249
|
+
entries for the same provider. The first entry was kept; `context.providers` names
|
|
1250
|
+
the duplicates. Provider matching is case-sensitive. **Fix:** Combine the
|
|
1251
|
+
parameters into one entry per provider.
|
|
1252
|
+
|
|
1003
1253
|
## DA-SSAI-SESSION-FAILED
|
|
1004
1254
|
|
|
1005
1255
|
**Cause:** An SSAI (`mode: 'ssai'`) session failed to start. The SDK could not
|
|
@@ -1017,7 +1267,30 @@ on the first break-manifest fetch and is pinned for the rest of the session.
|
|
|
1017
1267
|
`context.mode` is the resolved mode, `context.source` says whether it came from the
|
|
1018
1268
|
manifest `delivery` rules (`manifest`) or an explicit constructor `mode` (`config`),
|
|
1019
1269
|
and `context.ruleIndex` names the matching rule (`null` when no rule matched and the
|
|
1020
|
-
sgai default applied).
|
|
1270
|
+
sgai default applied). `context.deviceProfile` records the detected device type,
|
|
1271
|
+
platform, OS and OS version when available. **Fix:** No action needed.
|
|
1272
|
+
|
|
1273
|
+
## DA-TARGETING-VALUE-UNKNOWN
|
|
1274
|
+
|
|
1275
|
+
**Cause:** A delivery rule, variant or asset URI targeting entry contains a
|
|
1276
|
+
`deviceType`, `platform` or `os` value this SDK does not recognize, so the entry
|
|
1277
|
+
does not match. `context.field` and `context.value` identify the value. **Fix:**
|
|
1278
|
+
Use a value from the manifest targeting schema, or update the SDK if the manifest
|
|
1279
|
+
intentionally uses a newer value.
|
|
1280
|
+
|
|
1281
|
+
## DA-TARGETING-FIELD-UNKNOWN
|
|
1282
|
+
|
|
1283
|
+
**Cause:** A targeting entry contains an unknown field, so it does not match.
|
|
1284
|
+
`context.field` identifies the field path, including nested `osVersion` keys.
|
|
1285
|
+
**Fix:** Correct a field-name typo, or update the SDK if the manifest uses a
|
|
1286
|
+
newer targeting field.
|
|
1287
|
+
|
|
1288
|
+
## DA-TARGETING-INVALID
|
|
1289
|
+
|
|
1290
|
+
**Cause:** An `osVersion` constraint is malformed or incomplete, such as missing
|
|
1291
|
+
`os`, having neither `min` nor `max`, or using a non-decimal bound. The targeted
|
|
1292
|
+
entry does not match. `context.field` and optional `context.value` identify the
|
|
1293
|
+
problem. **Fix:** Include `os` and at least one decimal `min` or `max` version.
|
|
1021
1294
|
|
|
1022
1295
|
## DA-DELIVERY-MODE-OVERRIDDEN
|
|
1023
1296
|
|
|
@@ -1211,8 +1484,8 @@ resolve — the name is not a built-in (`$OPTIVIEW_USER_AGENT$`,
|
|
|
1211
1484
|
`$OPTIVIEW_PLAYER_WIDTH$`, `$OPTIVIEW_PLAYER_HEIGHT$`,
|
|
1212
1485
|
`$OPTIVIEW_BREAK_DURATION_MS$`, `$OPTIVIEW_BREAK_DURATION_SECONDS$`; matching is
|
|
1213
1486
|
exact and case-sensitive), or the value is unavailable on this platform (iOS has
|
|
1214
|
-
no user agent; the break duration is unknown
|
|
1215
|
-
break declares one). The parameter is still sent with the unresolved token kept
|
|
1487
|
+
no user agent; the break duration is unknown on the GAM stream request, before
|
|
1488
|
+
any break, or when neither the asset nor the break declares one). The parameter is still sent with the unresolved token kept
|
|
1216
1489
|
literally, so the token is visible in the ad request (`context.key`,
|
|
1217
1490
|
`context.macro`). **Fix:** Correct the macro name in the manifest, or register the name in
|
|
1218
1491
|
`SessionConfig.assetParameterMacros` so your fixed value or callback supplies
|
|
@@ -21,6 +21,8 @@ export declare class NativeVideoAdapter implements PlayerAdapter {
|
|
|
21
21
|
private emittedCues;
|
|
22
22
|
/** Target duration supplied by the SDK for the live startup distance; null when unknown. */
|
|
23
23
|
private liveStartTargetDuration;
|
|
24
|
+
/** The `load()` / `recreateMediaPipeline()` in flight, which owns element errors until it settles. */
|
|
25
|
+
private activeOperation;
|
|
24
26
|
constructor(videoElement: HTMLVideoElement);
|
|
25
27
|
get currentTime(): number;
|
|
26
28
|
get duration(): number;
|
|
@@ -128,6 +130,8 @@ export declare class NativeVideoAdapter implements PlayerAdapter {
|
|
|
128
130
|
private teardownMetadataListener;
|
|
129
131
|
/** Map a native metadata cue (Safari ID3 `DataCue`) onto a {@link TimedMetadataCue}. */
|
|
130
132
|
private toCue;
|
|
133
|
+
private beginOperation;
|
|
134
|
+
private endOperation;
|
|
131
135
|
private setupVideoListeners;
|
|
132
136
|
private emit;
|
|
133
137
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export class NativeVideoAdapter{constructor(e){this.boundHandlers=new Map,this.eventListeners=new Map,this.cueTracks=[],this.emittedCues=new WeakSet,this.liveStartTargetDuration=null,this.video=e,this.setupVideoListeners(),this.setupMetadataListener()}get currentTime(){return this.video.currentTime}get duration(){return this.video.duration}get paused(){return this.video.paused}get muted(){return this.video.muted}set muted(e){this.video.muted=e}get volume(){return this.video.volume}set volume(e){this.video.volume=e}get videoElement(){return this.video}get playbackReady(){return this.video.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA&&!this.video.seeking}get programDateTime(){const e=this.webKitStartDate();if(!e)return null;if(this.video.readyState<HTMLMediaElement.HAVE_METADATA)return null;const t=e.call(this.video);return t instanceof Date&&t.getTime()>0?new Date(t.getTime()+1e3*this.video.currentTime):null}get liveEdge(){const e=this.liveStartTargetDuration,t=this.video.seekable;if(null===e||!this.webKitStartDate()||this.video.duration!==1/0||!t?.length)return null;const i=t.end(t.length-1);return Number.isFinite(i)?i+3*e:null}requestLiveStartOffset(e,t){const i=t;return this.liveStartTargetDuration=null!==e&&null!==i&&Number.isFinite(i)&&i>0?i:null,!1}webKitStartDate(){const e=this.video.getStartDate;return"function"==typeof e?e:void 0}get supportsParallelBuffering(){return!0}pause(){this.video.pause()}async play(){await this.video.play()}seek(e){this.video.currentTime=e}get sourceUrl(){return this.video.currentSrc||this.video.src||null}async load(
|
|
1
|
+
import{nativeMediaLoadError as e}from"./nativeMediaLoadError";export class NativeVideoAdapter{constructor(e){this.boundHandlers=new Map,this.eventListeners=new Map,this.cueTracks=[],this.emittedCues=new WeakSet,this.liveStartTargetDuration=null,this.activeOperation=null,this.video=e,this.setupVideoListeners(),this.setupMetadataListener()}get currentTime(){return this.video.currentTime}get duration(){return this.video.duration}get paused(){return this.video.paused}get muted(){return this.video.muted}set muted(e){this.video.muted=e}get volume(){return this.video.volume}set volume(e){this.video.volume=e}get videoElement(){return this.video}get playbackReady(){return this.video.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA&&!this.video.seeking}get programDateTime(){const e=this.webKitStartDate();if(!e)return null;if(this.video.readyState<HTMLMediaElement.HAVE_METADATA)return null;const t=e.call(this.video);return t instanceof Date&&t.getTime()>0?new Date(t.getTime()+1e3*this.video.currentTime):null}get liveEdge(){const e=this.liveStartTargetDuration,t=this.video.seekable;if(null===e||!this.webKitStartDate()||this.video.duration!==1/0||!t?.length)return null;const i=t.end(t.length-1);return Number.isFinite(i)?i+3*e:null}requestLiveStartOffset(e,t){const i=t;return this.liveStartTargetDuration=null!==e&&null!==i&&Number.isFinite(i)&&i>0?i:null,!1}webKitStartDate(){const e=this.video.getStartDate;return"function"==typeof e?e:void 0}get supportsParallelBuffering(){return!0}pause(){this.video.pause()}async play(){await this.video.play()}seek(e){this.video.currentTime=e}get sourceUrl(){return this.video.currentSrc||this.video.src||null}async load(t){const i=this.beginOperation();return new Promise((r,n)=>{const s=()=>{o(),this.endOperation(i),r()},a=()=>{o(),e("Native video load error",t,this.video.error).then(e=>{this.endOperation(i),n(e)})},o=()=>{this.video.removeEventListener("loadedmetadata",s),this.video.removeEventListener("error",a)};this.video.addEventListener("loadedmetadata",s),this.video.addEventListener("error",a),this.video.preload="auto",this.video.src=t,this.video.load()})}unload(){this.activeOperation=null,this.video.removeAttribute("src"),this.video.load()}recreateMediaPipeline(t){const i=this.sourceUrl;if(!i)return Promise.reject(new Error("Native pipeline recreate: no source URL"));const r=this.beginOperation();return new Promise((n,s)=>{const a=()=>{d(),this.endOperation(r),this.video.currentTime=t.startTime,n()},o=()=>{d(),e("Native pipeline recreate error",i,this.video.error).then(e=>{this.endOperation(r),s(e)})},d=()=>{this.video.removeEventListener("loadedmetadata",a),this.video.removeEventListener("error",o)};this.video.addEventListener("loadedmetadata",a),this.video.addEventListener("error",o),this.video.removeAttribute("src"),this.video.load(),this.video.src=i,this.video.load()})}async preload(e){if("undefined"!=typeof document)return new Promise(t=>{const i=document.createElement("video");i.preload="metadata",i.muted=!0;let r=!1;const n=()=>{r||(r=!0,clearTimeout(s),i.removeAttribute("src"),i.load(),t())},s=setTimeout(n,8e3);i.addEventListener("loadedmetadata",n,{once:!0}),i.addEventListener("error",n,{once:!0}),i.src=e})}on(e,t){this.eventListeners.has(e)||this.eventListeners.set(e,new Set),this.eventListeners.get(e).add(t)}off(e,t){this.eventListeners.get(e)?.delete(t)}destroy(){for(const[e,t]of this.boundHandlers)this.video.removeEventListener(e,t);this.boundHandlers.clear(),this.teardownMetadataListener(),this.eventListeners.clear()}setupMetadataListener(){const e=this.video.textTracks;if(!e||"function"!=typeof e.addEventListener)return;const t=e=>{this.emittedCues.has(e)||(this.emittedCues.add(e),this.emit("timedmetadata",this.toCue(e)))},i=e=>{const i=e.cues;if(i)for(let e=0;e<i.length;e++)t(i[e])},r=e=>{if("metadata"!==e.kind)return;e.mode="hidden";const r=()=>{const i=e.activeCues;if(i)for(let e=0;e<i.length;e++)t(i[e])};e.addEventListener("cuechange",r),this.cueTracks.push({track:e,handler:r}),i(e)},n=e=>{const t=e.track;t&&r(t)};this.addTrackHandler=n,e.addEventListener("addtrack",n);const s=()=>{for(const{track:e}of this.cueTracks)i(e)};this.cueScanHandler=s,this.video.addEventListener("timeupdate",s);for(let t=0;t<e.length;t++)r(e[t])}teardownMetadataListener(){const e=this.video.textTracks;this.addTrackHandler&&e?.removeEventListener&&(e.removeEventListener("addtrack",this.addTrackHandler),this.addTrackHandler=void 0),this.cueScanHandler&&(this.video.removeEventListener("timeupdate",this.cueScanHandler),this.cueScanHandler=void 0);for(const{track:e,handler:t}of this.cueTracks)e.removeEventListener("cuechange",t);this.cueTracks=[],this.emittedCues=new WeakSet}toCue(e){const t={type:"id3",startTime:e.startTime,endTime:e.endTime},i=e.value;if(i?.key){const e="string"==typeof(r=i.data)?r:null==r?"":"[object ArrayBuffer]"===Object.prototype.toString.call(r)?(new TextDecoder).decode(new Uint8Array(r)):ArrayBuffer.isView(r)?(new TextDecoder).decode(new Uint8Array(r.buffer,r.byteOffset,r.byteLength)):String(r);t.frames={[i.key]:e},"string"==typeof i.info&&i.info&&(t.description=i.info),e&&(t.messageData=e)}var r;return t}beginOperation(){const e=Symbol("native-operation");return this.activeOperation=e,e}endOperation(e){this.activeOperation===e&&(this.activeOperation=null)}setupVideoListeners(){const e=["timeupdate","ended","error","seeked","volumechange","waiting","playing","pause"];for(const t of e){const e=()=>{"error"!==t?this.emit(t,void 0):null===this.activeOperation&&this.emit(t,this.video.error)};this.video.addEventListener(t,e),this.boundHandlers.set(t,e)}}emit(e,t){const i=this.eventListeners.get(e);if(i)for(const e of i)try{e(t)}catch(e){console.error("[NativeVideoAdapter] Event handler error:",e)}}}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** A native element load failure with its `MediaError` `code`, which core classifies. */
|
|
2
|
+
export declare class NativeMediaLoadError extends Error {
|
|
3
|
+
readonly code: number | undefined;
|
|
4
|
+
constructor(prefix: string, code: number | undefined, detail?: string);
|
|
5
|
+
}
|
|
6
|
+
type ProbeResult = 'unreachable' | 'reachable' | 'unknown';
|
|
7
|
+
/**
|
|
8
|
+
* One bounded request to tell a missing or unreachable resource from unplayable media. The
|
|
9
|
+
* element reports both as `MEDIA_ERR_SRC_NOT_SUPPORTED` before it has any data.
|
|
10
|
+
*/
|
|
11
|
+
export declare function probeMediaUrl(url: string, timeoutMs?: number): Promise<ProbeResult>;
|
|
12
|
+
/** The load error for a failed element, refining an unsupported-source code with {@link probeMediaUrl}. */
|
|
13
|
+
export declare function nativeMediaLoadError(prefix: string, url: string, mediaError: MediaError | null): Promise<NativeMediaLoadError>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export class NativeMediaLoadError extends Error{constructor(e,r,o){super(`${e}${void 0!==r?` (code ${r})`:""}${o?`: ${o}`:""}`),this.code=r,this.name="NativeMediaLoadError"}}export async function probeMediaUrl(e,r=2e3){if("function"!=typeof fetch||!/^https?:/i.test(e))return"unknown";const o="function"==typeof AbortController?new AbortController:null;let t=null;const a=new Promise(e=>{t=setTimeout(()=>{o?.abort(),e("unknown")},r)}),n=fetch(e,{credentials:"omit",cache:"no-store",...o?{signal:o.signal}:{}}).then(e=>(e.body?.cancel().catch(()=>{}),e.status>=400?"unreachable":"reachable"),()=>o?.signal.aborted?"unknown":"unreachable");try{return await Promise.race([n,a])}finally{t&&clearTimeout(t)}}export async function nativeMediaLoadError(e,r,o){const t="number"==typeof o?.code?o.code:void 0,a=o?.message||void 0;if(4!==t)return new NativeMediaLoadError(e,t,a);return"unreachable"===await probeMediaUrl(r)?new NativeMediaLoadError(e,2,a??"resource not reachable"):new NativeMediaLoadError(e,t,a)}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dolby-optiview/ads-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "OptiView Ads SDK entry package - OptiViewAds with a default HLS.js ad player (native fallback)",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
},
|
|
45
45
|
"sideEffects": false,
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@dolby-optiview/ads-sdk-adapter-hlsjs": "^1.
|
|
48
|
-
"@dolby-optiview/ads-sdk-core": "^1.
|
|
47
|
+
"@dolby-optiview/ads-sdk-adapter-hlsjs": "^1.2.0",
|
|
48
|
+
"@dolby-optiview/ads-sdk-core": "^1.2.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"hls.js": "^1.0.0"
|