Closed chwagssd closed 3 years ago
Hello @chwagssd ,
Thank you for raising this issue. Smart TVs are not official supported by the IMA SDK but are part of a list of devices not officially supported. For that reason, and because there is a work around to use IMA without the videoJS-IMA plugin on smart TVs, this issue will be low priority for the IMA team to work on. In addition, the team does not have these devices to test the changes on.
However, because the videoJS-IMA plugin, if you or your team is able to work on a pull request to resolve the issue, I am happy to review the changes and release a new version of the plugin.
Feel free to reply here and I am happy to assist how I can.
Thank you, Jackson IMA SDK DevRel
Hi @Kiro705 thank you for the response and direction on the issue. I was able to isolate the primary issue, which is a race condition, where the IMA SDK is starting preroll ads before the readyforpreroll
event is fired by videojs-contrib-ads:
https://videojs.github.io/videojs-contrib-ads/integrator/api.html readyforpreroll (EVENT) – Indicates that your ad plugin may start a preroll ad break by calling startLinearAdMode.
What this results in is the videojs-contrib-ads plugin being unable to restore the playback because the state is messed up.
Here is a workaround I found that works for Vizio/HiSense:
const IMA_OPTIONS = {
timeout: 2000,
autoPlayAdBreaks: false,
showControlsForJSAds: true,
prerollTimeout: 5000,
vastLoadTimeout: 5000,
preventLateAdStart: false,
enablePreloading: false,
showCountdown: true,
adsRenderingSettings: {
restoreCustomPlaybackStateOnAdBreakComplete: false,
},
videojsContribAds: {
stitchedAds: true, // using the same video player, not a 2nd
restorePlayerSnapshot: true,
},
}
autoplay: true
, manually start the ad breaklet allowedToShowPreroll = false;
// Will be called to tell videojs-contrib-ads that we are in linear ad mode AFTER ad break starts
const startLinearAdMode = () => {
console.log('startLinearAdMode()');
if (!p.ads.inAdBreak()) {
console.log('VideoPlayer::startLinearAdMode() - calling player.ads.startLinearAdMode()');
p.ads.startLinearAdMode();
} else {
console.log('startLinearAdMode() - already inAdBreak(), not calling player.ads.startLinearAdMode()');
}
};
// Manual callback for IMA to use when ad break is ready, instead of auto showing ads
const adBreakReadyListener = (evt) => {
console.log('adBreakReadyListener', evt,' in ad break? ', p.ads.inAdBreak(), allowedToShowPreroll);
// we are showing before the 'readyforpreroll' fired, because videojs-imasdk
// plugin doesn't follow videojs-contrib-ads required plugin
// order of operations of waiting for this event to begin playback
if (!allowedToShowPreroll) {
p.one('readyforpreroll', startLinearAdMode);
}
p.ima.playAdBreak();
}
// Call this to play a video with ads
const loadVideoWithAds = async (src, vmapAdUrl) () => {
// reset flag
allowedToShowPreroll = false; // since videojs-ima is not waiting as it should
// pre fetch the VAST XML so that the player can synchronously have it when
// src is set, not required if using p.ima.setContentWithAdTag(src, adTagUrl)
const xml = await ky.get( vmapAdUrl ).text();
// 1st video needs to initialize ima() and show ad
if (!p.ima.initializeAdDisplayContainer) {
p.on('readyforpreroll', () => { allowedToShowPreroll = true; });
p.ima(IMA_OPTIONS);
p.ima.setAdBreakReadyListener(adBreakReadyListener);
p.ima.initializeAdDisplayContainer();
p.ima.controller.playerWrapper.seekContentToZero = () => {};
console.log('initialized ads plugin and now going to load ', src);
p.ima.setContentWithAdsResponse(src, xml);
//2nd+ video can use ima, it won't start loading an ad unless you tell it to by resetting the ad tag and calling requestAds()
} else {
p.ima.changeAdTag(null);
p.ima.setContentWithAdsResponse(src, xml);
p.one('loadedmetadata', () => {
p.ima.requestAds(); // after video is queued up, get ad requests fired off against the xml
});
}
}
This type of bootstrapping is complex to go about as I did above, but the reason I did it that way was to make sure that the various plugins interacted in a predictable way when there is only 1 video player in the page, since as soon as IMA SDK begins playing back an ad in the single <video>
before VideoJS Contrib Ads has done its snapshot we get into an unsupported ad plugin state.
I think videojs-ima
needs to wait for the 'readyforpreroll'
event before it starts playing back the ads, and that might solve it.
Hi @chwagssd ,
Thanks for following up with the work around.
I was looking into where the videojs-ima plugin watches for 'readyforpreroll'
. Here in sdk-impl.js ads are played after 'readyforpreroll'
if 'autoPlayAdBreaks'
is true (it is true by default). Otherwise, ads are started here in sdk-impl.js.
Looking at this, I am unsure what would need to be changed to make sure to wait for the 'readyforpreroll'
, as it looks like that is already happening.
I don't think the videojs-ima plugin has any specifics to support single video player implementations, so the issue may rest there.
Please let me know if I am missing something.
Thank you, Jackson
I reviewed that link to where this.initAdsManager
is called when autoPlayAdBreaks
is enabled, however, there is another place it happens - perhaps it's getting called earlier when autoPlayAdBreaks is false
The problem I had to protect against was my callback getting called BEFORE the 'readyforpreroll' fired, for example:
adBreakReady
player.on('readyforpreroll', (evt){console.log('now contrib ads plugin says IMA has permission to take over and show ads'); });
player.ima.setAdBreakReadyListener((evt){console.log('Got ad break ready, so I think I can play an ad, did IMA wait to emit this callback until "readyforpreroll"?'); });
Is it possible that the plugin receives the google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED
and the google.ima.AdEvent.Type.AD_BREAK_READY
event before the player's readyforpreroll
event? Because in that case this.playerWrapper.onAdBreakStart()
would be called prematurely. It appears like some sort of race condition where the ads manager thinks it's allowed to begin playing the ads, even though 'readyforpreroll' has not yet fired.
@chwagssd I am developing application for samsung(tizen)and LG(webOS) , and I was facing the same issue for content not playing back after the google ads. This solution seem to worked for me https://github.com/googleads/videojs-ima/issues/601
var _currentAd
player.ima.addEventListener(google.ima.AdEvent.Type.STARTED, onAdStarted)
player.ima.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdComplete)
function onAdStarted (ev) {
_currentAd = ev.getAd()
}
function onAdComplete () {
if (options.forceReload) { // only if we decide it's needed
var adPod = _currentAd.getAdPodInfo()
var podInfo = {
podPosition: adPod.getAdPosition(),
podLength: adPod.getTotalAds()
}
_forceReload(podInfo)
}
_currentAd = null
}
function _forceReload (podInfo) {
if (podInfo.podPosition < podInfo.podLength) { return }
// _getSrc() returns something like { url: 'https://...', type: 'video/mp4' } which is the exact same value as originally used to play the content
player.src(_getSrc())
setTimeout(function () { // might not be needed
player.play()
}, 200) // arbitrary
}
Hi @Kiro705, I am facing a similar issue but this time error is being thrown before an ad is completed. In a 15 seconds ads, the error is being thrown within 5-8 seconds.
More info can be found https://github.com/videojs/video.js/issues/8238
Hi @chwagssd , I encountered the same problem as you. I replaced it with m3u8 video. On certain devices such as the iPhone, the video could not be played after the pre-roll Ads, and error 4 was prompted.
When using HLS playlist, PreRoll ads show, but leave the player in a broken state. The plugin thinks its still in an ad break
player.ads.isAdPlaying() === true
and the HLS playlist never gets restored. The same behavior is observed for MidRoll.For anyone trying to reproduce this issue without a smart tv (i.e Vizio/Hisense/Samsung TIzen TV - happens on all of them), all you need to do is run Chrome with the a user agent string that contains "smartTv", then when you hit an ad break, you can see
document.querySelectorAll('video').length
is 1 instead of the usual 2 or 3. Normally video ads are played in a separate<video>
placed over the content video. But ima sdk re-uses the singlevizio-720p-FW/4.40.21 Model/D24f-F1) smartTv
Google IMA SDK will re-use a video player on smart tv platforms. I am able to get VideoJS working with the IMA SDK when I use a custom ad integration and connect Google IMA SDK (ima3.js) to VideoJS manually, handling the CONTENT_PAUSE_REQUESTED and CONTENT_RESUME_REQUESTED events, at least for pre-roll. Using this videojs-ima plugin does not work for pre-roll, mid-roll, etc.
The docs for videojs-ima and videojs-contrib-ads both indicate that they will work on platforms where they reuse the single VideoJS player that content is playing.
Steps to reproduce:
Expected result:
preroll plays, and then HLS playlist plays
Actual Result:
iframeConsoleRunner-7f4d47902dc785f30dedcac9c996b9f31d4dfcc33567cc48f0431bc918c2bf05.js:1 VIDEOJS: ERROR: (CODE:4 MEDIA_ERR_SRC_NOT_SUPPORTED) The media could not be loaded, either because the server or network failed or because the format is not supported. Tt {code: 4, message: "The media could not be loaded, either because the …rk failed or because the format is not supported."}