videojs-ima: Unexpected invocation to startLinearAdMode

Hi, there is a bug with the PlayerWrapper class.

The bug occurs when you try to load a pre-roll ad that takes more than the time specified in the prerollTimeout setting to load.

The problem is that when the adtimeout event is triggered and then the adsready event is triggered, the pre-roll ad is displayed but UI is not updated to reflect that. For example, the vjs-ad-playing is not added to the player and the content playback is not resumed after pre-roll is ended.

The bug is caused because in the method onAdBreakStart the videojs-contrib-ads startLinearAdMode function is called, even if the adtimeout event was fired before.

This causes a WARN: ‘Unexpected startLinearAdMode invocation (Preroll)’, so this is an unexpected behavior for videojs-contrib-ads.

An easy way to reproduce this is set prerollTimeout in contribAdsSettings to a small value like 10.

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 1
  • Comments: 17 (3 by maintainers)

Commits related to this issue

Most upvoted comments

Hi, @GavinThompson. In my case I can fix my specific problem adding this workaround to my code. Check it out! I could be good for you, too.

/* Work around (started pre-roll after timeout event) */
const AD_PLAYING_CLASS = 'vjs-ad-playing';
const EVENTS = {
  AD_STARTED: 'ads-ad-started',
  ADS_READY: 'adsready',
  AD_TIMEOUT: 'adtimeout'
};

const onAdCompleted = () => {
  player.removeClass(AD_PLAYING_CLASS);
};

const onAdStarted = () => {
  player.addClass(AD_PLAYING_CLASS);
  player.ima.addEventListener([
    imaSdk.AdEvent.Type.COMPLETE,
    imaSdk.AdEvent.Type.SKIPPED
  ], onAdCompleted);
  player.off(EVENTS.AD_STARTED, onAdStarted);
};

const onAdsReady = () => {
  player.on(EVENTS.AD_STARTED, onAdStarted);
  player.off(EVENTS.ADS_READY, onAdsReady);
};

const onAdTimeout = () => {
  player.on(EVENTS.ADS_READY, onAdsReady);
  player.off(EVENTS.AD_TIMEOUT, onAdTimeout);
};

player.on(EVENTS.AD_TIMEOUT, onAdTimeout);
/* End work around */

I’ve managed to get it working in two ways.

  1. Downgrading videojs-contrib-ads to version 6.0.0. I can confirm this problem started happening somewhere between 6.0.1 and 6.1.0.

  2. I’ve also managed to get it working by adding a check on SdkImpl.prototype.onPlayerReadyForPreroll to check if the player is not in resuming state, as it seems videojs-contrib-ads ask for it now, and also implicitly calling a resume/play function on videojs-contrib-ads endLinearAdMode() functions. However, this last option wouldn’t play ads if they loaded after the player was already resuming (unless you increased your prerollTimeout). And it seemed to happen quite a lot, so I stick with the first method.

There was also a secondary problem which seemd to happen. When the HLS was resuming from ads, it seemed to get stuck on loading sometimes. The main .m3u8 playlist was being fetched, but not the .ts segments.

I’ve managed to fix it by adding a play event on adend. As such:

player.on('adend', function(e) {
    player.play();
})`

Workaround 1: Use https://cdnjs.cloudflare.com/ajax/libs/videojs-contrib-ads/6.0.0/videojs-contrib-ads.js instead.

Workaround 2 (recent versions):

In videojs.ima.js, change:

SdkImpl.prototype.onPlayerReadyForPreroll = function () {
  if (this.autoPlayAdBreaks) {

To

SdkImpl.prototype.onPlayerReadyForPreroll = function () {
  if (this.autoPlayAdBreaks && !player.ads.inAdBreak() && !player.ads.isContentResuming()) {

In videojs-contrib-ads.js, wherever there is a endLinearAdMode() function, add this to the end:

var _this = this;
  this.afterLoadStart(function () {
  _this.resumeAfterNoPreroll(player);
});

You may want to increase your prerollTimeout in player.ima options too.