IMA SDK를 사용하면 멀티미디어 광고를 웹사이트와 앱에 쉽게 통합할 수 있습니다. IMA SDK는 VAST 호환 광고 서버에서 광고를 요청하고 앱에서 광고 재생을 관리할 수 있습니다. IMA DAI SDK를 사용하면 앱에서 광고 및 콘텐츠 동영상(VOD 또는 실시간 콘텐츠)의 스트림을 요청합니다. 그러면 SDK가 결합된 동영상 스트림을 반환하므로 앱 내에서 광고와 콘텐츠 동영상 간의 전환을 관리하지 않아도 됩니다.
이 가이드에서는 IMA SDK를 간단한 동영상 플레이어 앱에 통합하는 방법을 설명합니다. 완료된 샘플 통합을 보거나 확인하려면 GitHub에서 BasicExample을 다운로드하세요.
IMA DAI 개요
IMA DAI 구현에는 네 가지 주요 SDK 구성요소가 포함됩니다. 여기에는 이 가이드에 설명되어 있습니다.
StreamDisplayContainer
: 동영상 재생 요소의 상단에 배치되고 광고 UI 요소를 수용하는 컨테이너 객체입니다.AdsLoader
: 스트림을 요청하고 스트림 요청 응답 객체에 의해 트리거된 이벤트를 처리하는 객체입니다. 애플리케이션의 수명 주기 내내 재사용할 수 있는 하나의 광고 로더만 인스턴스화해야 합니다.StreamRequest
: 스트림 요청을 정의하는 객체입니다. 스트림 요청은 VOD 또는 실시간 스트림에 대한 요청일 수 있습니다. 요청은 콘텐츠 ID, API 키 또는 인증 토큰, 기타 매개변수를 지정합니다.StreamManager
: 동적 광고 삽입 스트림 및 DAI 백엔드와의 상호작용을 처리하는 객체입니다. 또한 스트림 관리자는 추적 핑을 처리하고 스트림 및 광고 이벤트를 게시자에게 전달합니다.
기본 요건
- Android 스튜디오
- SDK를 통합하는 데 사용할 샘플 동영상 플레이어 앱
샘플 동영상 플레이어 앱 다운로드 및 실행
샘플 앱은 HLS 동영상을 재생하는 작동하는 동영상 플레이어를 제공합니다. 이 템플릿을 IMA Android SDK의 DAI 기능을 통합하기 위한 시작점으로 사용하세요.
- 샘플 동영상 플레이어 앱을 다운로드하고 압축을 풉니다.
- Android 스튜디오를 시작하고 Open an existing Android Studio project를 선택하거나, Android 스튜디오가 이미 실행 중인 경우 File > New > Import Project를 선택합니다. 그런 다음
SampleVideoPlayer/build.gradle
를 선택합니다. - Tools > Android > Sync Project with Gradle Files를 선택하여 Gradle 동기화를 실행합니다.
- Run > Run 'app'을 사용하여 실제 Android 기기 또는 Android Virtual Device에서 플레이어 앱이 컴파일되고 실행되는지 확인합니다. 일반적으로 동영상 스트림이 재생되기 전에 로드되는 데 몇 분 정도 걸립니다.
샘플 동영상 플레이어 확인
샘플 동영상 플레이어에 아직 IMA SDK 통합 코드가 포함되어 있지 않습니다. 샘플 앱은 두 가지 주요 부분으로 구성됩니다.
samplevideoplayer/SampleVideoPlayer.java
- IMA DAI 통합의 기반이 되는 간단한 ExoPlayer 기반 HLS 플레이어입니다.videoplayerapp/MyActivity.java
- 이 활동은 동영상 플레이어를 만들어Context
및SimpleExoPlayerView
를 전달합니다.
플레이어 앱에 IMA Android SDK 추가
IMA SDK에 대한 참조도 포함해야 합니다. Android 스튜디오에서 app/build.gradle
에 있는 애플리케이션 수준 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.19.0' implementation 'com.google.ads.interactivemedia.v3:interactivemedia:3.31.0' }
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.project name"> ... </application> <queries> <intent> <action android:name="android.intent.action.VIEW" /> <data android:scheme="https" /> </intent> <intent> <action android:name="android.intent.action.VIEW" /> <data android:scheme="http" /> </intent> </queries> </manifest>
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()); } }
-
광고가 포함된 스트림을 요청할 수 있도록
buildStreamRequest()
메서드를AdsLoader
에 추가합니다. 광고가 포함된 실시간 스트림(기본적으로 설정됨) 또는 사전 녹화된 콘텐츠를 광고와 함께 재생하는 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
도 필요하므로VideoStreamPlayer
를 구현하는 익명 클래스를 만드는createVideoStreamPlayer()
메서드를 추가합니다.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
구현은 광고 재생에 실패할 경우 대체 URL을 호출하므로 이를 참고하세요. 콘텐츠와 광고가 한 스트림에 있으므로 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); } }
videoplayerapp
의MyActivity
를 수정하여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); } }); } … }
- 로깅용 UI 요소를 추가하도록 활동의 레이아웃 파일인
activity_my.xml
를 수정합니다.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 애플리케이션에서 동영상 광고를 요청하고 게재합니다. 구현을 세부적으로 조정하려면 북마크 및 Snapback 가이드, API 문서를 참조하세요.
문제 해결
동영상 광고를 재생하는 데 문제가 있다면 완성된 BasicExample을 다운로드해 보세요. BasicExample에서 제대로 작동하면 앱의 IMA 통합 코드에 문제가 있을 수 있습니다. 이 가이드와 API 문서를 검토하여 불일치를 감지합니다.
문제가 해결되지 않았나요? IMA SDK 포럼에 의견을 남겨 주세요.