Nvidia uvádí kompletní nabídku grafik GeForce RTX 4000 pro notebooky. A 5. generaci Max-Q

4. 1. 2023

Sdílet

 Autor: Nvidia
Nová generace GPU Nvidia GeForce s architekturou Ada Lovelace přichází do notebooků. Vyšly modely od GeForce RTX 4050 až po RTX 4090 a s nimi také nová vylepšení Max-Q. První notebooky vyjdou za měsíc a něco.

Vedle GeForce RTX 4070 Ti pro desktop Nvidia včera na CES 2023 představila také další modely GPU generace Ada Lovelace, určené ale pro notebooky. Ty přijdou na trh během několika týdnů a díky použití 4nm čipů přinesou při stejné spotřebě lepší výkon. Mobilní Ada používá GPU AD103, AD104, ale i úplně nové čipy AD106 a AD107, které se ještě do desktopových grafik nestihly dostat.

Mobilní grafiky RTX 4000 přinášejí stejné architektonické novinky jako výkonnější verze pro stolní PC (tedy například hardwarový enkodér videa do formátu AV1, podporu interpolace pohybu DLSS 3). Budou ale mít nižší výkon jednak kvůli logicky více omezené spotřebě, ale také proto, že Nvidia v notebooku nepoužije úplně nejvýkonnější z těchto GPU (AD102), ale začne až druhým čipem v řadě.

Max-Q 5

Mobilní verze GPU by měla mít některá nová zlepšení energetické efektivity, které Nvidia nazývá Max-Q páté generace. Jde, zdá se, o zlepšení týkající se pamětí. Použité mají být paměti GDDR6 se sníženým napětím a také je implementováno přepínání frekvence GDDR6 ve třech stupních („Tri-Speed Memory Control“).

Třetí zlepšení je nazvané „High Efficiency On-Chip Memory“ a má zlepšovat energetickou efektivitu, zdvojnásobovat propustnost, zdesetinásobovat kapacitu „paměti GPU“ (toto je asi trošku omyl marketingu) a vylepšovat schopnost čipu odpojovat části obvodů od generátoru hodinového signálu pro úsporu energie. Pod tímto označením se asi prostě skrývá L2 cache, která byla v čipech architektury Ada výrazně zvětšená na podobné kapacity, jaké má Infinity Cache u AMD. Nejde tedy o nic nového, tuto funkci mají i desktopové verze RTX 4000 (a onen podivný údaj o 10× kapacitě paměti asi má říkat, že se 10× zvětšila cache v GPU).

CES23 Slide Max QTechnologies ReinventedLaptop Technologie Nvidia Max-Q 5. generace (zdroj: Nvidia)

GeForce RTX 4090

Nejvyšším modelem nabízeným v noteboocích bude GeForce RTX 4090. Ta je založená na čipu AD103 s 9728 jednotkami FP32 (shadery), GPU má tedy aktivních 76 z celkem 84 bloků SM v čipu a mělo by obsahovat 76 RT jader a 304 tensor jader – jde o konfiguraci odpovídající desktopové GeForce RTX 4080. Grafika má osazeno 16 GB paměti GDDR6 na 256bitové sběrnici, ale Nvidia neprozrazuje frekvenci. Pokud by běžela na 16,0 GHz efektivně, znamenalo by to propustnost 512 GB/s.

Takt boostu je pro toto GPU 1,45–2,04 GHz. Jak vysoký bude, to závisí na spotřebě. Výrobci notebooků mohou spotřebu nastavit v rozmezí 80–150 W, při 80 W logicky takty budou nízké. Tento rozsah spotřeb ještě nemá započítaný další příkon navíc prostřednictvím Dynamic Boostu (součást technologií Max-Q), kdy GPU dostane příděl elektřiny navíc na úkor procesoru, takže 150 W není maximum.

Při 150 W by mělo být prý dosahováno teoretického matematického výkonu až 38,9 TFLOPS, což je blízko výkonu desktopové GeForce RTX 4070 Ti, ale je otázka, zda bude udržitelný delší dobu, nebo jenom špičkově.

GeForce RTX 4080

Druhý model v řadě je již založený na čipu AD104. Tedy na tom, který pohání desktopovou RTX4070 Ti, ale zde bude trošku oslaben na jen 7424 shaderů (58 SM, 58 RT jader, 232 tensor jader). Takt má být 1,35–2,28 GHz při rozsahu spotřeb 60–150 W. Také zde se ovšem ještě může spotřeba zvýšit Dynamic Boostem.

Použité GPU má jenom 192bitovou sběrnici, takže kapacita paměti zde bude jenom 12 GB a propustnost patrně 384 GB/s (pokud bude frekvence paměti 16,0 GHz efektivně).

Podle Nvidie budou notebooky s těmito dvěma grafikami dostupné od 2. února (februára). Ceny přístrojů mají začínat na 1999 $ (54 650 Kč / 2278 €).

CES23 Slide AnnouncingFlagship RTX40SeriesLaptops Notebooky s grafikami Nvidia GeForce RTX 4000 (zdroj: Nvidia)

GeForce RTX 4070

Třetí grafika je založená na čtvrtém GPU v řadě, AD106. To jí poskytuje 4608 shaderů (36 SM, 36 RT jader, 144 tensor jader). Takt GPU je 1,23 až 2,175 GHz, což opět bude závislé na spotřebě nastavené v konkrétním notebooku. Výrobci mohou zvolit spotřebu od 35 až do 115 W – toto jsou opět údaje platné předtím, než se přidá Dynamic Boost.

Čip AD106 má jen 128bitovou paměťovou sběrnici, takže kapacita již je jen 8 GB GDDR6. Propustnost by byla 256 GB/s s 16,0GHz čipy, ale Nvidia frekvenci pamětí nepotvrdila.

GeForce RTX 4060

Další dva modely v nabídce, které ještě Nvidia oznámila, jsou založené na čipu AD107. Asi se jimi již uzavírá celá nabídka mobilních grafik Ada (i když později ještě mohou vyjít nějaké „Ti“ refreshe), toto GPU je asi poslední člen 4nm rodiny. GeForce RTX 4060 má toto GPU s 3072 shadery (24 SM / 24 RT jader, 96 tensor jader) na taktu 1,47–2,37 GHz. Rozsah spotřeby je možné nastavit mezi 35 až 115 W (plus ještě navíc Dynamic Boost), takže ve výkonech zase bude hodně velká kolísavost podle konstrukce a nastavení notebooku.

Tato grafika bude mít rovněž 8GB paměť a její sběrnice je také 128bitová, takže propustnost by mohla být podobná jako u RTX 4070 (tedy pokud není snížená frekvence, což by asi mohlo nastat). Čip AD107 se od AD106 sice neliší šířkou paměťové sběrnice, ale mohl by mít menší L2 cache, která by způsobila rozdíl v efektivním výkonu paměťového subsystému a horší výkon ve vyšších rozlišeních.

Specifikace mobilních grafik Nvidia GeForce RTX 4000 Specifikace mobilních grafik Nvidia GeForce RTX 4000 (zdroj: Nvidia)

GeForce RTX 4050

Poslední model série Lovelace pro notebooky je GeForce RTX 4050. Ta je založená na stejném GPU jako předchozí grafika, ale má jej v ořezané formě. Aktivních je jen 2560 shaderů (20 SM, 20 RT jader, 80 tensor jader) na taktu 1,61–2,37 GHz. Volitelný rozsah je opět stejných 35–115 W (plus Dynamic Boost).

Kromě výpočetních jednotek jsou ale osekané i paměti. Sběrnice je jen 96bitová, takže na ní má GPU pověšeno jen 6 GB GDDR6. Propustnost tak bude o 25 % horší (192 GB/s, pokud by byly použité 16GHz čipy).

Podle Nvidie se notebooky s těmito třemi levnějšími grafikami (RTX 4050, 4060 a 4070) začnou prodávat o něco později, dostupné budou od 22. 2. Ceny mají začínat na polovině (999 $), což je asi údaj pro konfigurace s nejslabší RTX 4050.

CES23 Slide AnnouncingRTX40SeriesLaptops Notebooky s grafikami Nvidia GeForce RTX 4000 (zdroj: Nvidia)

Nižší spotřeby, než se čekalo(?)

před nedávnem uniklých parametrech se pro tyto nové grafiky uváděla vyšší spotřeba – až 175 W plus 25 W dynamického boostu (takže celkově až 200 W), což se ale teď nepotvrdilo. Ledaže by k 150W maximu nyní dynamic boost dokázal přidat až 50 W. V uniklých parametrech jsou totiž základní maximální TDP všech GPU bez Dynamic Boostu konzistentně uváděná o 25 W vyšší (a předpokládal se jako dosud 25W Dynamic Boost navíc) než v nyní oficiálně odhalených parametrech.

bitcoin_smenarna

Je tedy možné, že tato nová generace GPU a notebooků bude větší kus spotřeby ponechávat na onom Dynamic Boostu, jež tyto watty dokáže přelévat mezi CPU a GPU. Pokud bude Dynamic Boost přidávat 50 W, pak by se potvrdil únik o až 200W spotřebě možné u GeForce RTX 4090 a RTX 4080. Ale toto je jenom hypotéza, uvidíme asi, až když budou představené konkrétní notebooky a jejich nastavení spotřeby GPU.

Zdroje: Nvidia (1, 2)

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 »