Za oči zdravější...

19. 5. 1999
Doba čtení: 6 minut

Sdílet

Ilustrační obrázek
Autor: Depositphotos – stori
Ilustrační obrázek
Když jsem začal dělat s Linuxem, samozřejmě jsem se setkal i s XWindows. To byla jedna z prvních věcí, kterou jsem se pokusil nakonfigurovat. Zatímco Linux jako takový po instalaci v podstatě fungoval podle mých představ, Xka bylo potřeba trochu přizpůsobit. I kdyby nic jiného, chtěl jsem změnit rozlišení, jsa zvyklý na 1024x768pixelů v 16bitové barevné hloubce. Samozřejmě jsem přikročil k nejsnazšímu řešení a použil jsem redhatí Xconfigurator. Až teprve nedávná debata v konferenci mě přiměla k takovému menšímu experimentování a najednou jsem se nestačil divit, co všechno moje stařičká grafická karta dokáže.
Malá exkurze do světa monitorů

Běžný monitor je analogové zařízení. Obraz je tvořen množstvím malých bodů, které „rozsvěcuje“ elektronový paprsek vystřelovaný za zadní části monitoru na přední plochu obrazovky. Tento paprsek se pohybuje z levého horního rohu obrazu po jednotlivých řádcích až do pravého dolního rohu. Kromě vlastní informace o obrazu posílá počítač i tzv. vertikální a horizontální synchronizační pulzy. Tyto pulzy určují horizontální a vertikální obnovovací frekvenci. Horizontální frekvence (HF) se uvádí v KHz (KiloHertz) a určuje, kolik řádků obrazu bude vykresleno za jednu vteřinu. Vertikální frekvence (VF) je uváděna v H (Hertz) a značí, kolikrát za jednu vteřinu bude překreslen celý obraz. Za dostatečně ergonomickou hodnotu pro VF se obecně považuje 70–75 Hz, ale osobně se domnívám, že jde o poměrně subjektivní záležitost. Hodnoty nižší způsobují, že oko dokáže rozlišit mihotání obrazu, které je způsobené pohasínáním bodů obrazu mezi jednotlivými zásahy elektronovým paprskem. K výše uvedeným hodnotám je pro charakteristiku monitoru podstatná ještě šířka pásma (bandwidth), která se pro změnu zase uvádí v MHz (MegaHertz). Ta v podstatě značí, kolik bodů za vteřinu je monitor schopen osvítit. Dále si ještě zavedeme hodnoty XR a YR, které značí horizontální a vertikální rozlišení obrazu v pixelech (to je těch obvyklých 800×600 nebo 1024×768 pixelů apod.)

Konfigurační soubor XF86Config
Tento soubor se vyskytuje obvykle v adresáři /etc/X11/ a obsahuje prakticky všechna podstatná nastavení pro Xserver. My se budeme zaobírat pouze těmi hodnotami, které mají co do činění s nastavením zobrazovacího subsystému.
Sekce Device obsahuje identifikaci grafické karty a případně některá její nastavení. Moje sekce Device vypadá takto:

Section "Device"
    Identifier  "Vision 868"
    VendorName  "Unknown"
    BoardName   "Unknown"
    Option      "nolinear"
EndSection

Až na volbu nolinear bude vypadat tato sekce u vás vypadat zřejmě hodně podobně. Identifier slouží k provázání s ostatními sekcemi, zbývající parametry jsou nepodstatné a mohou být vyplněny prakticky čímkoliv.

V sekci Monitor jsou definovány parametry, které přímo ovlivňují rozlišení i obnovovací frekvenci monitoru:

Section "Monitor"
    Identifier  "CTX"
    VendorName  "CTX"
    ModelName   "1785"

    HorizSync   30 - 85
    VertRefresh 50-120

# 1024x768
Modeline "1024x768"    85    1024 1032 1152 1360   768  784  787  823
# 1152x864
Modeline "1152x864" 100.00   1152 1160 1384 1440   864  867  880  904

EndSection

Stejně jako u grafické karty, volba Identifier slouží k identifikaci monitoru v jiných sekcích. HorizSync respektive VertRefresh určují rozsah podporovaných horizontálních respektive vertikálních obnovovacích frekvencí. Pak již následují definice parametrů pro jednotlivá rozlišení, tzv. Modelines. Na ty se podíváme podrobněji později.

Sekce Screen definuje parametry zobrazovacího subsystému:

Section "Screen"
    Driver      "accel"
    Device      "Vision 868"
    Monitor     "CTX"
    Subsection "Display"
        Depth       8
        Modes       "1024x768"
        ViewPort    0 0
        Virtual     1024 768
    EndSubsection
    Subsection "Display"
        Depth       16
        Modes       "1152x864"
        ViewPort    0 0
    EndSubsection
EndSection
Driver

určuje, který Xserver celou sekci použije. V tomto případě jde o akcelerované Xservery.
Device definuje vazbu na odpovídající sekci Device.
Monitor dělá totéž, ovšem pro sekci Monitor.
Pak následují podsekce pro jednotlivá rozlišení. V mém případě bude možné používat rozlišení 1024×768 v 8bitové barevné hloubce a 1152×864 v 16bitové barevné hloubce. Je nutno podotknout, že volba Modes obsahuje jméno rozlišení, shodné s některým jménem Modeline v sekci Monitor. Jak vidno, ani jedno z rozlišení nevyužívá možnost virtuální obrazovky.

Modelines
Jak jsem již dříve naznačil, parametr Modeline je klíčovým prvkem v nastavení rozlišení, případně obnovovací frekvence. Je velice pravděpodobné, že vás ta řada nepochopitelných čísel vyděsila jako mě. I přes hrozivý dojem, který vyvolává, to však není tak zlé. Stačí si totiž zjistit šířku pásma (bandwidth), kterou váš monitor podporuje, určit si rozlišení a pak již jenom postupovat podle jednoduchého vzorce, se kterým se teď seznámíme.
Nejdříve si tedy řekněme, co která hodnota znamená:

Tabulka č. 5
Name Dot clock Horizontal timing Vertical timing
XR HSS HSE VFL YR VSS VSE VFL
„1152×864“ 100 1152 864

Name

je jméno režimu, kterým je třeba se odkazovat v sekci Screen, respektive v subsekci Display.
Dot clock obsahuje hodnotu, která ovlivňuje výslednou obnovovací frekvenci. Lze do ní dosadit šířku pásma monitoru (bandwidth), ale může nastat situace, že se se Xserver odmítne spustit s hláškou, že maximální možná frekvence byla překročena. Logicky je zde třeba se přizpůsobit slabšímu z dvojice zařízení monitorgrafická karta. Maximální hodnotu lze snadno zjistit zavoláním
X -probeonly -bpp 16
POZOR: je důležité použít parametr -bpp (bitová barevná hloubka), protože Xserver vrací jinou hodnotu například pro 8bitovou nebo pro 16bitovou barevnou hloubku.
XR je šířka obrazu v pixelech
HSS je začátek horizontálního synchronizačního pulzu
HSE je konec horizontálního synchronizačního pulzu
HFL je celková délka horizontálního snímku
YR je výška obrazu v pixelech
VSS je začátek vertikálního synchronizačního pulzu
VSE je konec vertikálního synchronizačního pulzu
VFL je celková délka vertikálního snímku

Podrobnější popisy parametrů HSS, HSE, HFL, VSS, VSE a VFL najdete například v dokumentaci k Xserveru nebo ve VideoTimings HOWTO. My se nyní spokojíme s postupem, jak tyto hodnoty vypočítat. Je to opravdu jednoduché:

HFL = XR / 0,8
Pokud není výsledek dělitelný osmi, je třeba jej zaokrouhlit dolů na nejbližší číslo, které dělitelné je
HFL = 1152 / 0,8 = 1440 (1440 je dělitelné osmi, takže není třeba zaokrouhlovat)

VFL = YR * 1,05
Opět, pokud není výsledek dělitelný osmi, je třeba jej zaokrouhlit dolů na nejbližší číslo, které dělitelné je
VFL = 864 * 1,05 = 907,2 : není dělitelné osmi, zaokrouhlíme tedy výsledek na hodnotu 904

Tabulka teď vypadá takto:

Tabulka č. 6
Name Dot clock Horizontal timing Vertical timing
XR HSS HSE VFL YR VSS VSE VFL
„1152×864“ 100 1152 1440 864 904

Zbylé hodnoty se počítají ještě snáze:

HSS = XR + 32
HSS = 1152 + 32 = 1184

HSE = HFL – 32
HSE = 1440 32 = 1408

VSS = YR + 3
VSS = 864 + 3 = 867

VSE = VSS + 3
VSE = 867 + 3 = 870

A všechno pohromadě:

Tabulka č. 7
Name Dot clock Horizontal timing Vertical timing
XR HSS HSE VFL YR VSS VSE VFL
„1152×864“ 100 1152 1184 1408 1440 864 867 870 904

No a to je takřka vše. Těmito výpočty jsme získali všechny potřebné hodnoty do řádku Modeline, zbývá již jen patřičně upravit konfigurační soubor a restartovat XWindows. Možná si teď ťukáte na čelo a ptáte se, jaktože mám v této tabulce trochu jiné hodnoty, než jsem uvedl na začátku v příkladu. Je to proto, že jsem ještě použil program xvidtune pro doladění pozice a rozměru obrazu. Xvidtune získáte s XWindows a je to velice užitečná pomůcka. Jeho výhodou je, že s ním lze testovat různá nastavení za běhu XWindows bez nutnosti zásahu do konfiguračního souboru a následných restartů Xserveru. Nezapomeňte ale, že výsledek práce s xvidtune se nikam neukládá, stejně je nutné nechat si zobrazit Modeline a ten pak ručně zadat do konfigurace.

prace_s_linuxem_tip

Obrázek, xvidtune



Uvedené postupy mohou opravdu dávat netušené výsledky. Osobně jsem z již postarší karty S3 868 vymačkl rozlišení 1152×864 s vertikální obnovovací frekvencí 77 Hz. Pro srovnání: s originálním ovladačem přímo od S3 jsem ve Windows (když jsem je ještě měl :) dosáhl frekvence pouhých 65 Hz.
Za zmínku stojí i to, že není třeba se omezovat na zaběhaná rozlišení, lze experimentovat i s úplně jinými. Já jsem například dočasně používal rozlišení 1100×825 pixelů. Potíž těchto „zparchantělých“ rozlišení je v tom, že u nich často dochází ke zkreslení geometrie nebo barevného podání a vyladění už je otázkou mnoha pokusů.

POZOR
Nemohu si odpustit obligátní varování: nastavení špatných parametrů může vést až k nevratnému poškození monitoru (za což já nenesu žádnou odpovědnost :)! Přesto, u většiny nových digitálních monitorů se není třeba této krajnosti obávat. Monitor se raději v případě překročení limitních hodnot sám vypne než by připustil poškození.
Všechno, co zde bylo napsáno platí pro XFree86R6, ale není vyloučeno, že budou uvedené postupy fungovat i se staršími verzemi.

Tento dokument si neklade za cíl vysvětlit kompletně problematiku zobrazovacích mechanismů XWindows, ale spíše poskytnout zvídavějším začítečníkům vodítko při konfiguraci grafického subsystému.

Zdroje
XFree86 Video Timings HOWTO
Resolution X-Windows Newbie HowTo

Autor článku

'; document.getElementById('preroll-iframe').onload = function () { setupIframe(); } prerollContainer = document.getElementsByClassName('preroll-container-iframe')[0]; } function setupIframe() { prerollDocument = document.getElementById('preroll-iframe').contentWindow.document; let el = prerollDocument.createElement('style'); prerollDocument.head.appendChild(el); el.innerText = "#adContainer>div:nth-of-type(1),#adContainer>div:nth-of-type(1) > iframe { width: 99% !important;height: 99% !important;max-width: 100%;}#videoContent,body{ width:100vw;height:100vh}body{ font-family:'Helvetica Neue',Arial,sans-serif}#videoContent{ overflow:hidden;background:#000}#adMuteBtn{ width:35px;height:35px;border:0;background:0 0;display:none;position:absolute;fill:rgba(230,230,230,1);bottom:20px;right:25px}"; videoContent = prerollDocument.getElementById('contentElement'); videoContent.style.display = 'none'; videoContent.volume = 1; videoContent.muted = false; const playPromise = videoContent.play(); if (playPromise !== undefined) { playPromise.then(function () { console.log('PREROLL sound allowed'); // setUpIMA(true); videoContent.volume = 1; videoContent.muted = false; setUpIMA(); }).catch(function () { console.log('PREROLL sound forbidden'); videoContent.volume = 0; videoContent.muted = true; setUpIMA(); }); } } function setupDimensions() { prerollWidth = Math.min(iinfoPrerollPosition.offsetWidth, 480); prerollHeight = Math.min(iinfoPrerollPosition.offsetHeight, 320); } function setUpIMA() { google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true); google.ima.settings.setLocale('cs'); google.ima.settings.setNumRedirects(10); // Create the ad display container. createAdDisplayContainer(); // Create ads loader. adsLoader = new google.ima.AdsLoader(adDisplayContainer); // Listen and respond to ads loaded and error events. adsLoader.addEventListener( google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, onAdsManagerLoaded, false); adsLoader.addEventListener( google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); // An event listener to tell the SDK that our content video // is completed so the SDK can play any post-roll ads. const contentEndedListener = function () { adsLoader.contentComplete(); }; videoContent.onended = contentEndedListener; // Request video ads. const adsRequest = new google.ima.AdsRequest(); adsRequest.adTagUrl = iinfoVastUrls[iinfoVastUrlIndex]; console.log('Preroll advert: ' + iinfoVastUrls[iinfoVastUrlIndex]); videoContent.muted = false; videoContent.volume = 1; // Specify the linear and nonlinear slot sizes. This helps the SDK to // select the correct creative if multiple are returned. // adsRequest.linearAdSlotWidth = prerollWidth; // adsRequest.linearAdSlotHeight = prerollHeight; adsRequest.nonLinearAdSlotWidth = 0; adsRequest.nonLinearAdSlotHeight = 0; adsLoader.requestAds(adsRequest); } function createAdDisplayContainer() { // We assume the adContainer is the DOM id of the element that will house // the ads. prerollDocument.getElementById('videoContent').style.display = 'none'; adDisplayContainer = new google.ima.AdDisplayContainer( prerollDocument.getElementById('adContainer'), videoContent); } function unmutePrerollAdvert() { adVolume = !adVolume; if (adVolume) { adsManager.setVolume(0.3); prerollDocument.getElementById('adMuteBtn').innerHTML = ''; } else { adsManager.setVolume(0); prerollDocument.getElementById('adMuteBtn').innerHTML = ''; } } function onAdsManagerLoaded(adsManagerLoadedEvent) { // Get the ads manager. const adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; adsRenderingSettings.loadVideoTimeout = 12000; // videoContent should be set to the content video element. adsManager = adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); // Add listeners to the required events. adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, onContentResumeRequested); adsManager.addEventListener( google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); // Listen to any additional events, if necessary. adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); playAds(); } function playAds() { // Initialize the container. Must be done through a user action on mobile // devices. videoContent.load(); adDisplayContainer.initialize(); // setupDimensions(); try { // Initialize the ads manager. Ad rules playlist will start at this time. adsManager.init(1920, 1080, google.ima.ViewMode.NORMAL); // Call play to start showing the ad. Single video and overlay ads will // start at this time; the call will be ignored for ad rules. adsManager.start(); // window.addEventListener('resize', function (event) { // if (adsManager) { // setupDimensions(); // adsManager.resize(prerollWidth, prerollHeight, google.ima.ViewMode.NORMAL); // } // }); } catch (adError) { // An error may be thrown if there was a problem with the VAST response. // videoContent.play(); } } function onAdEvent(adEvent) { const ad = adEvent.getAd(); console.log('Preroll event: ' + adEvent.type); switch (adEvent.type) { case google.ima.AdEvent.Type.LOADED: if (!ad.isLinear()) { videoContent.play(); } prerollDocument.getElementById('adContainer').style.width = '100%'; prerollDocument.getElementById('adContainer').style.maxWidth = '640px'; prerollDocument.getElementById('adContainer').style.height = '360px'; break; case google.ima.AdEvent.Type.STARTED: window.addEventListener('scroll', onActiveView); if (ad.isLinear()) { intervalTimer = setInterval( function () { // Example: const remainingTime = adsManager.getRemainingTime(); // adsManager.pause(); }, 300); // every 300ms } prerollDocument.getElementById('adMuteBtn').style.display = 'block'; break; case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: if (ad.isLinear()) { clearInterval(intervalTimer); } if (prerollLastError === 303) { playYtVideo(); } break; case google.ima.AdEvent.Type.COMPLETE: if (ad.isLinear()) { clearInterval(intervalTimer); } playYtVideo(); break; } } function onAdError(adErrorEvent) { console.log(adErrorEvent.getError()); prerollLastError = adErrorEvent.getError().getErrorCode(); if (!loadNext()) { playYtVideo(); } } function loadNext() { iinfoVastUrlIndex++; if (iinfoVastUrlIndex < iinfoVastUrls.length) { iinfoPrerollPosition.remove(); playPrerollAd(); } else { return false; } adVolume = 1; return true; } function onContentPauseRequested() { videoContent.pause(); } function onContentResumeRequested() { videoContent.play(); } function onActiveView() { if (prerollContainer) { const containerOffset = prerollContainer.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight/1 && containerOffset.bottom > 0.0) { if (prerollPaused) { adsManager.resume(); prerollPaused = false; } return true; } else { if (!prerollPaused) { adsManager.pause(); prerollPaused = true; } } } return false; } function playYtVideo() { iinfoPrerollPosition.remove(); youtubeIframe.style.display = 'block'; youtubeIframe.src += '&autoplay=1&mute=1'; } }
'; document.getElementById('outstream-iframe').onload = function () { setupIframe(); } replayScreen = document.getElementById('iinfoOutstreamReplay'); iinfoOutstreamPosition = document.getElementById('iinfoOutstreamPosition'); outstreamContainer = document.getElementsByClassName('outstream-container')[0]; setupReplayScreen(); } function setupIframe() { outstreamDocument = document.getElementById('outstream-iframe').contentWindow.document; let el = outstreamDocument.createElement('style'); outstreamDocument.head.appendChild(el); el.innerText = "#adContainer>div:nth-of-type(1),#adContainer>div:nth-of-type(1) > iframe { width: 99% !important;height: 99% !important;max-width: 100%;}#videoContent,body{ width:100vw;height:100vh}body{ font-family:'Helvetica Neue',Arial,sans-serif}#videoContent{ overflow:hidden;background:#000}#adMuteBtn{ width:35px;height:35px;border:0;background:0 0;display:none;position:absolute;fill:rgba(230,230,230,1);bottom:-5px;right:25px}"; videoContent = outstreamDocument.getElementById('contentElement'); videoContent.style.display = 'none'; videoContent.volume = 1; videoContent.muted = false; if ( location.href.indexOf('rejstriky.finance.cz') !== -1 || location.href.indexOf('finance-rejstrik') !== -1 || location.href.indexOf('firmy.euro.cz') !== -1 || location.href.indexOf('euro-rejstrik') !== -1 || location.href.indexOf('/rejstrik/') !== -1 || location.href.indexOf('/rejstrik-firem/') !== -1) { outstreamDirectPlayed = true; soundAllowed = true; iinfoVastUrlIndex = 0; } if (!outstreamDirectPlayed) { console.log('OUTSTREAM direct'); setUpIMA(true); } else { if (soundAllowed) { const playPromise = videoContent.play(); if (playPromise !== undefined) { playPromise.then(function () { console.log('OUTSTREAM sound allowed'); setUpIMA(false); }).catch(function () { console.log('OUTSTREAM sound forbidden'); renderBanner(); }); } } else { renderBanner(); } } } function getWrapper() { let articleWrapper = document.querySelector('.rs-outstream-placeholder'); // Outstream Placeholder from RedSys manipulation if (articleWrapper && articleWrapper.style.display !== 'block') { articleWrapper.innerHTML = ""; articleWrapper.style.display = 'block'; } // Don't render OutStream on homepages if (articleWrapper === null) { if (document.querySelector('body.p-index')) { return null; } } if (articleWrapper === null) { articleWrapper = document.getElementById('iinfo-outstream'); } if (articleWrapper === null) { articleWrapper = document.querySelector('.layout-main__content .detail__article p:nth-of-type(6)'); } if (articleWrapper === null) { // Euro, Autobible, Zdravi articleWrapper = document.querySelector('.o-article .o-article__text p:nth-of-type(6)'); } if (articleWrapper === null) { articleWrapper = document.getElementById('sidebar'); } if (!articleWrapper) { console.error("Outstream wrapper of article was not found."); } return articleWrapper; } function setupDimensions() { outstreamWidth = Math.min(iinfoOutstreamPosition.offsetWidth, 480); outstreamHeight = Math.min(iinfoOutstreamPosition.offsetHeight, 320); } /** * Sets up IMA ad display container, ads loader, and makes an ad request. */ function setUpIMA(direct) { google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true); google.ima.settings.setLocale('cs'); google.ima.settings.setNumRedirects(10); // Create the ad display container. createAdDisplayContainer(); // Create ads loader. adsLoader = new google.ima.AdsLoader(adDisplayContainer); // Listen and respond to ads loaded and error events. adsLoader.addEventListener( google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, onAdsManagerLoaded, false); adsLoader.addEventListener( google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); // An event listener to tell the SDK that our content video // is completed so the SDK can play any post-roll ads. const contentEndedListener = function () { adsLoader.contentComplete(); }; videoContent.onended = contentEndedListener; // Request video ads. const adsRequest = new google.ima.AdsRequest(); if (direct) { adsRequest.adTagUrl = directVast; console.log('Outstream DIRECT CAMPAING advert: ' + directVast); videoContent.muted = true; videoContent.volume = 0; outstreamDirectPlayed = true; } else { adsRequest.adTagUrl = iinfoVastUrls[iinfoVastUrlIndex]; console.log('Outstream advert: ' + iinfoVastUrls[iinfoVastUrlIndex]); videoContent.muted = false; videoContent.volume = 1; } // Specify the linear and nonlinear slot sizes. This helps the SDK to // select the correct creative if multiple are returned. // adsRequest.linearAdSlotWidth = outstreamWidth; // adsRequest.linearAdSlotHeight = outstreamHeight; adsRequest.nonLinearAdSlotWidth = 0; adsRequest.nonLinearAdSlotHeight = 0; adsLoader.requestAds(adsRequest); } function setupReplayScreen() { replayScreen.addEventListener('click', function () { iinfoOutstreamPosition.remove(); iinfoVastUrlIndex = 0; outstreamInit(); }); } /** * Sets the 'adContainer' div as the IMA ad display container. */ function createAdDisplayContainer() { // We assume the adContainer is the DOM id of the element that will house // the ads. outstreamDocument.getElementById('videoContent').style.display = 'none'; adDisplayContainer = new google.ima.AdDisplayContainer( outstreamDocument.getElementById('adContainer'), videoContent); } function unmuteAdvert() { adVolume = !adVolume; if (adVolume) { adsManager.setVolume(0.3); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } else { adsManager.setVolume(0); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } } /** * Loads the video content and initializes IMA ad playback. */ function playAds() { // Initialize the container. Must be done through a user action on mobile // devices. videoContent.load(); adDisplayContainer.initialize(); // setupDimensions(); try { // Initialize the ads manager. Ad rules playlist will start at this time. adsManager.init(1920, 1080, google.ima.ViewMode.NORMAL); // Call play to start showing the ad. Single video and overlay ads will // start at this time; the call will be ignored for ad rules. adsManager.start(); // window.addEventListener('resize', function (event) { // if (adsManager) { // setupDimensions(); // adsManager.resize(outstreamWidth, outstreamHeight, google.ima.ViewMode.NORMAL); // } // }); } catch (adError) { // An error may be thrown if there was a problem with the VAST response. // videoContent.play(); } } /** * Handles the ad manager loading and sets ad event listeners. * @param { !google.ima.AdsManagerLoadedEvent } adsManagerLoadedEvent */ function onAdsManagerLoaded(adsManagerLoadedEvent) { // Get the ads manager. const adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; adsRenderingSettings.loadVideoTimeout = 12000; // videoContent should be set to the content video element. adsManager = adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); // Add listeners to the required events. adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, onContentResumeRequested); adsManager.addEventListener( google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); // Listen to any additional events, if necessary. adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); playAds(); } /** * Handles actions taken in response to ad events. * @param { !google.ima.AdEvent } adEvent */ function onAdEvent(adEvent) { // Retrieve the ad from the event. Some events (for example, // ALL_ADS_COMPLETED) don't have ad object associated. const ad = adEvent.getAd(); console.log('Outstream event: ' + adEvent.type); switch (adEvent.type) { case google.ima.AdEvent.Type.LOADED: // This is the first event sent for an ad - it is possible to // determine whether the ad is a video ad or an overlay. if (!ad.isLinear()) { // Position AdDisplayContainer correctly for overlay. // Use ad.width and ad.height. videoContent.play(); } outstreamDocument.getElementById('adContainer').style.width = '100%'; outstreamDocument.getElementById('adContainer').style.maxWidth = '640px'; outstreamDocument.getElementById('adContainer').style.height = '360px'; break; case google.ima.AdEvent.Type.STARTED: window.addEventListener('scroll', onActiveView); // This event indicates the ad has started - the video player // can adjust the UI, for example display a pause button and // remaining time. if (ad.isLinear()) { // For a linear ad, a timer can be started to poll for // the remaining time. intervalTimer = setInterval( function () { // Example: const remainingTime = adsManager.getRemainingTime(); // adsManager.pause(); }, 300); // every 300ms } outstreamDocument.getElementById('adMuteBtn').style.display = 'block'; break; case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: if (ad.isLinear()) { clearInterval(intervalTimer); } if (outstreamLastError === 303) { if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } } break; case google.ima.AdEvent.Type.COMPLETE: // This event indicates the ad has finished - the video player // can perform appropriate UI actions, such as removing the timer for // remaining time detection. if (ad.isLinear()) { clearInterval(intervalTimer); } if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } break; } } /** * Handles ad errors. * @param { !google.ima.AdErrorEvent } adErrorEvent */ function onAdError(adErrorEvent) { // Handle the error logging. console.log(adErrorEvent.getError()); outstreamLastError = adErrorEvent.getError().getErrorCode(); if (!loadNext()) { renderBanner(); } } function renderBanner() { if (isBanner) { console.log('Outstream: Render Banner'); iinfoOutstreamPosition.innerHTML = ""; iinfoOutstreamPosition.style.height = "330px"; iinfoOutstreamPosition.appendChild(bannerDiv); } else { console.log('Outstream: Banner is not set'); } } function loadNext() { iinfoVastUrlIndex++; if (iinfoVastUrlIndex < iinfoVastUrls.length) { iinfoOutstreamPosition.remove(); outstreamInit(); } else { return false; } adVolume = 1; return true; } /** * Pauses video content and sets up ad UI. */ function onContentPauseRequested() { videoContent.pause(); // This function is where you should setup UI for showing ads (for example, // display ad timer countdown, disable seeking and more.) // setupUIForAds(); } /** * Resumes video content and removes ad UI. */ function onContentResumeRequested() { videoContent.play(); // This function is where you should ensure that your UI is ready // to play content. It is the responsibility of the Publisher to // implement this function when necessary. // setupUIForContent(); } function onActiveView() { if (outstreamContainer) { const containerOffset = outstreamContainer.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight/1 && containerOffset.bottom > 0.0) { if (outstreamPaused) { adsManager.resume(); outstreamPaused = false; } return true; } else { if (!outstreamPaused) { adsManager.pause(); outstreamPaused = true; } } } return false; } let outstreamInitInterval; if (typeof cpexPackage !== "undefined") { outstreamInitInterval = setInterval(tryToInitializeOutstream, 100); } else { const wrapper = getWrapper(); if (wrapper) { let outstreamInitialized = false; window.addEventListener('scroll', () => { if (!outstreamInitialized) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { outstreamInit(); outstreamInitialized = true; } } }); } } function tryToInitializeOutstream() { const wrapper = getWrapper(); if (wrapper) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { if (cpexPackage.adserver.displayed) { clearInterval(outstreamInitInterval); outstreamInit(); } } } else { clearInterval(outstreamInitInterval); } } }
OSZAR »