Skip to content

Commit

Permalink
Use TextTrackCue "enter" event to improve Interstitial playback timing
Browse files Browse the repository at this point in the history
Restore DateRange metadata TextTrack cues when re-attaching media
  • Loading branch information
robwalch committed Dec 10, 2024
1 parent de41b0a commit c5a3ae0
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 5 deletions.
4 changes: 4 additions & 0 deletions api-extractor/report/hls.js.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1429,6 +1429,8 @@ export enum Events {
// (undocumented)
ERROR = "hlsError",
// (undocumented)
EVENT_CUE_ENTER = "hlsEventCueEnter",
// (undocumented)
FPS_DROP = "hlsFpsDrop",
// (undocumented)
FPS_DROP_LEVEL_CAPPING = "hlsFpsDropLevelCapping",
Expand Down Expand Up @@ -2269,6 +2271,8 @@ export interface HlsListeners {
// (undocumented)
[Events.ERROR]: (event: Events.ERROR, data: ErrorData) => void;
// (undocumented)
[Events.EVENT_CUE_ENTER]: (event: Events.EVENT_CUE_ENTER, data: {}) => void;
// (undocumented)
[Events.FPS_DROP]: (event: Events.FPS_DROP, data: FPSDropData) => void;
// (undocumented)
[Events.FPS_DROP_LEVEL_CAPPING]: (event: Events.FPS_DROP_LEVEL_CAPPING, data: FPSDropLevelCappingData) => void;
Expand Down
35 changes: 31 additions & 4 deletions src/controller/id3-track-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,13 @@ class ID3TrackController implements ComponentAPI {
this.media = null;
this.dateRangeCuesAppended = {};
// @ts-ignore
this.hls = null;
this.hls = this.onEventCueEnter = null;
}

private _registerListeners() {
const { hls } = this;
hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this);
hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
hls.on(Events.FRAG_PARSING_METADATA, this.onFragParsingMetadata, this);
Expand All @@ -123,6 +124,7 @@ class ID3TrackController implements ComponentAPI {
private _unregisterListeners() {
const { hls } = this;
hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this);
hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
hls.off(Events.FRAG_PARSING_METADATA, this.onFragParsingMetadata, this);
Expand All @@ -131,6 +133,13 @@ class ID3TrackController implements ComponentAPI {
hls.off(Events.LEVEL_PTS_UPDATED, this.onLevelPtsUpdated, this);
}

private onEventCueEnter = () => {
if (!this.hls) {
return;
}
this.hls.trigger(Events.EVENT_CUE_ENTER, {});
};

// Add ID3 metatadata text track.
private onMediaAttaching(
event: Events.MEDIA_ATTACHING,
Expand All @@ -142,6 +151,13 @@ class ID3TrackController implements ComponentAPI {
}
}

private onMediaAttached() {
const details = this.hls.latestLevelDetails;
if (details) {
this.updateDateRangeCues(details);
}
}

private onMediaDetaching(
event: Events.MEDIA_DETACHING,
data: MediaDetachingData,
Expand All @@ -153,7 +169,7 @@ class ID3TrackController implements ComponentAPI {
}
if (this.id3Track) {
if (this.removeCues) {
clearCurrentCues(this.id3Track);
clearCurrentCues(this.id3Track, this.onEventCueEnter);
}
this.id3Track = null;
}
Expand Down Expand Up @@ -349,7 +365,9 @@ class ID3TrackController implements ComponentAPI {
delete dateRangeCuesAppended[id];
Object.keys(cues).forEach((key) => {
try {
id3Track.removeCue(cues[key]);
const cue = cues[key];
cue.removeEventListener('enter', this.onEventCueEnter);
id3Track.removeCue(cue);
} catch (e) {
/* no-op */
}
Expand Down Expand Up @@ -429,17 +447,26 @@ class ID3TrackController implements ComponentAPI {
if (isSCTE35Attribute(key)) {
data = hexToArrayBuffer(data);
}
const payload: any = { key, data };
const cue = createCueWithDataFields(
Cue,
startTime,
endTime,
{ key, data },
payload,
MetadataSchema.dateRange,
);
if (cue) {
cue.id = id;
this.id3Track.addCue(cue);
cues[key] = cue;
if (
__USE_INTERSTITALS__ &&
this.hls.config.interstitialsController
) {
if (key === 'X-ASSET-LIST' || key === 'X-ASSET-URL') {
cue.addEventListener('enter', this.onEventCueEnter);
}
}
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/controller/interstitials-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export default class InterstitialsController
hls.on(Events.AUDIO_TRACK_UPDATED, this.onAudioTrackUpdated, this);
hls.on(Events.SUBTITLE_TRACK_SWITCH, this.onSubtitleTrackSwitch, this);
hls.on(Events.SUBTITLE_TRACK_UPDATED, this.onSubtitleTrackUpdated, this);
hls.on(Events.EVENT_CUE_ENTER, this.onInterstitialCueEnter, this);
hls.on(Events.ASSET_LIST_LOADED, this.onAssetListLoaded, this);
hls.on(Events.BUFFER_APPENDED, this.onBufferAppended, this);
hls.on(Events.BUFFER_FLUSHED, this.onBufferFlushed, this);
Expand All @@ -158,6 +159,7 @@ export default class InterstitialsController
hls.off(Events.AUDIO_TRACK_UPDATED, this.onAudioTrackUpdated, this);
hls.off(Events.SUBTITLE_TRACK_SWITCH, this.onSubtitleTrackSwitch, this);
hls.off(Events.SUBTITLE_TRACK_UPDATED, this.onSubtitleTrackUpdated, this);
hls.off(Events.EVENT_CUE_ENTER, this.onInterstitialCueEnter, this);
hls.off(Events.ASSET_LIST_LOADED, this.onAssetListLoaded, this);
hls.off(Events.BUFFER_CODECS, this.onBufferCodecs, this);
hls.off(Events.BUFFER_APPENDED, this.onBufferAppended, this);
Expand Down Expand Up @@ -822,6 +824,10 @@ MediaSource ${JSON.stringify(attachMediaSourceData)} from ${logFromSource}`,
}
};

private onInterstitialCueEnter() {
this.onTimeupdate();
}

private onTimeupdate = () => {
const currentTime = this.currentTime;
if (currentTime === undefined || this.playbackDisabled) {
Expand Down
3 changes: 3 additions & 0 deletions src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ export enum Events {
INTERSTITIALS_PRIMARY_RESUMED = 'hlsInterstitialsPrimaryResumed',
// Interstitial players dispatch this event when playout limit is reached
PLAYOUT_LIMIT_REACHED = 'hlsPlayoutLimitReached',
// Event DateRange cue "enter" event dispatched
EVENT_CUE_ENTER = 'hlsEventCueEnter',
}

/**
Expand Down Expand Up @@ -490,6 +492,7 @@ export interface HlsListeners {
event: Events.PLAYOUT_LIMIT_REACHED,
data: {},
) => void;
[Events.EVENT_CUE_ENTER]: (event: Events.EVENT_CUE_ENTER, data: {}) => void;
}
export interface HlsEventEmitter {
on<E extends keyof HlsListeners, Context = undefined>(
Expand Down
5 changes: 4 additions & 1 deletion src/utils/texttrack-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export function addCueToTrack(track: TextTrack, cue: VTTCue) {
}
}

export function clearCurrentCues(track: TextTrack) {
export function clearCurrentCues(track: TextTrack, enterHandler?: () => void) {
// When track.mode is disabled, track.cues will be null.
// To guarantee the removal of cues, we need to temporarily
// change the mode to hidden
Expand All @@ -59,6 +59,9 @@ export function clearCurrentCues(track: TextTrack) {
}
if (track.cues) {
for (let i = track.cues.length; i--; ) {
if (enterHandler) {
track.cues[i].removeEventListener('enter', enterHandler);
}
track.removeCue(track.cues[i]);
}
}
Expand Down

0 comments on commit c5a3ae0

Please sign in to comment.