@dolby-optiview/ads-sdk-adapter-hlsjs 1.0.0-beta.5 → 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.
- package/dist/HlsJsAdapter.d.ts +21 -1
- package/dist/HlsJsAdapter.js +1 -1
- package/package.json +2 -2
package/dist/HlsJsAdapter.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import Hls from 'hls.js';
|
|
2
2
|
import type { PlayerAdapter, PlayerAdapterEvent, PlayerAdapterEventHandler, VideoQuality } from '@dolby-optiview/ads-sdk-core';
|
|
3
3
|
/**
|
|
4
|
-
* HLS.js
|
|
4
|
+
* The {@link PlayerAdapter} for HLS.js, from the `@dolby-optiview/ads-sdk-adapter-hlsjs` package
|
|
5
|
+
* (peer dependency: `hls.js` 1.x).
|
|
5
6
|
*
|
|
7
|
+
* @category Player Integration
|
|
6
8
|
* @example
|
|
7
9
|
* ```typescript
|
|
8
10
|
* import Hls from 'hls.js';
|
|
@@ -60,6 +62,10 @@ export declare class HlsJsAdapter implements PlayerAdapter {
|
|
|
60
62
|
private presentedPts;
|
|
61
63
|
/** The metadata track listener, kept for teardown. */
|
|
62
64
|
private id3TrackListener;
|
|
65
|
+
/**
|
|
66
|
+
* @param hls The `Hls` instance that plays the content.
|
|
67
|
+
* @param videoElement The `<video>` element `hls` is attached to.
|
|
68
|
+
*/
|
|
63
69
|
constructor(hls: Hls, videoElement: HTMLVideoElement);
|
|
64
70
|
/**
|
|
65
71
|
* Current playback time in seconds.
|
|
@@ -143,6 +149,20 @@ export declare class HlsJsAdapter implements PlayerAdapter {
|
|
|
143
149
|
*/
|
|
144
150
|
get sourceUrl(): string | null;
|
|
145
151
|
load(url: string): Promise<void>;
|
|
152
|
+
/**
|
|
153
|
+
* Release the engine's media element and network loading — ad-role teardown at break end.
|
|
154
|
+
* The next `load()` re-attaches media, so nothing else is needed to reuse this adapter.
|
|
155
|
+
*/
|
|
156
|
+
unload(): void;
|
|
157
|
+
/**
|
|
158
|
+
* Rebuild the pipeline for the CURRENT source positioned at `startTime` — see
|
|
159
|
+
* `PlayerAdapter.recreateMediaPipeline`. Resolves on the first of `loadedmetadata` /
|
|
160
|
+
* `FRAG_BUFFERED`; rejects on a fatal hls.js error. `recoverMediaError()` is deliberately
|
|
161
|
+
* not used: it seeds `startLoad` with the old currentTime, which double-seeks.
|
|
162
|
+
*/
|
|
163
|
+
recreateMediaPipeline(options: {
|
|
164
|
+
startTime: number;
|
|
165
|
+
}): Promise<void>;
|
|
146
166
|
/**
|
|
147
167
|
* Detached prefetch for the single-decoder preload strategy.
|
|
148
168
|
*
|
package/dist/HlsJsAdapter.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"hls.js";import{decodeUtf8 as t,parseId3GeobFrames as s}from"@dolby-optiview/ads-sdk-core/id3";export class HlsJsAdapter{constructor(e,t){this.eventListeners=new Map,this.boundHandlers=new Map,this._programDateTime=null,this.deferredSeekTime=null,this.levelBeforePin=null,this.parsedCues=new Map,this.presentedPts=new Set,this.id3TrackListener=null,this.hls=e,this.video=t,this.setupHlsListeners(),this.setupVideoListeners()}get currentTime(){return this.video.currentTime}get duration(){return this.video.duration}get isLive(){const e=this.hls?.latestLevelDetails;return"boolean"==typeof e?.live?e.live:null}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 playbackRate(){return this.video.playbackRate}set playbackRate(e){this.video.playbackRate=e}get videoElement(){return this.video}get supportsParallelBuffering(){return e.isSupported()}get programDateTime(){return this._programDateTime}pause(){this.video.pause()}async play(){await this.video.play()}seek(e){this.video.seeking?this.deferredSeekTime=e:this.video.currentTime=e}get sourceUrl(){return this.hls.url??null}async load(t){return new Promise((s,i)=>{const r=()=>{this.hls.off(e.Events.MANIFEST_PARSED,r),this.hls.off(e.Events.ERROR,n),s()},n=(t,s)=>{s.fatal&&(this.hls.off(e.Events.MANIFEST_PARSED,r),this.hls.off(e.Events.ERROR,n),i(new Error(`HLS load error: ${s.details}`)))};this.hls.on(e.Events.MANIFEST_PARSED,r),this.hls.on(e.Events.ERROR,n),this.hls.loadSource(t),this.hls.attachMedia(this.video)})}async preload(t){if(e.isSupported())return new Promise(s=>{const i=new e({startFragPrefetch:!0,autoStartLoad:!0});let r=!1;const n=()=>{if(!r){r=!0,clearTimeout(d),i.off(e.Events.FRAG_LOADED,a),i.off(e.Events.ERROR,o);try{i.destroy()}catch{}s()}},a=()=>n(),o=(e,t)=>{t.fatal&&n()},d=setTimeout(n,8e3);i.on(e.Events.FRAG_LOADED,a),i.on(e.Events.ERROR,o),i.loadSource(t)})}get maxLiveSeekPosition(){const e=this.hls.latestLevelDetails;if(!e?.live)return null;const t=this.hls.liveSyncPosition;return"number"==typeof t&&Number.isFinite(t)?t:null}setVideoQuality(e){if(null===e){if(null===this.levelBeforePin)return!1;const e=this.levelBeforePin;try{return this.hls.nextLevel=e,this.hls.nextLevel!==e?!1:(this.levelBeforePin=null,!0)}catch{return!1}}const t=this.hls.levels;if(!t||t.length<2)return!1;let s=0;for(let e=1;e<t.length;e++)t[e].bitrate<t[s].bitrate&&(s=e);const i=t[s].bitrate;if("number"!=typeof i||!Number.isFinite(i)||i<=0)return!1;const r=null===this.levelBeforePin;let n;r&&(this.levelBeforePin=this.hls.manualLevel);try{this.hls.nextLevel=s,n=this.hls.nextLevel===s}catch{n=!1}return!!n||(r&&(this.levelBeforePin=null),!1)}on(e,t){this.eventListeners.has(e)||this.eventListeners.set(e,new Set),this.eventListeners.get(e).add(t)}off(e,t){const s=this.eventListeners.get(e);s&&s.delete(t)}destroy(){for(const[e,t]of this.boundHandlers)this.video.removeEventListener(e,t);this.boundHandlers.clear(),this.eventListeners.clear(),this.deferredSeekTime=null,this.id3TrackListener&&(this.id3TrackListener.track.removeEventListener("cuechange",this.id3TrackListener.handler),this.id3TrackListener=null),this.parsedCues.clear(),this.presentedPts.clear()}setupHlsListeners(){this.hls.on(e.Events.FRAG_CHANGED,(e,t)=>{t.frag.programDateTime&&this.anchorProgramDateTime(t.frag.programDateTime,t.frag.start)}),this.hls.on(e.Events.LEVEL_LOADED,(e,t)=>{if(t.details.hasProgramDateTime&&t.details.fragments.length>0){const e=t.details.fragments[0];e.programDateTime&&this.anchorProgramDateTime(e.programDateTime,e.start)}}),this.hls.on(e.Events.FRAG_PARSING_METADATA,(e,i)=>{for(const e of i.samples??[]){const i=s(e.data);if(0===i.length){const t={type:"id3",startTime:e.pts,raw:e.data,phase:"parsed"};this.emit("timedmetadata",t);continue}const r=[];for(const s of i){const i={type:"id3",startTime:e.pts,description:s.description,mimeType:s.mimeType,messageData:t(s.data),raw:e.data,phase:"parsed"};r.push(i),this.emit("timedmetadata",i)}this.parsedCues.set(e.pts,r),this.pruneParsedCues()}this.attachId3CueEnterListener()})}attachId3CueEnterListener(){if(this.id3TrackListener)return;const e=this.video.textTracks;if(!e)return;let t;for(let s=0;s<e.length;s++){const i=e[s];if("metadata"===i.kind&&"id3"===i.label){t=i;break}}if(!t||"function"!=typeof t.addEventListener)return;"disabled"===t.mode&&(t.mode="hidden");const s=()=>{const e=t?.activeCues;if(e)for(let t=0;t<e.length;t++){const s=e[t].startTime;if(this.presentedPts.has(s))continue;const i=this.parsedCues.get(s);if(i){this.presentedPts.add(s);for(const e of i)this.emit("timedmetadata",{...e,phase:"presented"})}}};t.addEventListener("cuechange",s),this.id3TrackListener={track:t,handler:s}}pruneParsedCues(){const e=this.video.currentTime-120;for(const t of this.parsedCues.keys())t<e&&(this.parsedCues.delete(t),this.presentedPts.delete(t))}setupVideoListeners(){const e=()=>{this.updateProgramDateTime(),this.emit("timeupdate")},t=()=>{this.emit("ended")},s=()=>{this.emit("error",this.video.error)},i=()=>{if(this.emit("seeked"),null!==this.deferredSeekTime){const e=this.deferredSeekTime;this.deferredSeekTime=null,this.seek(e)}},r=()=>{this.emit("volumechange")},n=()=>{this.emit("waiting")},a=()=>{this.emit("playing")},o=()=>{this.emit("pause")};this.video.addEventListener("timeupdate",e),this.video.addEventListener("ended",t),this.video.addEventListener("error",s),this.video.addEventListener("seeked",i),this.video.addEventListener("volumechange",r),this.video.addEventListener("waiting",n),this.video.addEventListener("playing",a),this.video.addEventListener("pause",o),this.boundHandlers.set("timeupdate",e),this.boundHandlers.set("ended",t),this.boundHandlers.set("error",s),this.boundHandlers.set("seeked",i),this.boundHandlers.set("volumechange",r),this.boundHandlers.set("waiting",n),this.boundHandlers.set("playing",a),this.boundHandlers.set("pause",o)}updateProgramDateTime(){const e=this.hls.levels,t=this.hls.currentLevel;if(t>=0&&e[t]?.details){const s=e[t].details;if(s?.hasProgramDateTime&&s.fragments.length>0){const e=s.fragments[0];e.programDateTime&&this.anchorProgramDateTime(e.programDateTime,e.start)}}}anchorProgramDateTime(e,t){this._programDateTime=new Date(e+1e3*(this.video.currentTime-t))}emit(e,t){const s=this.eventListeners.get(e);if(s)for(const e of s)try{e(t)}catch(e){console.error("[HlsJsAdapter] Event handler error:",e)}}}
|
|
1
|
+
import e from"hls.js";import{decodeUtf8 as t,parseId3GeobFrames as s}from"@dolby-optiview/ads-sdk-core/id3";export class HlsJsAdapter{constructor(e,t){this.eventListeners=new Map,this.boundHandlers=new Map,this._programDateTime=null,this.deferredSeekTime=null,this.levelBeforePin=null,this.parsedCues=new Map,this.presentedPts=new Set,this.id3TrackListener=null,this.hls=e,this.video=t,this.setupHlsListeners(),this.setupVideoListeners()}get currentTime(){return this.video.currentTime}get duration(){return this.video.duration}get isLive(){const e=this.hls?.latestLevelDetails;return"boolean"==typeof e?.live?e.live:null}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 playbackRate(){return this.video.playbackRate}set playbackRate(e){this.video.playbackRate=e}get videoElement(){return this.video}get supportsParallelBuffering(){return e.isSupported()}get programDateTime(){return this._programDateTime}pause(){this.video.pause()}async play(){await this.video.play()}seek(e){this.video.seeking?this.deferredSeekTime=e:this.video.currentTime=e}get sourceUrl(){return this.hls.url??null}async load(t){return new Promise((s,i)=>{const r=()=>{this.hls.off(e.Events.MANIFEST_PARSED,r),this.hls.off(e.Events.ERROR,n),s()},n=(t,s)=>{s.fatal&&(this.hls.off(e.Events.MANIFEST_PARSED,r),this.hls.off(e.Events.ERROR,n),i(new Error(`HLS load error: ${s.details}`)))};this.hls.on(e.Events.MANIFEST_PARSED,r),this.hls.on(e.Events.ERROR,n),this.hls.loadSource(t),this.hls.attachMedia(this.video)})}unload(){this.deferredSeekTime=null,this.hls.stopLoad(),this.hls.detachMedia()}recreateMediaPipeline(t){return this.deferredSeekTime=null,new Promise((s,i)=>{const r=()=>{this.video.removeEventListener("loadedmetadata",n),this.hls.off(e.Events.FRAG_BUFFERED,a),this.hls.off(e.Events.ERROR,d)},n=()=>{o()},a=()=>{o()},o=()=>{Math.abs(this.video.currentTime-t.startTime)>.5&&(this.video.currentTime=t.startTime),r(),s()},d=(e,t)=>{t.fatal&&(r(),i(new Error(`HLS pipeline recreate error: ${t.details}`)))};this.video.addEventListener("loadedmetadata",n),this.hls.on(e.Events.FRAG_BUFFERED,a),this.hls.on(e.Events.ERROR,d),this.hls.detachMedia(),this.hls.attachMedia(this.video);try{this.video.currentTime=t.startTime}catch{}this.hls.startLoad(t.startTime)})}async preload(t){if(e.isSupported())return new Promise(s=>{const i=new e({startFragPrefetch:!0,autoStartLoad:!0});let r=!1;const n=()=>{if(!r){r=!0,clearTimeout(d),i.off(e.Events.FRAG_LOADED,a),i.off(e.Events.ERROR,o);try{i.destroy()}catch{}s()}},a=()=>n(),o=(e,t)=>{t.fatal&&n()},d=setTimeout(n,8e3);i.on(e.Events.FRAG_LOADED,a),i.on(e.Events.ERROR,o),i.loadSource(t)})}get maxLiveSeekPosition(){const e=this.hls.latestLevelDetails;if(!e?.live)return null;const t=this.hls.liveSyncPosition;return"number"==typeof t&&Number.isFinite(t)?t:null}setVideoQuality(e){if(null===e){if(null===this.levelBeforePin)return!1;const e=this.levelBeforePin;try{return this.hls.nextLevel=e,this.hls.nextLevel!==e?!1:(this.levelBeforePin=null,!0)}catch{return!1}}const t=this.hls.levels;if(!t||t.length<2)return!1;let s=0;for(let e=1;e<t.length;e++)t[e].bitrate<t[s].bitrate&&(s=e);const i=t[s].bitrate;if("number"!=typeof i||!Number.isFinite(i)||i<=0)return!1;const r=null===this.levelBeforePin;let n;r&&(this.levelBeforePin=this.hls.manualLevel);try{this.hls.nextLevel=s,n=this.hls.nextLevel===s}catch{n=!1}return!!n||(r&&(this.levelBeforePin=null),!1)}on(e,t){this.eventListeners.has(e)||this.eventListeners.set(e,new Set),this.eventListeners.get(e).add(t)}off(e,t){const s=this.eventListeners.get(e);s&&s.delete(t)}destroy(){for(const[e,t]of this.boundHandlers)this.video.removeEventListener(e,t);this.boundHandlers.clear(),this.eventListeners.clear(),this.deferredSeekTime=null,this.id3TrackListener&&(this.id3TrackListener.track.removeEventListener("cuechange",this.id3TrackListener.handler),this.id3TrackListener=null),this.parsedCues.clear(),this.presentedPts.clear()}setupHlsListeners(){this.hls.on(e.Events.FRAG_CHANGED,(e,t)=>{t.frag.programDateTime&&this.anchorProgramDateTime(t.frag.programDateTime,t.frag.start)}),this.hls.on(e.Events.LEVEL_LOADED,(e,t)=>{if(t.details.hasProgramDateTime&&t.details.fragments.length>0){const e=t.details.fragments[0];e.programDateTime&&this.anchorProgramDateTime(e.programDateTime,e.start)}}),this.hls.on(e.Events.FRAG_PARSING_METADATA,(e,i)=>{for(const e of i.samples??[]){const i=s(e.data);if(0===i.length){const t={type:"id3",startTime:e.pts,raw:e.data,phase:"parsed"};this.emit("timedmetadata",t);continue}const r=[];for(const s of i){const i={type:"id3",startTime:e.pts,description:s.description,mimeType:s.mimeType,messageData:t(s.data),raw:e.data,phase:"parsed"};r.push(i),this.emit("timedmetadata",i)}this.parsedCues.set(e.pts,r),this.pruneParsedCues()}this.attachId3CueEnterListener()})}attachId3CueEnterListener(){if(this.id3TrackListener)return;const e=this.video.textTracks;if(!e)return;let t;for(let s=0;s<e.length;s++){const i=e[s];if("metadata"===i.kind&&"id3"===i.label){t=i;break}}if(!t||"function"!=typeof t.addEventListener)return;"disabled"===t.mode&&(t.mode="hidden");const s=()=>{const e=t?.activeCues;if(e)for(let t=0;t<e.length;t++){const s=e[t].startTime;if(this.presentedPts.has(s))continue;const i=this.parsedCues.get(s);if(i){this.presentedPts.add(s);for(const e of i)this.emit("timedmetadata",{...e,phase:"presented"})}}};t.addEventListener("cuechange",s),this.id3TrackListener={track:t,handler:s}}pruneParsedCues(){const e=this.video.currentTime-120;for(const t of this.parsedCues.keys())t<e&&(this.parsedCues.delete(t),this.presentedPts.delete(t))}setupVideoListeners(){const e=()=>{this.updateProgramDateTime(),this.emit("timeupdate")},t=()=>{this.emit("ended")},s=()=>{this.emit("error",this.video.error)},i=()=>{if(this.emit("seeked"),null!==this.deferredSeekTime){const e=this.deferredSeekTime;this.deferredSeekTime=null,this.seek(e)}},r=()=>{this.emit("volumechange")},n=()=>{this.emit("waiting")},a=()=>{this.emit("playing")},o=()=>{this.emit("pause")};this.video.addEventListener("timeupdate",e),this.video.addEventListener("ended",t),this.video.addEventListener("error",s),this.video.addEventListener("seeked",i),this.video.addEventListener("volumechange",r),this.video.addEventListener("waiting",n),this.video.addEventListener("playing",a),this.video.addEventListener("pause",o),this.boundHandlers.set("timeupdate",e),this.boundHandlers.set("ended",t),this.boundHandlers.set("error",s),this.boundHandlers.set("seeked",i),this.boundHandlers.set("volumechange",r),this.boundHandlers.set("waiting",n),this.boundHandlers.set("playing",a),this.boundHandlers.set("pause",o)}updateProgramDateTime(){const e=this.hls.levels,t=this.hls.currentLevel;if(t>=0&&e[t]?.details){const s=e[t].details;if(s?.hasProgramDateTime&&s.fragments.length>0){const e=s.fragments[0];e.programDateTime&&this.anchorProgramDateTime(e.programDateTime,e.start)}}}anchorProgramDateTime(e,t){this._programDateTime=new Date(e+1e3*(this.video.currentTime-t))}emit(e,t){const s=this.eventListeners.get(e);if(s)for(const e of s)try{e(t)}catch(e){console.error("[HlsJsAdapter] Event handler error:",e)}}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dolby-optiview/ads-sdk-adapter-hlsjs",
|
|
3
|
-
"version": "1.0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "HLS.js adapter for OptiView Ads SDK",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"sideEffects": false,
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@dolby-optiview/ads-sdk-core": "1.0.0
|
|
41
|
+
"@dolby-optiview/ads-sdk-core": "^1.0.0"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"hls.js": "^1.0.0"
|