Od megabitů k terabitu: Ethernet slaví padesátiny a zrychlil stotisíckrát

22. 5. 2023
Doba čtení: 7 minut

Sdílet

Autor: Depositphotos
Přesně před padesáti lety byl načrtnut první koncept Ethernetu. Z tlustého koaxiálního kabelu se vyvinul v nejběžnější způsob propojení sítí všech velikostí prostřednictvím kroucených dvojlinek a optických vláken.

Podle svých vlastních vzpomínek přišel s prvním návrhem Ethernetu Bob Metcalfe 22. května 1973. Název odkazuje k éteru, substanci, o které se vědci domnívali, že prostupuje celým vesmírem a umožňuje šíření elektromagnetických vln. Pro počítačové sítě tehdejší doby byl takovým éterem právě koaxiální kabel, návrh sítě ale měl být univerzální a počítat i s šířením jinými médii. Na historickém náčrtku autor tak trochu předpověděl i existenci technologií jako DSL nebo Wi-Fi.

Co se dozvíte v článku
  1. Všichni připojeni k jednomu kabelu
  2. Přechod na telefonní kabely a hvězdicovou topologii
  3. Desetinásobné zrychlení
  4. Příchod mostů a přepínačů
  5. Gigabit používá všechny dráty
  6. Rychleji a dále s optickými vlákny
  7. Nový Ethernet může být i pomalejší
  8. Budoucnost patří terabitu
Původní návrh sítí používajících éter

Původní návrh sítí používajících éter

Autor: Bob Metcalfe

Pro některé je tím správným okamžikem 11. listopadu 1973, kdy první prototyp ethernetové sítě skutečně začal fungovat. Úplně první verze pracovala s tehdy bez ironie závratnou rychlostí 2,94 megabitů za sekundu, ta byla už v roce 1980 zvýšena na ještě vyšší rychlost 10 megabitů za sekundu.

Všichni připojeni k jednomu kabelu

Základní myšlenka Ethernetu spočívá v připojení všech počítačů do jednoho sdíleného média, kterým byl v prvních verzích koaxiální kabel. U něj bylo důležité, aby se nijak nevětvil, ale procházel postupně jeden počítač po druhém. Na obou jeho koncích je zakončený rezistory, které brání odrazům a vytvoření stojatého vlnění, které by komunikaci rušilo.

Původní návrh Ethernetu ze sedmdesátých let 20. století.

Původní návrh Ethernetu ze sedmdesátých let 20. století.

Autor: Alan Freir

Nejstarší ze standardů 10Base5, přezdívaný také tlustý Ethernet, používal externí transceiver, tedy kombinací vysílače a přijímače, který se propíchnutím napojil na probíhající koaxiální kabel. Tento transceiver byl pak připojen 15žilovým kabelem k rozhraní AUI síťové karty.

Vybavení pro Ethernet standardu 10Base5

Vybavení pro Ethernet standardu 10Base5

Autor: Robert Harker, podle licence: CC BY-SA 2.0

O něco modernější standard 10Base2 používal tenčí koaxiální kabel, který se prostřednictvím odboček typu T připojoval přímo ke každému počítači. To s sebou neslo značnou míru nespolehlivosti, neboť k připojení další stanice bylo vždy potřeba kabel přestřihnout a osadit konektory. Každé selhání konektoru nebo rozpojení kabelu z jiného důvodu vedlo k nefunkčnosti celého segmentu: kvůli absenci řádného zakončení nebyly funkční ani jednotlivé rozpojené ostrovy.

Přechod na telefonní kabely a hvězdicovou topologii

Právě kvůli nízké spolehlivosti dlouhé sběrnice vinoucí se od jednoho počítače k druhému se na začátku devadesátých let začíná prosazovat úplně jiný Ethernet, takový který používá hvězdicovou topologii s aktivním prvkem uprostřed – koncentrátorem neboli hubem.

Mnohem větší spotřeba kabelů je v tomto případě kompenzována použitím výrazně levnějších původně telefonních kabelů s kroucenými páry. Počítalo se s využitím stávající telefonní infrastruktury, která také používá hvězdicovou topologii. Dodnes je to patrné ze způsobu, jakým jsou jednotlivé páry podle standardu TIA/EIA-568B zapojeny do modulárního konektoru i tím, že původní Ethernet používá pouze dva páry a je tak možné jej kombinovat s telefonním vedením (které tradičně používá prostřední dva kontakty) v jednom kabelu.

ANSI/TIA-568 T568B termination
Pin Pair Wire[d] Color
1 2 tip Pair 2 Wire 1 white/orange
2 2 ring Pair 2 Wire 2 orange
3 3 tip Pair 3 Wire 1 white/green
4 1 ring Pair 1 Wire 2 blue
5 1 tip Pair 1 Wire 1 white/blue
6 3 ring Pair 3 Wire 2 green
7 4 tip Pair 4 Wire 1 white/brown
8 4 ring Pair 4 Wire 2 brown

Koncentrátor byl původně jen celkem jednoduché elektronické zařízení, které přijatý signál zesílilo a odeslalo na všechny ostatní porty, čímž vlastně simulovalo sdílené médium. Na rozdíl od předchozích verzí Ethernetu selhání jedné linky ovlivnilo jen funkčnost zařízení připojeného k dané lince, na druhou stranu tato topologie dala vzniknout potenciálu vytvoření smyček, které dokáží dodnes potrápit.

Desetinásobné zrychlení

Už v roce 1995 byl představen standard Fast Ethernet, který na tehdejší dobu už tak vysokou rychlost 10 megabitů za sekundu zvýšil, a to rovnou na desetinásobek. Koncepčně přitom fungoval shodně se stávajícím Ethernetem, jen kladl větší nároky na kvalitu kabelů a aktivních prvků. Vyšší rychlost také zpřísnila omezení maximálního počtu koncentrátorů v cestě.

Zavádění Fast Ethernetu bylo původně složité rozhodnutí. Vyžadovalo totiž vyměnit jak koncentrátor, tak všechny síťové karty k němu připojené. Existovaly sice i dvourychlostní koncentrátory, které umožňovaly připojit do jednoho jak pomalé tak rychlé síťové karty, avšak různě rychlá zařízení spolu nemohla komunikovat. Jednoduchá elektronika koncentrátoru neobsahovala žádnou paměť, která by umožnila rychlé signály zpomalovat nebo naopak pomalé signály zrychlovat.

Příchod mostů a přepínačů

Zařízení, které zrychlování a zpomalování umožňuje se nazývá síťový most (angl. bridge). V nejjednodušší variantě uloží přijatý ethernetový rámec do paměti a následně odvysílá do druhého portu. Provádí přitom optimalizaci, že na druhou stranu neposílá rámce, jejichž cílová adresa tam prokazatelně neleží. To je velmi důležité například pro spojení síťových segmentů různých rychlostí, jinak by pomalejší strana byla neustále zahlcena nerelevantním provozem přeposílaným z rychlejší strany.

Skutečný nástup Fast Ethernetu tak přišel až s dostupností přepínačů, což jsou víceportové mosty, které postupně nahradily obyčejné koncentrátory. Díky vnitřní paměti u každého portu dokážou přepínače nejen efektivně propojit zařízení různých rychlostí, ale dokonce i filtrovat neužitečný provoz a paralelizovat vzájemně nezávislé komunikace nebo nabídnout plně duplexní spojení, tedy možnost přijímat data i v okamžiku, kdy jsou jiná data odesílána.

Tím se z Ethernetu postupně vytrácí onen éter; v přepínané síti jednoduše žádné společné prostředí umožňující šíření počítačových dat není. Do všech směrů přepínače šíří pouze rámce adresované všem, dosud neznámým adresám, nebo určité skupině – tento provoz se označuje jako BUM a je obvykle vnímán jako problematický právě proto, že je šířen v celé síti.

Gigabit používá všechny dráty

Zatímco na přelomu tisíciletí běžné počítače málokdy zvládly vytížit linku o rychlosti 100 megabitů za sekundu, Ethernet už se zase vydal dál a představil opět desetinásobné zrychlení. Gigabitový Ethernet podle standardu 1000Base-T takové zrychlení přitom dosáhl bez nutnosti zvýšení nároků na kvalitu kabelů. To bylo velice důležité, protože kabelů kategorie 5e bylo v té době všude instalováno velké množství. Je velice pravděpodobné, že nejbližší kabel, který kolem sebe najdete, bude právě kategorie 5e.

Zatímco Fast Ethernet používal jen jeden kroucený pár pro vysílání a jeden pro příjem, gigabitový Ethernet osadil všechny čtyři páry telekomunikačními vidlicemi a použil je tedy k obousměrnému přenosu. Jenom tím vzrostla rychlost na 400 megabitů za sekundu. Dalším krokem bylo odstranění kódování 4b5b, čímž se zvýšila rychlost o 25 procent, tedy na 500 megabitů za sekundu. Použití vícestavového linkového kódu kódujícího dva bity do jedné změny stavu pak způsobilo konečné zdvojnásobení na 1000 megabitů za sekundu.

Je zřejmé, že transceivery pro Gigabit Ethernet jsou mnohem složitější než ty pro Fast Ethernet (a ty jsou zase složitější než ty pro klasický Ethernet). Díky masivnímu rozvoji elektroniky se však už na konci nultých let stal Gigabitový Ethernet komoditou.

Rychleji a dále s optickými vlákny

Vedle metalických kabelů Ethernet také podporuje nejrůznější optická vlákna. Nejčastěji se používají v páteřních sítích, kde limit délky metalického kabelu nestačí. Využití najdou ale i na krátké spoje v datacentrech pro rychlosti vyšší nebo rovné 10 gigabitům. Podle typu použití existuje velmi mnoho různých řešení Ethernetu po optických vláknech lišící se například vlnovou délkou, počtem vláken a jejich druhem (jednovidová nebo vícevidová), maximální podporovanou vzdáleností či rozhraním optického modulu.

Zvláštní kategorií jsou pasivní optické přístupové sítě, kdy několik účastníků sdílí jedno vlákno, které se po cestě rozděluje pomocí optických rozbočovačů. I tady nabízí Ethernet své řešení pod názvem EPON, avšak velká část provozovatelů sítí raději staví pasivní sítě postavené na konkurenčním standardu GPON z dílny mezinárodní telekomunikační unie. I tam ale dochází k tunelování Ethernetových rámců.

Nový Ethernet může být i pomalejší

Už v roce 2002 byl standardizován první 10gigabitový Ethernet. Našel svoje uplatnění zejména v páteřních sítích a na dlouhých trasách, obměna veškerého síťového hardwaru se ale spíše nekonala. Částečně proto, že rychlost jednoho gigabitu je i pro dnešní dobu pro naprostou většinu činností víc než dostatečná, částečně také proto, že 10gigabitový Ethernet už na starých kabelech kategorie 5e obvykle nefunguje. Jeho metalická varianta 10GBase-T není oblíbená kvůli vyššímu zpoždění v porovnání s optickými spoji, ke kterému dochází vlivem pokročilejšího linkového kódování.

Gigabitový Ethernet ale v některých specifických případech naráží na své limity. Jde například o připojení přístupových bodů sítě Wi-Fi. Díky dramatickému rozvoji v oblasti bezdrátových sítí dnes běžně nastává situace, kdy teoretická datová kapacita rádiového rozhraní převyšuje 1000 megabitů za sekundu. Přejít na 10gigabitový Ethernet není možné bez výměny kabelů.

Přesně z těchto důvodů byly později představeny standardy pro 2,5gigabitový a 5gigabitový Ethernet. Využívají pokročilé linkové kódování vyvinuté pro 10gigabitový Ethernet, ale zpomalené tak, aby fungovalo i s obyčejnými kabely kategorie 5e, resp. 6. Zejména prvně jmenovaný standard zažívá v současné době velkou popularitu i v high-end domácích sítích, do kterých již první poskytovatelé nabízejí připojení rychlostí vyšší než jeden gigabit za sekundu.

Budoucnost patří terabitu

Ethernet se i po padesáti letech vyvíjí směrem ke stále vyšším rychlostem. Sto gigabitové spoje jsou dnes zcela běžné v rámci peeringových center, postupně se objevují i rychlosti 200 a 400 gigabitů, ve vývoji je standard pro 800 gigabitů za sekundu.

Je pravděpodobné, že v následujících deseti letech bude k dispozici i standard přenášející ethernetové rámce rychlostí jednoho terabitu za sekundu. Pro zajímavost, při velikosti ethernetových rámců v rozmezí 64–1518 bajtů taková rychlost znamená jeden rámec každých 0,5–1,5 nanosekund.

Odhlédneme-li od datacenter a páteřní internetové infrastruktury, je zde přece jen jistá stagnace. Pravděpodobně uvidíme nějaký rozvoj 2,5gigabitového Ethernetu v souvislosti s multigigabitovými nabídkami ze stran poskytovatelů přístupu k internetu, obecně ale ve spotřebitelském segmentu dominuje Wi-Fi a od drátové infrastruktury se spíše upouští.

Samotné připojení přístupových bodů Wi-Fi pak nejspíše ještě několik let vystačí s 2,5gigabitovým a 5gigabitovým Ethernetem ve firemním prostředí, domácnosti proti tomu budou nejspíše víc a víc používat zcela bezdrátové řešení typu Wi-Fi mesh, kdy se rádiové rozhraní používá i pro propojení přístupových bodů mezi sebou.

Autor článku

Ondřej Caletka vystudoval obor Telekomunikační technika na ČVUT a dnes pracuje ve vzdělávacím oddělení RIPE NCC, mezinárodní asociaci koordinující internetové sítě.

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