Kdy skončí éra Linuxu?

16. 5. 2025
Doba čtení: 7 minut

Sdílet

Linus v rozhovoru o 20 letech Gitu
Nihil perpetuum, pauca diuturna sunt. Nic netrvá věčně. Ani tento vesmír, ani jeden život, ani jedna říše. Přesvědčily se o tom mnohé národy. A také mnohé softwarové projekty. Kolik života má před sebou ten největší, jaký lidstvo stvořilo?

Rovnou se ti laskavý čtenáři úvodem omlouvám. Došly mi v posledních dnech síly a nedokážu se vrtat v jaderných novinkách či procesorových zranitelnostech, a tak to dnes bude spíš takové filozofování, možná až amatérská pocitologie.

Potřebuji si totiž utřídit myšlenky, které mně zalezly do hlavy zhruba před 15 lety, když jsem přecházel z Windows na Linux a které mi na povrch opět vytáhl YouTube, který mi předhodil jeden jakýsi naprosto nesouvisející Short, nicméně obsahují snímek knížky s velmi významným grafem. Bohužel jsem si vyřízl jen ten snímek, nikoli i název Shortu (takže ani nevím, z jaké knížky je), ale to není podstatné. Jde o obsah.

Historické éry zemí

Historické éry zemí

Autor: Youtube

Jsou éry, které přijdou, zažijí svůj vrchol a pak je čeká nevyhnutelně úpadek. Nechci zde lézt do zelí profesoru Bártovi a jeho Sedmi zákonům, spíše připomenout tu skutečnost, že věci se v tomto vesmíru mají tendenci řídit stejnými zákonitostmi. Říkejme jim třeba všeobecný princip. A tak, jako svůj vrchol zažili Holanďané a po nich Britové následovaní Američany, stoupají nyní (znovu?) na svůj vrchol Číňané.

Období slávy

Stejně tak měly své minuty na výsluní slávy různí sportovci či filmoví režiséři, měly je i programovací jazyky či operační systémy. Mnoho z nás jistě vzpomene na éru, kdy to vypadalo, že PHP je konec, máme tu Ruby on Rails. Nebo když odbočím, tak jedna generace nedala dopustit na Sennu, další na Schumachera, ta pozdější na Hamiltona a dnes třeba na Verstappena. S operačními systémy tomu nebylo jinak (dnes mám na mysli systémy používané námi všemi „běžnými plebejci“).

Možná byla doba, kdy majorita používala CP/M. Možná byla doba MS-DOSu a potom Windows. Možná všechny tyto doby měly své souběžně existující alternativy (od všeho osmibitového, přes Amiga OS, Atari TOS či cokoli dalšího). Ale vždy tu byl jeden, který používala většina, pro kterou probíhala většina vývoje aplikací, jakkoli aplikace vždy vznikaly i pro další platformy (ano, tím mám na mysli hlavně macOS).

Se skrývaným obdivem těch 15 let sleduji, jak se Microsoftu daří odsouvat ústup Windows a platformy Wintel ze slávy za současného diverzifikování rizik vstupem na nové trhy. Ale dovoluji si tvrdit, že Windows už ve fázi ústupu ze slávy jsou, a jsou v této fázi už minimálně 10 let. Protože v roce 2015 jsme mohli s naprostou jistotou říci, že lidé mají buď iPhone, nebo Android, neboť pokus jménem Windows Phone, odsouzený od začátku k neúspěchu, neuspěl. A Microsoft čerstvě vydaná Windows 10 lidem vnucoval silou a často formou upgradu z verzí 7 a 8 zadarmo. 

Navíc všechny ty „alternativní“ systémy se prokutávaly do dalších oblastí, všech televizorů, chytrých krabiček, automobilů (i kdyby jen zčásti) či na Mars.

Konkurence Linuxu

Stejně tak ale roste konkurence i „Linuxu“. Nejpikantnější na tom je právě tak skutečnost, že zde velmi záleží na tom, co si pod pojmem „Linux“ představíme. Samozřejmě i nadále platí, že linuxové jádro je srdcem řady dalších projektů, nicméně málo z běžných lidí ví, že Android je svého druhu „linuxová distribuce“, oni tento pojem obvykle ani nikdy neslyšeli. Stejné platí pro webOS, který LG dává do svých televizorů či ledniček a před 10 lety s ním nabídla i hodinky.

Vedle těchto variací, které lze považovat za součást „Linuxu“ ale žijí i projekty, které jsou sice třeba plně či částečně open-source, případně umí pracovat s Linuxem, ale jinak si jdou od základu jinou cestou. Třeba HarmonyOS od Huawei a Fuchsia od Google. Historicky víme o projektech, které nestavějí na Linuxu právě proto, aby nemusely sdílet dovyvinuté věci zpět pod licencí GNU GPLv2, třeba OrbisOS, systém založený na FreeBSD a pohánějící Playstationy. Sony se před lety rozhodla zařídit využitím projektu pod benevolentnější BSD licencí, Google či Huawei zcela samostatně.

Pak už je vše jen otázka tržní síly dané značky, zdali se systém prosadí. Huawei sice v posledních letech moc na západ neprodává, ostatně přístup k čipům či službám a technologií ze západu má stále omezen, nicméně sama Čína je 1,5miliardový trh a s dalšími zeměmi řekněme, že na vývoj vlastního Harmony OS, který by poháněl jak telefony a TV, tak třeba chytré žárovky, vysavače, ledničky či auta, si „dokáže sám vydělat“, i kdyby to nebylo dnes, ale třeba až za 10 let.

Podobně Google je natolik bohatý, že dokáže protlačit jakoukoli inovaci, kterou bude chtít, ať už hrubou silou, nebo prostě tím, že jí nasadí či nenasadí ve svých produktech. Příklad za všechny: JPEG XL a Chrome a obecně cokoli kolem webových standardů, co se mu hodí do krámu.

Pokud by jednoho dne Google změnil Android z jádra Linux na Fuchsii a zařídil to tak, aby to uživatel nepoznal, bude to téměř všem uživatelům naprosto lhostejné. Dnešní uživatel se identifikuje s platformou či produktem a je mu jedno, na čem to celé běží.

Většina lidí tohle neřeší

Měli bychom se smířit s tím, že drtivá většina lidí, kteří každodenně používají Linux, to často ani nevědí a je jim to úplně fuk. Ostatně mnozí z nás také rozlišují kávu / víno na „chutná mi“ × „nechutná mi“ a jemné nuance pánů sommelierů nás nechávají chladnými. 

Stejně tak Sony může mít svůj trh s Playstationy díky tomu, že nabízí silou svých možností cestu, jak hráči mohou hrát hry, na které by před 25 lety potřebovali PC. A to, zdali hráč zvolí Xbox, nebo Playstation, je spíše otázkou nabízených služeb za dané ceny, případně vášní pro nějaký ten exkluzivní titul. Na jakém OS to celé běží, je prakticky každému jednotlivému uživateli naprosto ukradené.

Linux nestihl na výsluní vystoupat. Jeho stoupání totiž stálo na korporacích, které přes něj předtím hodily závoj s nápisy jako „Android“ či „webOS“ apod. a jsem přesvědčen, že kdyby nemuseli, tak by výrobci chytrých telefonů neměli ve svých produktech uloženy všechny licence ke všem open-source projektům, na kterých jejich telefonů staví.

To je holt ona stinná stránka otevřených projektů: korporace je využijí na maximum možného a ano, samy do nich často velmi výrazně investují své prostředky, protože to pro ně je výhodnější, než dělat celý vývoj in-house, jak ostatně ukazuje Apple či již zmíněný Google s každoročním Summer of Code.

Stárnoucí nadšení otců zakladatelů

Velkou část běžící mašinérie jménem Linux táhnou tisíce a tisíce vývojářů ve stovkách a stovkách společností a korporací. Tomu všemu „vládne kasta“ správců a otců zakladatelů, kteří mě nepřestávají udivovat tím, jak konzistentní dokáží být po desítky let ve svém přístupu, v jeho hlavních cestách. Tato konzistence je to, co drží projekt pohromadě, zachovává status quo, jistotu, že zítra bude Linux stejný jako dnes či včera. Ale jak dlouho toto může trvat?

Těžko říci. Zalovme do hlubin YouTube, neb si ukážeme tři videa v řadě. To první je slavné, Nvidií milované povídání na Aalto University z roku 2012, kdy Linusovi bylo 42 let.

Zdroj: Youtube.com

Další v řadě je jedno z relativně nedávných, povídání s tehdejším šéfem Intelu Patem Gelsingerem z roku 2022, to bylo Linusovi 52 let.

Zdroj: Youtube.com

Nakonec tu máme nedávný rozhovor u příležitosti 20 let Gitu, kdy Linusovi bylo 55 let.

Zdroj: Youtube.com

Nemohu si pomoci, ale přijde mi, že za poslední dva roky Linus hodně zestárl. Z nemalé části to bude jistě tím, že zhubl, přesto působí dojmem buď nemocného, nebo výrazně staršího. Všechna čest, mozek zjevně běží stále na 110 %, zde vše v pořádku, nicméně je jasné, že jednoho dne buď z projektu Linux odejde, nebo bude muset odejít. Vynechme polemiku, že odcházet se dá se ctí, nebo naopak stylem Joea Bidena, tato doba je, věřím, velmi velmi daleko.

Zakladatelé v důchodu

Linus Torvalds letos oslaví 56. narozeniny. Greg Kroah-Hartman je o rok starší. To samé Ted Tso a další. V podstatě většina vývojářů a správců jádra jsou dnes buď už řadu let padesátníky, nebo rovnou šedesátníky a platí to i pro lidi spojené se souvisejícími projekty. I Miguel de Icaza je za padesátkou a třeba Hans Reiser oslavil předloni ve vězení šedesátku.

Jinými slovy: do deseti let budou všichni ti letití současní správci Linuxu v důchodovém věku, popřípadě jim bude přes 70. Z logiky věci tak během příštích 10 let bude ve větší míře probíhat generační obměna a je otázkou, zdali noví správci budou tak nastavené osobnosti, aby zachovaly status quo a podobně vhodně vyvažovaly chod celého projektu, jako to dělají lidé typu Linuse, Grega či kohokoli dalšího, kdy by si zasloužil být na tomto místě jmenován.

Ona sama myšlenka otevřenosti projektu je úctyhodná, ale potřebuje každodenní péči. Linuxu by se jinak jednoho dne mohlo stát, že jej někdo jiný kompletně vytěží, zbytek si dořeší in-house (případně s AI) a Linux bude potupně nahrazen něčím jinými. Linux klidně může dál existovat, ale bude méně relevantní, pokud se nepřizpůsobí změně světa.

hacking_tip

Lidstvo půjde samozřejmě dál, jak praví i už zmiňovaný profesor Bárta. Ale nestane se nám, že jednoho dne budeme na klíně chovat vnoučata a vyprávět jim o světě, kterému udával tón jeden otevřený softwarový projekt? Projekt, který měl dobře nakročeno, ale nikdy se mu nepodařilo prolomit klíčovou hranici, vyřešit bug #1 systému Ubuntu?

Zkrátka a dobře, laskavý čtenáři, dokážeš si představit svět po Linuxu? Já ještě ne. Ale už mám pocit, že někde za obzorem se chystají vysvitnout první paprsky nové éry. Jestli součástí té éry bude Linux, si 100% jistý nejsem.

Autor článku

Příznivec open-source rád píšící i o ne-IT tématech. Odpůrce softwarových patentů a omezování občanských svobod ve prospěch korporací.

'; 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 »