使用 IMA SDK 即可輕鬆將互動式多媒體廣告整合至您的網站和應用程式。IMA SDK 能向任何 符合 VAST 規定的廣告伺服器請求廣告,並管理您應用程式中的廣告播放。使用 IMA DAI SDK,應用程式會對廣告與內容影片發出串流要求 (VOD 或直播內容)。接著,SDK 會傳回合併的影片串流,因此您不必管理應用程式中的廣告和內容影片之間切換。
本指南會說明如何將 IMA SDK 整合到簡單的影片播放器應用程式中。如果您想查看或遵循完成的範例整合,請從 GitHub 下載 BasicExample。
IMA 動態廣告插播總覽
導入 IMA DAI 涉及四個主要 SDK 元件,如本指南所示:
StreamDisplayContainer
: 位於影片播放元素頂端的容器物件,並保存廣告 UI 元素。AdsLoader
:要求要求及處理串流要求回應物件觸發的事件的物件。您只能在一個廣告載入器中執行個體化,並在應用程式的整個生命週期中重複使用。StreamRequest
:定義串流要求的物件。串流要求可以是隨選影片或直播活動。要求會指定內容 ID、API 金鑰或驗證權杖,以及其他參數。StreamManager
: 處理動態廣告插播串流和 DAI 後端互動的物件。串流管理員也會處理追蹤連線偵測 (ping),並將串流和廣告事件轉送給發布商。
必要條件
- Android Studio
- 您將用於整合 SDK 的影片播放器應用程式範例
下載並執行影片播放器應用程式範例
範例應用程式提供可播放 HLS 影片的有效影片播放器。以此做為開始整合 IMA Android SDK 動態廣告插播功能的功能的起點。
- 下載影片播放器應用程式範例並解壓縮。
- 啟動 Android Studio,然後選取「Open an existing Android Studio project」。如果 Android Studio 已在執行中,請選取「File」>「New」>「Import Project」。然後選擇
SampleVideoPlayer/build.gradle
。 - 依序選取「Tools」>「Android」>「Sync Project with Gradle」,以執行 Gradle 同步處理。
- 使用「Run」(執行) >「Run 'app'」(執行應用程式),確認玩家應用程式可透過實體 Android 裝置或 Android 虛擬裝置編譯及執行。影片串流在載入之前稍候片刻。
查看範例影片播放器
範例影片播放器尚未包含任何 IMA SDK 整合程式碼。範例應用程式由兩個主要部分組成:
samplevideoplayer/SampleVideoPlayer.java
- 簡易的 ExoPlayer 式 HLS 播放器,做為 IMA DAI 整合的基礎。videoplayerapp/MyActivity.java
- 這個活動會建立影片播放器,並傳遞Context
和SimpleExoPlayerView
。
將 IMA Android SDK 新增到播放器應用程式
您也必須附上 IMA SDK 的參照。在 Android Studio 中,將下列程式碼新增至應用程式層級的 build.gradle
檔案,位於 app/build.gradle
:
repositories { google() jcenter() } dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.browser:browser:1.3.0' implementation 'com.google.android.exoplayer:exoplayer:2.18.5' implementation 'com.google.ads.interactivemedia.v3:interactivemedia:3.30.1' }
整合 IMA SDK
-
在
videoplayerapp
套件 (在app/java/com.google.ads.interactivemedia.v3.samples/videoplayerapp/
中) 中建立名為SampleAdsWrapper
的新類別,納入現有的SampleVideoPlayer
並新增導入 IMA 動態廣告插播 (DAI) 的邏輯。如要這麼做,您必須先建立AdsLoader
,用來向廣告伺服器請求廣告。videoplayerapp/SampleAdsWrapper.java
package com.google.ads.interactivemedia.v3.samples.videoplayerapp; import android.annotation.TargetApi; import android.content.Context; import android.os.Build; import android.view.ViewGroup; import android.webkit.WebView; import com.google.ads.interactivemedia.v3.api.AdErrorEvent; import com.google.ads.interactivemedia.v3.api.AdEvent; import com.google.ads.interactivemedia.v3.api.AdsLoader; import com.google.ads.interactivemedia.v3.api.AdsManagerLoadedEvent; import com.google.ads.interactivemedia.v3.api.CuePoint; import com.google.ads.interactivemedia.v3.api.ImaSdkFactory; import com.google.ads.interactivemedia.v3.api.ImaSdkSettings; import com.google.ads.interactivemedia.v3.api.StreamDisplayContainer; import com.google.ads.interactivemedia.v3.api.StreamManager; import com.google.ads.interactivemedia.v3.api.StreamRequest; import com.google.ads.interactivemedia.v3.api.player.VideoProgressUpdate; import com.google.ads.interactivemedia.v3.api.player.VideoStreamPlayer; import com.google.ads.interactivemedia.v3.samples.samplevideoplayer.SampleVideoPlayer; import java.util.ArrayList; import java.util.HashMap; import java.util.List; public class SampleAdsWrapper implements AdEvent.AdEventListener, AdErrorEvent.AdErrorListener, AdsLoader.AdsLoadedListener { // Live stream asset key. private static final String TEST_ASSET_KEY = "sN_IYUG8STe1ZzhIIE_ksA"; // VOD content source and video IDs. private static final String TEST_CONTENT_SOURCE_ID = "2528370"; private static final String TEST_VIDEO_ID = "tears-of-steel"; private static final String PLAYER_TYPE = "DAISamplePlayer"; /** * Log interface, so you can output the log commands to the UI or similar. */ public interface Logger { void log(String logMessage); } private ImaSdkFactory sdkFactory; private AdsLoader adsLoader; private StreamDisplayContainer displayContainer; private StreamManager streamManager; private List<VideoStreamPlayer.VideoStreamPlayerCallback> playerCallbacks; private SampleVideoPlayer videoPlayer; private Context context; private ViewGroup adUiContainer; private String fallbackUrl; private Logger logger; public SampleAdsWrapper(Context context, SampleVideoPlayer videoPlayer, ViewGroup adUiContainer) { this.videoPlayer = videoPlayer; this.context = context; this.adUiContainer = adUiContainer; sdkFactory = ImaSdkFactory.getInstance(); playerCallbacks = new ArrayList<>(); createAdsLoader(); displayContainer = sdkFactory.createStreamDisplayContainer( this.adUiContainer, videoStreamPlayer ); } private void createAdsLoader() { ImaSdkSettings settings = new ImaSdkSettings(); adsLoader = sdkFactory.createAdsLoader(context); } public void requestAndPlayAds() { adsLoader.addAdErrorListener(this); adsLoader.addAdsLoadedListener(this); adsLoader.requestStream(buildStreamRequest()); } }
-
在
AdsLoader
中加入buildStreamRequest()
方法,以便要求包含廣告的串流。這是直播廣告 (預設設定) 的直播或隨選影片(VOD) 直播影片,會播放含有廣告的預錄內容。如要啟用 VOD 串流,請對直播要求加上註解,並將 VOD 串流要求取消註解。videoplayerapp/SampleAdsWrapper.java
private StreamRequest buildStreamRequest() { VideoStreamPlayer videoStreamPlayer = createVideoStreamPlayer(); videoPlayer.setSampleVideoPlayerCallback( new SampleVideoPlayer.SampleVideoPlayerCallback() { @Override public void onUserTextReceived(String userText) { for (VideoStreamPlayer.VideoStreamPlayerCallback callback : playerCallbacks) { callback.onUserTextReceived(userText); } } @Override public void onSeek(int windowIndex, long positionMs) { // See if you would seek past an ad, and if so, jump back to it. long newSeekPositionMs = positionMs; if (streamManager != null) { CuePoint prevCuePoint = streamManager.getPreviousCuePointForStreamTime(positionMs / 1000); if (prevCuePoint != null && !prevCuePoint.isPlayed()) { newSeekPositionMs = (long) (prevCuePoint.getStartTime() * 1000); } } videoPlayer.seekTo(windowIndex, newSeekPositionMs); } @Override public void onContentComplete() { for (VideoStreamPlayer.VideoStreamPlayerCallback callback : playerCallbacks) { callback.onContentComplete(); } } @Override public void onPause() { for (VideoStreamPlayer.VideoStreamPlayerCallback callback : playerCallbacks) { callback.onPause(); } } @Override public void onResume() { for (VideoStreamPlayer.VideoStreamPlayerCallback callback : playerCallbacks) { callback.onResume(); } } @Override public void onVolumeChanged(int percentage) { for (VideoStreamPlayer.VideoStreamPlayerCallback callback : playerCallbacks) { callback.onVolumeChanged(percentage); } } }); // Live stream request. StreamRequest request = sdkFactory.createLiveStreamRequest( TEST_ASSET_KEY, null, displayContainer); // VOD request. Comment the createLiveStreamRequest() line above and uncomment this // createVodStreamRequest() below to switch from a live stream to a VOD stream. // StreamRequest request = sdkFactory.createVodStreamRequest(TEST_CONTENT_SOURCE_ID, // TEST_VIDEO_ID, null, displayContainer); return request; }
-
此外,您還必須使用
VideoStreamPlayer
來播放串流,因此請新增createVideoStreamPlayer()
方法,藉此建立實作VideoStreamPlayer
的匿名類別。videoplayerapp/SampleAdsWrapper.java
private VideoStreamPlayer createVideoStreamPlayer() { VideoStreamPlayer player = new VideoStreamPlayer() { @Override public void loadUrl(String url, List<HashMap<String, String>> subtitles) { videoPlayer.setStreamUrl(url); videoPlayer.play(); } @Override public void addCallback( VideoStreamPlayerCallback videoStreamPlayerCallback) { playerCallbacks.add(videoStreamPlayerCallback); } @Override public void removeCallback( VideoStreamPlayerCallback videoStreamPlayerCallback) { playerCallbacks.remove(videoStreamPlayerCallback); } @Override public void onAdBreakStarted() { // Disable player controls. videoPlayer.enableControls(false); log("Ad Break Started\n"); } @Override public void onAdBreakEnded() { // Re-enable player controls. videoPlayer.enableControls(true); log("Ad Break Ended\n"); } @Override public VideoProgressUpdate getContentProgress() { return new VideoProgressUpdate(videoPlayer.getCurrentPosition(), videoPlayer.getDuration()); } }; return player; }
-
實作必要的事件監聽器,並新增錯誤處理機制的支援。
重要事項:請參照AdErrorListener
的實作方式,因為如果廣告無法播放,就會呼叫備用網址。由於內容和廣告位於一個串流中,因此如果 DAI 串流發生錯誤,您也必須呼叫備用串流。videoplayerapp/SampleAdsWrapper.java
/** AdErrorListener implementation **/ @Override public void onAdError(AdErrorEvent event) { // play fallback URL. videoPlayer.setStreamUrl(fallbackUrl); videoPlayer.enableControls(true); videoPlayer.play(); } /** AdEventListener implementation **/ @Override public void onAdEvent(AdEvent event) { switch (event.getType()) { case AD_PROGRESS: // Do nothing or else log are filled by these messages. break; default: log(String.format("Event: %s\n", event.getType())); break; } } /** AdsLoadedListener implementation **/ @Override public void onAdsManagerLoaded(AdsManagerLoadedEvent event) { streamManager = event.getStreamManager(); streamManager.addAdErrorListener(this); streamManager.addAdEventListener(this); streamManager.init(); } /** Sets fallback URL in case ads stream fails. **/ void setFallbackUrl(String url) { fallbackUrl = url; }
-
新增記錄至記錄。
videoplayerapp/SampleAdsWrapper.java
/** Sets logger for displaying events to screen. Optional. **/ void setLogger(Logger logger) { this.logger = logger; } private void log(String message) { if (logger != null) { logger.log(message); } }
- 修改
MyActivity
videoplayerapp
中的執行個體並呼叫SampleAdsWrapper
。videoplayerapp/MyActivity.java
… import android.view.ViewGroup; import android.widget.ScrollView; … public class MyActivity extends AppCompatActivity { … @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my); View rootView = findViewById(R.id.videoLayout); videoPlayer = new SampleVideoPlayer(rootView.getContext(), (SimpleExoPlayerView) rootView.findViewById(R.id.playerView)); videoPlayer.enableControls(false); final SampleAdsWrapper sampleAdsWrapper = new SampleAdsWrapper(this, videoPlayer, (ViewGroup) rootView.findViewById(R.id.adUiContainer)); sampleAdsWrapper.setFallbackUrl(DEFAULT_STREAM_URL); final ScrollView scrollView = (ScrollView) findViewById(R.id.logScroll); final TextView textView = (TextView) findViewById(R.id.logText); sampleAdsWrapper.setLogger(new SampleAdsWrapper.Logger() { @Override public void log(String logMessage) { Log.i(APP_LOG_TAG, logMessage); if (textView != null) { textView.append(logMessage); } if (scrollView != null) { scrollView.post(new Runnable() { @Override public void run() { scrollView.fullScroll(View.FOCUS_DOWN); } }); } } }); playButton = (ImageButton) rootView.findViewById(R.id.playButton); // Set up play button listener to play video then hide play button. playButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { sampleAdsWrapper.requestAndPlayAds(); playButton.setVisibility(View.GONE); } }); } … }
- 修改活動的版面配置檔案
activity_my.xml
,以便新增 UI 元素進行記錄。res/layout/activity_my.xml
… <TextView android:id="@+id/playerDescription" android:text="@string/video_description" android:textAlignment="center" android:gravity="center_horizontal" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="0.1" android:textSize="@dimen/font_size" /> <!-- UI element for viewing SDK event log --> <ScrollView android:id="@+id/logScroll" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="0.5" android:padding="5dp" android:background="#DDDDDD"> <TextView android:id="@+id/logText" android:layout_width="match_parent" android:layout_height="wrap_content"> </TextView> </ScrollView> …
恭喜!您現在可以在 Android 應用程式中請求並顯示影片廣告。 如要調整實作方式,請參閱書籤和貼齊指南以及 API 說明文件。
疑難排解
如果您在播放影片廣告時遇到問題,請嘗試下載已完成的 BasicExample。如果 BasicExample 可以正常執行,則可能是應用程式的 IMA 整合程式碼有問題。請參閱本指南和 API 文件,偵測任何的差異。
問題仍然沒有解決嗎?請前往 IMA SDK 論壇放置一行。