Procesory Ampere Altra jsou tu: 80jádrový ARM slibuje vyšší výkon než nejrychlejší Epyc

14. 3. 2020

Sdílet

Po pozoruhodném Gravitonu2 je tu 7nm procesor ARM pro servery s 80 jádry Neoverse N1. Altra má špičkovou konektivitu i výkon, Xeon 8280 má předčit 2,23×.

Před nedávnem vzbudil značný ohlas v serverových procesorech příchod 7nm (a na rozdíl od AMD Epyců 7002 monolitického) procesoru Amazon Graviton2 s 64 jádry. U toho ale kazilo radost, že je dostupný jenom jako služba na dálku (nájemná instance) v cloudu. Ale stejná architektura ARM N1 Neoverse bude teď k mání i pro ostatní zájemce – firma Ampere, která dříve vyráběla procesory X-Gene a eMAG, nyní uvádí svůj vlastní procesor na bázi N1 Neoverse. Jmenuje se Ampere Altra, rovněž je 7nm a jader má dokonce 80, přičemž díky vyšším taktům také asi bude výkonnější.

80jádrový seriózní nástup serverových ARMů

Procesor Altra je určen zejména tzv. „hyperscale“ obřím datacentrovým hráčům a poskytovatelům cloudových služeb. Trošku jsme ho tu nakousli už v prosinci, kdy jsme o něm ale psali ještě jako o eMAG 2. Přechod na architekturu Neoverse N1 ale bude v podstatě úplně nový začátek, takže dává na místě dát čipu i nové jméno.

Altra obsahuje 80 jader ARM Neoverse N1, přičemž tato jádra nepoužívají SMT, takže CPU poskytuje pouze 80 vláken. Ve slajdech se to podává jako výhoda, kdy výkon je předvídatelnější, protože vlákno nemůže být negativně ovlivněno zátěží druhého SMT vlákna; současně také odpadají některé bezpečnostní opatření. Nicméně ve skutečnosti je to samozřejmě tak, že jádro prostě SMT neumí a hledání výhod je tu až sekundární reakce na tuto skutečnost. Je celkem pravděpodobné, že jednou ARM u architektur Neoverse SMT také přidá a lze pochybovat, že ho pak Ampere plošne deaktivuje.

Prezentace ARM procesoru pro servery Ampere Altra 10

Jádra jsou typu out of order se čtyřmi dekodéry a třemi ALU a poměrně krátkou 11stupňovou pipeline. L1 cache má 64+64 KB (data/instrukce), k tomu má každé jádro 1MB L2 cache a konečně má celý procesor společnou 32MB L3 cache. Instrukční sada je ARMv8.2, ale obsahuje již některé součásti ARMv8.3 a v8.5. Více detailů jako třeba přehled pipeline můžete vidět v galerii slajdů ARMu k architektuře N1.

Procesor poběží zprvu na frekvenci 3,0 GHz (respektive nejvýkonnější verze bude, vedle toho asi firma bude prodávat i osekané levnější modely), i když výhledově by snad mohla být až 3,3 GHz verze. Ampere 3,0 GHz uvádí jako „turbo“ ale podle zastoupení firmy údajně ve skutečnosti procesor je schopný držet tuto frekvenci permanentně při všech jádrech zatížených (včetně SIMD zátěžě), takže označení turbo se úplně nehodí. Výkonu bude pomáhat, že jádro má velmi krátkou pipeline (a tím nízké postihy za neodhadnuté větvení) a také další rysy, které asi snižují dosažitelné frekvence, ale zvyšují IPC. Například L2 cache má latenci jenom 9-11 cyklů (pro srovnání – Zen/Zen 2 nebo Skylake mají latenci L2 12 cyklů, Intel Ice Lake 13 cyklů).

Top konektivita a paměti

Procesor má osmikanálový řadič pamětí DDR4-3200 podporující ECC (ochranu ECC a RAS funkce mají i všechny cache), jeden procesor podporuje až 4 TB RAM, přičemž procesory mohou být v sestavě dva (2S systém), každý s 4 TB RAM, čili celkem až 8 TB.

Prezentace ARM procesoru pro servery Ampere Altra 11 Schopnosti řadiče pamětí

Procesor má řadič poskytující 128 linek PCI Express 4.0, čímž se vyrovná AMD Epycu 7002. U 2S systému se 32 linek vyhradí pro komunikaci s druhým CPU (v takovém případě na nich ale běží koherentní protokol ARM CCIX s celkovou propustností mezi oběma CPU 50 GB/s). 2S systém má tak pro volné použití 192 linek PCIe 4.0 – více než 2S Epyc, jenž používá více linek k projení (je možné mít konfiguraci používající 48 linek k propojení a 160 pro periférie, výchozí režim je 64 linek pro propojení a 128 linek pro periférie, i když konfigurace jako u Altra by podle webu ServeTheHome volitelně v případě zájmu byla možná také).

Prezentace ARM procesoru pro servery Ampere Altra 12 I/O konektivita

Tato konektivita bude součástí všech různých modelů procesorů Altra, které Ampere bude nabízet. Podobně jako u AMD se budou jednotlivé verze lišit jen spotřebou, počtem jader a frekvencemi (a pak cenou). Konfigurace bohužel nemáme, ale málojádrové verze mají začínat na 45W TDP a nejvýkonnější 3,0GHz 80jádro má TDP až 210 W. Vše pravděpodobně ale bude vyráběno ze stejného výchozího křemíku, který by asi mohl být dost veliký, i když číslo nemáme. Výroba na 7 nm probíhá u TSMC. Procesor se jinak vyrábí v provedení pro socket (LGA). Není pájený na desku, takže bude upgradovatelný.

ARM procesor Ampere Altra ServeTheHome ARM procesor Ampere Altra. Pouzdro LGA je hodně velké (Zdroj: ServeTheHome)

Podpora v Linuxu by měla být out of the box

Po stránce softwaru podporuje procesor standard ARM SBSA Level 4, což by mělo znamenat, že Linuxovbé OS na něm poběží bez problémů a modifikací, jako jsme na to zvyklí na platformě x86. Čip má zabudovanou řídící jednotku, která se stará o management (včetně možno vzdálené správy), řízení spotřeby a také třeba Secure Boot, jde tedy o obdobu PSP v procesorech AMD.

Prezentace ARM procesoru pro servery Ampere Altra 14 Designy serverů s procesory Altra. 1S servery chystá například Gigabyte

Výkon vyšší než Epyc 7742

Ampere slibuje výkon vyšší, než co podává nejrychlejší 7nm Epyc od AMD (64jádro Epyc 7742 – neporovnává se se speciálním HPC Epycem 7H12 s 280W TDP, což je asi ale fér). V benchmarku Specrate2017_int_base je top Altra údajně 1,04× rychlejší. Proti nejrychlejšímu 14nm Intel Xeonu Platinum 8280 (dvojčipové až 400W Xeony 9200 jsou pominuty) má být Altra až 2,23× výkonnější. Čísla pro FPU variantu testu Ampere neuvádí a asi budou horší (jádro má jen dvě 128bitové jednotky SIMD Neon), ale podle výrobce není procesor zaměřený na cloudové použití na tyto úlohy určen.

Je ale ještě třeba říct, že toto srovnání má dva háčky. Za prvé jsou penalizovány x86 procesory. Aby bylo zohledněno, že ARM verze benchmarku SPEC byla přeložena pomocí GCC, které má nižší výkon, zatímco binárky pro AMD a Intel vyplivly optimalizované kompilátory AOCC a ICC, byla reálná skóre Epycu a Xeonu před výše uvedeným srovnáním snížena o 16,5 % a 24 %. Toto není úplně neobvyklá věc (dělalo to myslím i AMD) a je to jasně přiznáno. Čistější by možná bylo přeměřit tyto procesory s GCC, ale co se dá dělat. Druhá výhrada až zrada je v tom, že číslo Ampere není pro 3,0 GHz, ale pro 3,3 GHz. Ampere údajně později uvede verzi procesoru, která bude mít až takovýto takt, ale zatím byl ohlášen jenom 3,0 GHz model 80jádra, takže toto trošku zavání snahou mít v grafu prvenství, i když k tomu podmínky ještě úplně nejsou.

Prezentace ARM procesoru pro servery Ampere Altra 06 Porovnání výkonu Altra na 3,3 GHz s Epycem 7742 (64 jader) a Xeonem Platinum 8280 (28 jader) dle Ampere

Další generace na cestě

I beztak vypadá Ampere Altra jako velmi výkonné a pozoruhodné serverové CPU, které by mělo v řadě úloh být dobře uplatnitelné, byť samozřejmě neznáme ceny, takže není jasné, jak atraktivní bude ekonomicky. Spolu s Gravitonem2 by měla tato CPU představovat moment, kdy jsou procesory ARM opravdu konkurenceschopné v serverech (i když to se snad dalo říct už i o Cavium ThunderX2, tedy před příchodem současné nové generace post14nm procesorů).

hacking_tip

Prezentace ARM procesoru pro servery Ampere Altra 15 Roadmapa procesorů Ampere Altra

A agresivně má Ampere konkurovat i v následujících letech. Po nynějším uvedení této generace Ampere Altra má už v roce 2021 přijít následník, jenž má kódové označení Mystique. A má údajně používat stejný socket, takže servery se možná budou dát upgradovat. V roce 2022 bude opět nová generace, s kódovým označením Siryn.

Zdroje: ServeTheHome, AnandTech

Galerie: ARM procesor pro servery Ampere Altra

Autor článku

Redaktor portálu Cnews.cz. Zaměřuje se na procesory, mobilní SoC, grafické karty, disky a další počítačový hardware. 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 »