Skip to main content

Ad Playback Delegate

What you learn​

This article is intended for front-end developers integrating DIVA Player with DIVA Back Office as the video streaming source. By the end of this article, your application controls client-side ads (VAST/VMAP) when a VOD playback resumes from a saved position, and receives a callback for every ad break that starts and ends on Web and WebTV.

note

adPlaybackDelegate is available from DIVA Web and WebTV 5.12.6.

Before starting​

  • Your video setup is working and you have a <videoId> and basic instantiation code for your platform.
  • Client-side ads are configured: the googleIMA lib is loaded (see Libs) and the video has a VAST/VMAP ad tag.
  • The playback is a VOD. The delegate is ignored for Live and DVR.
  • To resume a video, you know the resume position of the user. See Resume Point API to track it.

How it works​

adPlaybackDelegate is an optional object in the player configuration. Every method is optional. When no delegate is set, ads behave as before.

MethodWhen DIVA Player calls itWhat you do with it
shouldSkipAdsBeforeStart(videoId, startPosition)Once per VOD playback, before the ad schedule is requested.Return true to skip every ad break before the resume position. Return false to play the latest one.
onAdScheduleLoaded(videoId, cuePoints)When the ad schedule is loaded, before content starts.Read the ad break positions, for example to mark them on your own UI.
onAdBreakStarted(videoId, cue)When the first ad of an ad break starts.Track the ad break start.
onAdBreakEnded(videoId, cue, outcome)When an ad break that started ends.Track the result, for example to remember which ad breaks the user watched.

Times​

All times are content seconds:

  • The video trim-in is removed. 0 is the start of the content.
  • The pre-roll cue point is 0.
  • The post-roll is out of scope. DIVA Player never reports it.

Resume rule​

shouldSkipAdsBeforeStart receives startPosition, the resume position in seconds, from deepLinkType: "relative" and deepLinkValue. DIVA Player then applies one of two rules to the ad breaks scheduled at or before startPosition:

AnswerResult
trueEvery ad break at or before startPosition is skipped. Content starts directly at startPosition.
false (default)Only the latest ad break at or before startPosition plays. The ones before it are skipped. Content then resumes at startPosition.

Ad breaks after startPosition always play as scheduled.

Example: the schedule has a pre-roll (0) and mid-rolls at 300 and 900. The user resumes at 600.

  • true: no ad plays at start. The mid-roll at 900 plays later.
  • false: the mid-roll at 300 plays, then content resumes at 600. The mid-roll at 900 plays later.
info
  • shouldSkipAdsBeforeStart can return a boolean or a Promise<boolean>.
  • DIVA Player waits for the answer for 2 seconds at most (AD_PLAYBACK_DELEGATE_TIMEOUT_MS = 2000). A late answer, a rejected promise or a thrown error is treated as false.
  • When startPosition is 0, the playback is not a resume: ads behave as normal, whatever the answer.
  • With deepLinkType: "absolute", DIVA Player does not call shouldSkipAdsBeforeStart.
  • DIVA Player calls it when a video starts (first play or video switch). It does not call it again when the player recovers from an error: the first answer is kept.

Ad break outcomes​

onAdBreakEnded receives an outcome of type AdBreakOutcome:

OutcomeMeaning
"completed"Every ad of the ad break played until the end, and content resumed.
"skipped"The user skipped an ad of the ad break.
"abandoned"The ad break was left part-way: exit, player teardown, or playback stopped before it ended.
"failed"The ad break ended because of an ad error.

onAdBreakEnded is called only for an ad break that started (onAdBreakStarted was called for the same cue).

caution

Keep the callbacks fast and safe. DIVA Player catches and logs an error thrown by a callback, and playback continues.

Integration steps​

Step 1 — Write the delegate​

import type { AdPlaybackDelegate } from "diva-web-types";

const adPlaybackDelegate: AdPlaybackDelegate = {
shouldSkipAdsBeforeStart: async (videoId, startPosition) => {
// Example: skip ads before the resume position when the user already watched them
return await myAdHistory.hasWatchedAdsBefore(videoId, startPosition);
},
onAdScheduleLoaded: (videoId, cuePoints) => {
console.log("Ad schedule:", videoId, cuePoints);
},
onAdBreakStarted: (videoId, cue) => {
console.log("Ad break started:", videoId, cue);
},
onAdBreakEnded: (videoId, cue, outcome) => {
console.log("Ad break ended:", videoId, cue, outcome);
if (outcome === "completed") {
myAdHistory.saveWatchedAdBreak(videoId, cue);
}
},
};

myAdHistory is your own storage. Replace it with your logic.

Step 2 — Add the delegate and the resume position to the configuration​

Set adPlaybackDelegate in the configuration. Set deepLinkType: "relative" and deepLinkValue (milliseconds) to start from the resume position.

  • RW: deepLinkValue is an array, one value for each video of the configuration.
  • WebTV: deepLinkValue is a string.

Full code samples​

Write the Basic instantiation code. Extend it with adPlaybackDelegate as below.

import {
BoAdapterWebComponentProps,
DivaWebBoAdapter,
} from "@deltatre-vxp/diva-sdk/diva-web-bo-adapter";
import "@deltatre-vxp/diva-sdk/diva-web-sdk/index.min.css";
import type { AdPlaybackDelegate } from "diva-web-types";

const videoId = "<videoId>";
const resumePositionMs = 600000; // 10 minutes, read from your resume point storage

const libs = {
mux: "https://cdnjs.cloudflare.com/ajax/libs/mux.js/6.2.0/mux.min.js",
shaka: "https://cdnjs.cloudflare.com/ajax/libs/shaka-player/4.11.2/shaka-player.compiled.js",
hlsJs: "https://cdn.jsdelivr.net/npm/hls.js@1.5.7",
googleIMA: "https://imasdk.googleapis.com/js/sdkloader/ima3.js",
googleDAI: false,
googleCast: false,
threeJs: false,
};

const adPlaybackDelegate: AdPlaybackDelegate = {
shouldSkipAdsBeforeStart: (videoId, startPosition) => {
console.log("Resume:", videoId, startPosition);
return true;
},
onAdScheduleLoaded: (videoId, cuePoints) => console.log("Ad schedule:", videoId, cuePoints),
onAdBreakStarted: (videoId, cue) => console.log("Ad break started:", videoId, cue),
onAdBreakEnded: (videoId, cue, outcome) => console.log("Ad break ended:", videoId, cue, outcome),
};

const config = {
videoId,
libs,
deepLinkType: "relative",
deepLinkValue: [resumePositionMs],
// When no delegate is set, DIVA keeps its default client-side ad behaviour:
// no resume rule is applied and no ad break events are reported.
adPlaybackDelegate,
};

function App() {
const props: BoAdapterWebComponentProps = {
settingsUrl: "<settings URL>",
languageCode: "en-US",
languageDictionary: "en-US",
onDivaBoAdapterError: console.error,
config,
};

return <DivaWebBoAdapter {...props} />;
}

export default App;

Reference​

export const AdBreakOutcome = {
COMPLETED: "completed",
SKIPPED: "skipped",
ABANDONED: "abandoned",
FAILED: "failed",
} as const;

export type AdBreakOutcome = (typeof AdBreakOutcome)[keyof typeof AdBreakOutcome];

export interface AdPlaybackDelegate {
shouldSkipAdsBeforeStart?: (videoId: string, startPosition: number) => boolean | Promise<boolean>;
onAdScheduleLoaded?: (videoId: string, cuePoints: number[]) => void;
onAdBreakStarted?: (videoId: string, cue: number) => void;
onAdBreakEnded?: (videoId: string, cue: number, outcome: AdBreakOutcome) => void;
}

export const AD_PLAYBACK_DELEGATE_TIMEOUT_MS = 2000;
ParameterTypeDescription
videoIdstringId of the video that plays.
startPositionnumberResume position in content seconds.
cuePointsnumber[]Ad break positions in content seconds. 0 is the pre-roll. The post-roll is not included.
cuenumberAd break position in content seconds.
outcomeAdBreakOutcome"completed", "skipped", "abandoned" or "failed".