Vše, co potřebujete vědět o internetové televizi (operátoři, funkce, HD kvalita)

22. 2. 2021

Sdílet

 Autor: Pixabay
Internetová televize je jedním z druhů příjmu televizního vysílání. Proti pozemnímu vysílání přes DVB-T2 jde o placenou službu, která ale přináší řadu výhod – více kanálů, vyšší kvalitu obrazu a ve srovnání s kabelovkou či satelitem navíc možnost pustit si pořady i zpětně. A navzdory názvu se na takovou televizi nemusíte dívat na počítači, ale normálně na velkém televizoru v obýváku.

Internetovou televizi aktuálně (únor 2021) nabízí jedenáct operátorů (pokud počítám ty celoplošné) v různých tarifech a s doplňkovými balíčky. Ceny začínají na stovce za základní nabídku po 1200 Kč měsíčně. Proč si vůbec něco takového pořídit?

  • Získáte stanice jinak dostupné jen na satelitu či kabelovce.
  • Pořady můžete přetáčet zpět, pouštět si několik dní staré vysílání, a přeskakovat reklamy, když se díváte „ze záznamu“.
  • Kvalita obrazu je obecně vyšší.
  • Televizi můžete sledovat současně na velkém televizoru, na mobilu i na počítači.

Hlavní výhodou je zpětné zhlédnutí – přestanete být závislí na lineárním vysílání a z nabídky pořadů si pustíte, co chcete a kdy chcete (a kde chcete). Speciální set-top-box nabízejí všichni operátoři, ale je nutností jen pro starší televizory bez chytrých funkcí. Většinou si již vystačíte s nainstalováním aplikace, což vyjde proti fyzické krabičce levněji.

IPTV vs. satelit vs. kabelovka vs. DVB-T2

Vedle pozemního neboli terestrického vysílání, které je většinou zadarmo, můžete ještě vybírat mezi kabelovým, satelitním a právě internetovým vysíláním. Jen už ale nestačí tuner a místo antény potřebujete připojení k internetu. Koncesionářské poplatky nejsou závislé na typu vysílání a platíte je stejné u všech typů vysílání.

Pozemní signál DVB-T2 zajišťují prostřednictvím čtyř celoplošných multiplexů tři společnosti: Česká televize, České radiokomunikace a Digital Broadcasting. Terestrické vysílání je zdarma a naladit můžete aktuálně 38 stanic. V dobré kvalitě obrazu ale jen stanice České televize a s přimhouřením oka ještě TV Seznam, Barrandov a Primu. V DVB-T2 bohužel nikdo nevysílá dvě zvukové stopy a jedinou funkcí navíc je HbbTV – červené tlačítko.

Kabelová televize je na ústupu a ve větším měřítku ji nabízí pouze Vodafone v rámci sítě bývalého UPC. I ten ale preferuje internetovou televizi. Klasickou kabelovku si z Vodafone TV uděláte tak, že k televizi nemáte internet. Nabídka tarifů je totožná, ve výsledku je to o něco levnější, ale bez funkcí navíc.

Satelitní vysílání je svou nabídkou podobné tomu internetovému. V Česku jej provozují Skylink a Telly (bývalé Digi TV), menším hráčem je pak ještě T-Mobile. Platíte měsíční poplatek a řada stanic je k dispozici i nekódovaně (zdarma mimo tarif). Výhodou je nezávislost na adrese – nemusíte mít rychlý internet ani bydlet v dosahu vysílače, stačí jen namířit parabolu správným směrem. Všichni výše zmínění operátoři satelitní televize nabízejí i internetovou televizi. Udávaná kvalita obrazu je u nich alespoň podle rozlišení lepší u internetové televize. Třeba Telly na satelitu vysílá prokládaně (50i) a Skylink to ani neuvádí.

Internetové vysílání je z hlediska funkcí asi nejlepší možnou volbou příjmu televizního signálu. Potřebujete samozřejmě internetové připojení, ale nemusí být kdovíjak rychlé. Televize si z vaší rychlosti ukousne jen jednotky Mb/s a důležitá je spíše latence. Místo Wi-Fi je vhodnější pevné připojení, ale to platí vždy.

Výhody internetové televize jsou hlavně ve funkcích. Kdykoliv během sledování pořadu dáte pauzu a můžete si odskočit. Lze se vracet zpět a pouštět si pořady staré několik dní (většinou týden). Můžete si označit pořady pro nahrání a budete je mít k dispozici ke zhlédnutí až měsíc. Vedle televizoru se lze současně dívat na jinou stanici na počítači nebo mobilu.

HD kvalita vysílání

Nabídka HD stanic stále roste, ale velká část je jich samozřejmě stále v SD. Zejména komerční stanice pak v DVB-T2 vysílají v mizerném rozlišení, ale na placených službách nabídnou Full HD.

Byť všichni uvádějí pouze zkratky HD a SD, tak HD v mluvě operátorů znamená cokoliv od 1280 × 720 po 1920 × 1080 px. A technicky vzato i SD vysílání v rozlišení 960 × 540 px už není SD, ale qHD, byť se tato zkratka příliš nepoužívá.

Většinou se pod HD už skrývá Full HD, někdy v 50 snímcích (časté pro sportovní či filmové stanice), jindy v 25 snímcích.

Operátoři

Vybírat můžete aktuálně mezi jedenácti celoplošnými operátory.

Celkem využívá internetovou televizi v Česku již téměř 800 tisíc uživatelů. Je přitom jedno, od jaké firmy máte internetové připojení, ale někdy můžete dostat slevu, pokud máte od stejné firmy více služeb.

Největším operátorem je O2 TV, které se chlubí půl milionem zákazníků. Nedávno naopak zanikla služba Zapni.TV a její doménu si chytře koupilo Kuki. U Eri TV můžete v případě pořízení internetu od stejného operátora využít technologii IPTV (viz Technické okénko níže). Vodafone TV pak nabízí i Netflix. A operátor se zajímavým jménem Číčo dokonce základní internetovou televizi zadarmo.

Jednotlivé nabídky operátorů se liší:

  • nabídkou televizních stanic a cenou tarifů
  • kvalitou obrazu jednotlivých stanic (rozlišení, datový tok)
  • nabídkou aplikací pro Smart TV
  • uživatelským rozhraním
  • délkou zpětného zhlédnutí a prostoru pro nahrávky
  • počtem zařízení, na kterých je možné vysílání současně sledovat

V základním tarifu získáte většinou stanice běžně dostupné i v pozemním vysílání, ale zato v rozlišení HD a s možností přetáčení. Oproti tomu v nejdražším tarifu můžete dnes mít okolo 140 stanic z celého světa.

Brněnská centrála SledováníTV:

photo_2018-01-18_15-57-28 Vývojáři mají k dispozici různé přehrávače i televize, na kterých aplikace testují

Zpětné zhlédnutí

Délka zpětného zhlédnutí je standardně týden. To znamená, že si v daný okamžik můžete pustit nejvýše týden staré pořady. Někteří operátoři nabízejí až měsíc, často se ale tato doba výrazněji liší i podle televizních stanic. Také v levném tarifu může být kratší.

Tuto dobu lze prodloužit funkcí nahrávek, kdy nejde o nahrávání na pevný disk připojený k televizoru, ale o vytvoření virtuální nahrávky na straně operátora. Stisknutím tlačítka pro nahrání se vám daný pořad zpřístupní na delší dobu, zpravidla na měsíc.

docker + kubernetes školení s dotací tip

Můžu si televizní stanice sám vybrat?

Jen velmi omezeně. Nemůžete chtít pouze vaše oblíbené stanice, ale je třeba se řídit nabídkou tarifů. Operátoři jsou v tom ale spíše nevinně, nabídku ovlivňují programeři, respektive majitelé práv – stanice totiž bývají už pro ně dostupné v balíčcích a zjednodušeně řečeno si k zajímavější televizní stanici prostě musíte vzít i ty méně zajímavé.

Nabídku si můžete upravit zvolením tematických balíčků, ale na jejich podobu opět nemáte vliv. Přesto někteří operátoři jistou „customizaci“ nabízejí. Nejznámější je v tomto asi Kuki, které umožňuje vlastní výběr určitého počtu kanálů. Telly má pak netradiční tarif obsahující pouze sportovní kanály a stanice ČT.

Technické okénko: Jak to funguje

Ne každý operátor si svou platformu vyvíjí sám. O2 TV či T-Mobile TV používají známé technologické pozadí Nangu TV, naopak třeba Sledování TV či Lepší TV mají vlastní platformu a ještě ji nabízejí ostatním.

Operátor přebírá televizní vysílání v reálném čase a převádí jej pro internetový přenos. Zpoždění proti pozemnímu vysílání tak může být klidně dvě minuty. Vysílání zároveň ukládá někde v datacentru, aby kdykoliv fungovala služba zpětné zhlédnutí. A mezi těmito zdroji plynule přepíná podle toho, jestli se díváte na živé vysílání, nebo na (byť pár sekund starý) záznam. Video bývá kódované většinou v kodeku H.264 či H.265 s datovým tokem zhruba mezi 1 až 7 Mb/s.

Jak dostat obraz do televizoru

Každý operátor internetové televize nabízí k zakoupení či pronájmu svůj set-top-box, přičemž někdy jde o zařízení se systémem Android TV, které můžete využít i jinak a nainstalovat si další aplikace. Operátoři nabízejí i přímo aplikace pro Smart TV, set-top-box byste pak nepotřebovali, ale ne všichni podporují všechny značky. Největší šanci máte s Android TV či Tizenem (Samsung), u operátorů pak nejvíce systémů podporuje Sledování TV a Lepší TV.

IPTV vs. OTT

Internetovou televizi můžete přijímat přes dvě různé technologie. Běžně se využívá OTT (Over The Top), kdy nedochází k žádnému řízení kvality služby a pokud budete vytěžovat linku třeba stahováním, může se televizní vysílání začít „sekat“. V praxi se mi to ještě nepovedlo nasimulovat, ale taková je teorie.

Od operátora můžete mít i nabídku na IPTV, které se od OTT kvalitativně liší. V tomto případě je část internetového připojení pevně vyhrazené pro televizní vysílání. Síť ví, že přes ni jdou televizní data a uzpůsobuje tomu provoz. IPTV teoreticky slibuje vyšší a stálejší kvalitu. Je ale závislé na tom, že operátor musí vědět, které pakety se týkají televize. Typicky je tedy pro IPTV potřeba mít internet i službu internetové televize od jednoho poskytovatele. A ten navíc musí službu IPTV nabízet. Jako IPTV bývá někdy nesprávně označováno i OTT, neboť jde o líbivější a známější zkratku.

Autor článku

Redaktor portálu Cnews.cz. Zaměřuje se na televizní témata, technologické zpravodajství, mobilní operátory a vědu. Příznivec kompaktních smartphonů. Profil autora →

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