Skip to main content
Version: 7.12.0

How to display visual cues on the timeline to mark mid-rolls?

You may ask this question if you want to let the user know when and how many mid-rolls there are during the video.

Similar questions may be:

  • Can I show where my ad is during the video?
  • How can I let the user know when the ads will be during the video like in YouTube?
  • Can ad markers be added to the timeline?

Showing visual cues indicating the moment in which the ads will start is certainly useful. This is not (yet) a THEOplayer standard feature, but what follows is a first step in that direction. Please keep in mind that this solution is still in development and has to date several limitations.

Prerequisites and limitations

  • It only works for THEOplayer ad integration

  • A timeOffset for each roll must be included in the AdDescription

  • timeOffset values must be one of the following:

  • a keyword (“start” and “end” allowed);

  • a number (seconds from video beginning);

  • undefined (which indicates a pre-roll).

  • it has only been tested on Chrome, Firefox and Edge for Win 10 and with the standard THEOplayer UI.

The code

Add this line to your CSS

.adCue {
width: 0.3em;
height: 100%;
background-color: white;
position: absolute;
top: 0;
}

Your AdDescription should look something like this

player.source.ads: [
{
"sources": "path/to/your-pre/midroll1.xml",
"timeOffset": value //valid values: ["start", "end", numbers]
},
{
"sources": "path/to/your-pre/midroll2.xml",
"timeOffset": value //valid values: ["start", "end", numbers]
},
{...}
]

Include (or call) this JavaScript snippet on your page:

THEOdisplayCues.js

var adsPresent = !!player.source.ads;
var contentDuration = 0;
var to = [];
var checker = true;
var playedAds = [];

player.addEventListener('pause', () => {
checker = false;
});
player.addEventListener('playing', () => {
var adIsPlaying = player.ads.playing;
detectTimeOffset();
detectDuration(adIsPlaying);
if (!checker) {
checker = adIsPlaying;
}
if (checker) {
!adIsPlaying ? drawCues() : destroyCues();
}
});

player.addEventListener('sourcechange', function () {
playedAds.length = 0;
});

player.ads.addEventListener('adend', (adEvent) => {
playedAds.push(adEvent.ad.adBreak.timeOffset);
});

function drawCues() {
var progressBar = element.querySelector('.vjs-progress-control.vjs-control');

for (var i = 0; i < to.length; i++) {
var timeOffset = to[i];

if (playedAds.indexOf(timeOffset) === -1) {
var left = (to[i] * progressBar.offsetWidth) / contentDuration;
var div = document.createElement('div');
div.setAttribute('to', to[i]);
div.classList.add('adCue');
div.style.left = left + 'px';
progressBar.children[0].appendChild(div);
}
}
}

function destroyCues() {
var cues = element.querySelectorAll('.vjs-progress-control.vjs-control .adCue');
for (cue of cues) {
cue.parentNode.removeChild(cue);
}
}

function detectDuration(adIsPlaying) {
if (!adIsPlaying) {
contentDuration = player.duration;
//console.log("Duration ",contentDuration)
}
var t = 0;
for (var ad of player.source.ads) {
to[t] = to[t] == 'end' ? contentDuration : to[t];
t++;
}
}

function detectTimeOffset() {
if (adsPresent) {
var t = 0;
for (var ad of player.source.ads) {
to[t] = ad.timeOffset == undefined ? 0 : ad.timeOffset == 'start' ? 0 : ad.timeOffset;
t++;
}
}
//console.log("TimeOffsets ",to);
}