@dolby-optiview/ads-sdk 1.0.0-beta.6 → 1.0.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.
|
@@ -52,6 +52,9 @@ of the public contract and are never renamed.
|
|
|
52
52
|
| `DA-VENDOR-CONFIG-UNKNOWN-TYPE` | manifest | info | One or more vendorConfiguration entries declare a type this SDK version does not recognise and were ignored. |
|
|
53
53
|
| `DA-CONTENT-PLAYBACK-ERROR` | playback | error | The content player reported a playback error. |
|
|
54
54
|
| `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. |
|
|
55
|
+
| `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. |
|
|
56
|
+
| `DA-CONTENT-PIPELINE-RECREATED` | playback | info | The content player's media pipeline was rebuilt at break end before content resumed (recovery mode recreate); context carries breakId, startTime and durationMs. |
|
|
57
|
+
| `DA-CONTENT-PIPELINE-RECREATE-FAILED` | playback | warn | Rebuilding the content media pipeline at break end failed or timed out — or the adapter does not implement recreateMediaPipeline() (reason 'unsupported-adapter', e.g. a wrapper adapter that does not forward it); the SDK fell back to resuming in place with seek + play; context carries breakId, startTime, reason. |
|
|
55
58
|
| `DA-AD-PLAYBACK-ERROR` | playback | error | The ad player reported a playback error during a break. |
|
|
56
59
|
| `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. |
|
|
57
60
|
| `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). |
|
|
@@ -439,6 +439,34 @@ pre-buffers into a second MediaSource on such TVs: a second attached
|
|
|
439
439
|
MediaSource wedges their pipeline (PLAYG-319), and MSE makes a detached
|
|
440
440
|
pre-fill impossible (see `docs/ctv-preload-findings.md`).
|
|
441
441
|
|
|
442
|
+
## DA-CONTENT-RECOVERY-MODE-RESOLVED
|
|
443
|
+
|
|
444
|
+
**Cause:** Informational, emitted once at construction. States the post-break
|
|
445
|
+
content recovery mode in force for the session: `context.configured` is what
|
|
446
|
+
the integrator set (or the default) and `context.resolved` is what
|
|
447
|
+
`resolveContentRecovery` decided (`auto` resolving to `recreate` on VIZIO
|
|
448
|
+
SmartCast User-Agents, `none` elsewhere). **Fix:** None — use it to confirm
|
|
449
|
+
which recovery strategy a session/report actually ran with.
|
|
450
|
+
|
|
451
|
+
## DA-CONTENT-PIPELINE-RECREATED
|
|
452
|
+
|
|
453
|
+
**Cause:** Informational, emitted when `adBreakContentRecovery` resolved to
|
|
454
|
+
`recreate` and the content player's media pipeline was rebuilt at break end
|
|
455
|
+
before content resumed. `context` carries `breakId`, `startTime` (the resume
|
|
456
|
+
position the pipeline was seeded at) and `durationMs` (the rebuild cost).
|
|
457
|
+
**Fix:** None — use `durationMs` to measure what the rebuild costs on a panel;
|
|
458
|
+
expect roughly a second of re-buffering instead of an instant resume.
|
|
459
|
+
|
|
460
|
+
## DA-CONTENT-PIPELINE-RECREATE-FAILED
|
|
461
|
+
|
|
462
|
+
**Cause:** In `recreate` recovery mode, the adapter's `recreateMediaPipeline`
|
|
463
|
+
rejected or exceeded its 10s budget; the SDK fell back to resuming in place
|
|
464
|
+
with seek + play. `context` carries `breakId`, `startTime` and `reason`.
|
|
465
|
+
**Fix:** Read `reason` — adapter-side engine errors vs a timeout. Frequent
|
|
466
|
+
failures on a platform mean the rebuild path is not viable there; consider
|
|
467
|
+
`adBreakContentRecovery: 'none'` for it, or fix the adapter's rebuild. The
|
|
468
|
+
session still resumes — the diagnostic marks the fallback, not a stall.
|
|
469
|
+
|
|
442
470
|
## DA-BREAK-WINDOW-ESCAPED
|
|
443
471
|
|
|
444
472
|
**Cause:** An active wallclock break was still rendering after its window
|
|
@@ -63,6 +63,19 @@ export declare class NativeVideoAdapter implements PlayerAdapter {
|
|
|
63
63
|
*/
|
|
64
64
|
get sourceUrl(): string | null;
|
|
65
65
|
load(url: string): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Release the element's media at break end — remove `src` and force the load
|
|
68
|
+
* algorithm to run so the resource is fully released.
|
|
69
|
+
*/
|
|
70
|
+
unload(): void;
|
|
71
|
+
/**
|
|
72
|
+
* Rebuild the pipeline for the CURRENT source positioned at `startTime` — see
|
|
73
|
+
* `PlayerAdapter.recreateMediaPipeline`. Resolves on `loadedmetadata`, rejects on
|
|
74
|
+
* the element's `error`; the resume position is applied once metadata is known.
|
|
75
|
+
*/
|
|
76
|
+
recreateMediaPipeline(options: {
|
|
77
|
+
startTime: number;
|
|
78
|
+
}): Promise<void>;
|
|
66
79
|
/**
|
|
67
80
|
* Best-effort detached prefetch: warm the HTTP cache using a throwaway,
|
|
68
81
|
* un-attached video element set to preload metadata. Never rejects.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export class NativeVideoAdapter{constructor(e){this.boundHandlers=new Map,this.eventListeners=new Map,this.cueTracks=[],this.emittedCues=new WeakSet,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 programDateTime(){const e=this.video.getStartDate;if("function"!=typeof 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 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(e){return new Promise((t,r)=>{const i=()=>{n(),t()},s=()=>{n(),r(new Error("Native video load error"))},n=()=>{this.video.removeEventListener("loadedmetadata",i),this.video.removeEventListener("error",s)};this.video.addEventListener("loadedmetadata",i),this.video.addEventListener("error",s),this.video.preload="auto",this.video.src=e,this.video.load()})}async preload(e){if("undefined"!=typeof document)return new Promise(t=>{const r=document.createElement("video");r.preload="metadata",r.muted=!0;let i=!1;const s=()=>{i||(i=!0,clearTimeout(n),r.removeAttribute("src"),r.load(),t())},n=setTimeout(s,8e3);r.addEventListener("loadedmetadata",s,{once:!0}),r.addEventListener("error",s,{once:!0}),r.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)))},r=e=>{const r=e.cues;if(r)for(let e=0;e<r.length;e++)t(r[e])},i=e=>{if("metadata"!==e.kind)return;e.mode="hidden";const i=()=>{const r=e.activeCues;if(r)for(let e=0;e<r.length;e++)t(r[e])};e.addEventListener("cuechange",i),this.cueTracks.push({track:e,handler:i}),r(e)},s=e=>{const t=e.track;t&&i(t)};this.addTrackHandler=s,e.addEventListener("addtrack",s);const n=()=>{for(const{track:e}of this.cueTracks)r(e)};this.cueScanHandler=n,this.video.addEventListener("timeupdate",n);for(let t=0;t<e.length;t++)i(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},r=e.value;if(r?.key){const e="string"==typeof(i=r.data)?i:null==i?"":"[object ArrayBuffer]"===Object.prototype.toString.call(i)?(new TextDecoder).decode(new Uint8Array(i)):ArrayBuffer.isView(i)?(new TextDecoder).decode(new Uint8Array(i.buffer,i.byteOffset,i.byteLength)):String(i);t.frames={[r.key]:e},"string"==typeof r.info&&r.info&&(t.description=r.info),e&&(t.messageData=e)}var i;return t}setupVideoListeners(){const e=["timeupdate","ended","error","seeked","volumechange","waiting","playing","pause"];for(const t of e){const e=()=>{this.emit(t,"error"===t?this.video.error:void 0)};this.video.addEventListener(t,e),this.boundHandlers.set(t,e)}}emit(e,t){const r=this.eventListeners.get(e);if(r)for(const e of r)try{e(t)}catch(e){console.error("[NativeVideoAdapter] Event handler error:",e)}}}
|
|
1
|
+
export class NativeVideoAdapter{constructor(e){this.boundHandlers=new Map,this.eventListeners=new Map,this.cueTracks=[],this.emittedCues=new WeakSet,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 programDateTime(){const e=this.video.getStartDate;if("function"!=typeof 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 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(e){return new Promise((t,r)=>{const i=()=>{n(),t()},s=()=>{n(),r(new Error("Native video load error"))},n=()=>{this.video.removeEventListener("loadedmetadata",i),this.video.removeEventListener("error",s)};this.video.addEventListener("loadedmetadata",i),this.video.addEventListener("error",s),this.video.preload="auto",this.video.src=e,this.video.load()})}unload(){this.video.removeAttribute("src"),this.video.load()}recreateMediaPipeline(e){const t=this.sourceUrl;return t?new Promise((r,i)=>{const s=()=>{o(),this.video.currentTime=e.startTime,r()},n=()=>{o(),i(new Error("Native pipeline recreate error"))},o=()=>{this.video.removeEventListener("loadedmetadata",s),this.video.removeEventListener("error",n)};this.video.addEventListener("loadedmetadata",s),this.video.addEventListener("error",n),this.video.removeAttribute("src"),this.video.load(),this.video.src=t,this.video.load()}):Promise.reject(new Error("Native pipeline recreate: no source URL"))}async preload(e){if("undefined"!=typeof document)return new Promise(t=>{const r=document.createElement("video");r.preload="metadata",r.muted=!0;let i=!1;const s=()=>{i||(i=!0,clearTimeout(n),r.removeAttribute("src"),r.load(),t())},n=setTimeout(s,8e3);r.addEventListener("loadedmetadata",s,{once:!0}),r.addEventListener("error",s,{once:!0}),r.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)))},r=e=>{const r=e.cues;if(r)for(let e=0;e<r.length;e++)t(r[e])},i=e=>{if("metadata"!==e.kind)return;e.mode="hidden";const i=()=>{const r=e.activeCues;if(r)for(let e=0;e<r.length;e++)t(r[e])};e.addEventListener("cuechange",i),this.cueTracks.push({track:e,handler:i}),r(e)},s=e=>{const t=e.track;t&&i(t)};this.addTrackHandler=s,e.addEventListener("addtrack",s);const n=()=>{for(const{track:e}of this.cueTracks)r(e)};this.cueScanHandler=n,this.video.addEventListener("timeupdate",n);for(let t=0;t<e.length;t++)i(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},r=e.value;if(r?.key){const e="string"==typeof(i=r.data)?i:null==i?"":"[object ArrayBuffer]"===Object.prototype.toString.call(i)?(new TextDecoder).decode(new Uint8Array(i)):ArrayBuffer.isView(i)?(new TextDecoder).decode(new Uint8Array(i.buffer,i.byteOffset,i.byteLength)):String(i);t.frames={[r.key]:e},"string"==typeof r.info&&r.info&&(t.description=r.info),e&&(t.messageData=e)}var i;return t}setupVideoListeners(){const e=["timeupdate","ended","error","seeked","volumechange","waiting","playing","pause"];for(const t of e){const e=()=>{this.emit(t,"error"===t?this.video.error:void 0)};this.video.addEventListener(t,e),this.boundHandlers.set(t,e)}}emit(e,t){const r=this.eventListeners.get(e);if(r)for(const e of r)try{e(t)}catch(e){console.error("[NativeVideoAdapter] Event handler error:",e)}}}
|
package/dist/RoutingAdAdapter.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"hls.js";import{HlsJsAdapter as t}from"@dolby-optiview/ads-sdk-adapter-hlsjs";import{NativeVideoAdapter as
|
|
1
|
+
import e from"hls.js";import{HlsJsAdapter as t}from"@dolby-optiview/ads-sdk-adapter-hlsjs";import{NativeVideoAdapter as i}from"./NativeVideoAdapter";import{selectAdTech as n}from"./selectAdTech";export class RoutingAdAdapter{constructor(t,i=e.isSupported()){this.inner=null,this.currentTech=null,this.ownedSrc=null,this.hls=null,this.eventListeners=new Map,this.video=t,this.hlsSupported=i}get currentTime(){return this.inner?this.inner.currentTime:this.video.currentTime}get duration(){return this.inner?this.inner.duration:this.video.duration}get paused(){return this.inner?this.inner.paused: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 programDateTime(){return this.inner?this.inner.programDateTime:null}get videoElement(){return this.video}get supportsParallelBuffering(){return this.inner?.supportsParallelBuffering??this.hlsSupported}pause(){this.inner?this.inner.pause():this.video.pause()}async play(){if(this.inner)return this.inner.play();await this.video.play()}seek(e){this.inner?this.inner.seek(e):this.video.currentTime=e}load(e){const t=n(e),i=t=>{const i=this.ensureInner(t).load(e);return this.ownedSrc=this.video.getAttribute("src"),i};return"string"==typeof t?i(t):t.then(i)}preload(e){const t=n(e),i=t=>{const i=this.ensureInner(t);return i.preload?i.preload(e):Promise.resolve()};return"string"==typeof t?i(t):t.then(i)}on(e,t){let i=this.eventListeners.get(e);i||(i=new Set,this.eventListeners.set(e,i)),i.add(t),this.inner?.on(e,t)}off(e,t){this.eventListeners.get(e)?.delete(t),this.inner?.off(e,t)}unload(){this.teardownInner()}destroy(){this.teardownInner(),this.eventListeners.clear()}ensureInner(e){const t=this.hlsSupported?e:"native";if(this.inner&&this.currentTech===t)return this.inner;this.teardownInner(),this.inner=this.buildInner(t),this.currentTech=t;for(const[e,t]of this.eventListeners)for(const i of t)this.inner.on(e,i);return this.inner}buildInner(n){return"native"===n?new i(this.video):(this.hls=new e,new t(this.hls,this.video))}teardownInner(){if(this.inner?.destroy(),this.hls){try{this.hls.destroy()}catch{}this.hls=null}const e=this.video.getAttribute("src");e&&e!==this.ownedSrc||(this.video.removeAttribute("src"),"native"!==this.currentTech||null===this.ownedSrc||this.video.srcObject||this.video.load()),this.ownedSrc=null,this.inner=null,this.currentTech=null}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dolby-optiview/ads-sdk",
|
|
3
|
-
"version": "1.0.0
|
|
3
|
+
"version": "1.0.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.0.0
|
|
48
|
-
"@dolby-optiview/ads-sdk-core": "1.0.0
|
|
47
|
+
"@dolby-optiview/ads-sdk-adapter-hlsjs": "^1.0.0",
|
|
48
|
+
"@dolby-optiview/ads-sdk-core": "^1.0.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"hls.js": "^1.0.0"
|