Android용 IMA SDK를 사용한 맞춤 광고 재생

Android용 IMA SDK를 앱에 통합하는 가장 빠르고 간단한 방법은 SDK가 모든 광고 재생 로직을 처리하도록 하고 앱에서는 콘텐츠 동영상 재생에 집중하는 것입니다. 'SDK 소유 광고 재생'이라고 하는 이 접근 방식은 시작하기의 기본 옵션입니다.

그러나 동영상 플레이어에서도 광고를 재생하려는 경우 SDK에서 이를 위한 인터페이스를 제공합니다. 이 접근 방식을 '맞춤 광고 재생'이라고 하며 이 가이드의 나머지 부분에서는 구현에 대해 설명합니다.

기본 요건

  • 기본 IMA 통합

현재 기본 IMA 통합이 없는 경우 github의 고급 예제를 출발점으로 참고하시기 바랍니다. 이 예에서는 이미 맞춤 광고 재생을 구현하고 있습니다. 이 가이드의 나머지 부분에서는 IMA 광고를 통한 맞춤 광고 재생에 필요한 기능을 설명합니다.

VideoAdPlayer와의 인터페이스

맞춤 광고를 재생하려면 앱에서 VideoAdPlayer 인터페이스를 구현해야 합니다. 이 인터페이스는 SDK에서 앱에 광고 동영상을 재생하도록 알리는 데 사용됩니다. 또한 앱에서 이 인터페이스를 사용하여 SDK에 주요 동영상 광고 이벤트를 알립니다. 인터페이스를 구현하려면 다음 단계를 따르세요.

VideoAdPlayer 만들기

첫 번째 단계는 requestAds()에서 익명의 VideoAdPlayer 클래스를 만드는 것입니다.

private VideoAdPlayer videoAdPlayer;
...

private void requestAds(String adTagUrl) {
    videoAdPlayer = new VideoAdPlayer() {
    };
}

동영상 메서드 추가

다음으로 동영상 플레이어에 광고 동영상을 재생, 로드, 중지, 일시중지하도록 지시하는 메서드를 추가합니다. 플레이어를 해제하고 볼륨을 가져오는 메서드도 여기에 추가합니다.

videoAdPlayer = new VideoAdPlayer() {
        @Override
        public void playAd() {
            if (mIsAdDisplayed) {
                videoPlayer.resume();
            } else {
                isAdDisplayed = true;
                videoPlayer.play();
            }
        }

        @Override
        public void loadAd(String url) {
            isAdDisplayed = true;
            videoPlayer.setVideoPath(url);
        }
        @Override
        public void stopAd() {
            videoPlayer.stopPlayback();
        }
        @Override
        public void pauseAd() {
            videoPlayer.pause();
        }

        @Override
        public void release() {
            // any clean up that needs to be done
        }

        @Override
        public int getVolume() {
            return videoPlayer.getVolume();
        }
};

이러한 메서드는 동영상 플레이어 자체의 유사한 메서드를 둘러싼 얇은 래퍼입니다. 이러한 메서드는 광고 표시 여부를 추적하는 데 사용되는 내부 변수를 설정합니다. 맞춤 광고 재생에서는 동영상 플레이어가 콘텐츠 동영상과 동영상 광고를 모두 재생하므로 현재 표시되고 있는 광고를 추적해야 합니다.

광고 재생 진행률

VideoAdPlayer 인터페이스는 또 다른 인터페이스인 AdProgressProvider를 구현하므로 개발자는 이 인터페이스도 구현해야 합니다. 이 인터페이스에는 SDK가 광고의 재생 정보를 가져오는 데 사용하는 getAdProgress() 메서드가 하나뿐입니다. 익명의 VideoAdPlayer 클래스에서 다른 메서드 아래에 추가합니다.

VideoAdPlayer videoAdPlayer = new VideoAdPlayer() {
        ...
        @Override
        public VideoProgressUpdate getAdProgress() {
            if (!isAdDisplayed || videoPlayer.getDuration() <= 0) {
                return VideoProgressUpdate.VIDEO_TIME_NOT_READY;
            }
            return new VideoProgressUpdate(videoPlayer.getCurrentPosition(),
                    videoPlayer.getDuration());
        }
};

getAdProgress()VideoProgressUpdate 유형을 반환하며 이 유형은 동영상의 현재 위치와 길이로 구성되어야 합니다. 플레이어에서 광고를 재생하지 않거나 재생 시간을 사용할 수 없는 경우 다음 예와 같이 VideoProgressUpdate.VIDEO_TIME_NOT_READY를 반환하도록 합니다.

동영상 콜백 관리

맞춤 광고를 재생하려면 앱에서 SDK에 주요 동영상 이벤트를 알려야 합니다. SDK의 뷰에서 VideoAdPlayer.VideoAdPlayerCallback 인터페이스에서 설명하는 콜백입니다. 콜백 메서드 자체에 들어가기 전에 SDK의 요청 시 콜백을 추가하고 삭제할 수 있어야 합니다. 이 작업은 addCallback()removeCallback()를 사용하여 VideoAdPlayer 내에서 실행됩니다.

private List<VideoAdPlayerCallback> adCallbacks = new ArrayList<>(1);

VideoAdPlayer videoAdPlayer = new VideoAdPlayer() {
        ...
        @Override
        public void addCallback(VideoAdPlayerCallback videoAdPlayerCallback) {
            adCallbacks.add(videoAdPlayerCallback);
        }

        @Override
        public void removeCallback(VideoAdPlayerCallback videoAdPlayerCallback) {
            adCallbacks.remove(videoAdPlayerCallback);
        }
};

이 구현은 List<>.add()remove() 메서드를 호출할 콜백에 List<>를 사용합니다.

콜백 호출

이제 SDK에서 앱에 콜백을 추가 및 삭제하도록 지시할 수 있으므로 콜백이 호출되는 위치를 정의합니다. 동영상 재생, 일시중지 또는 다시 시작과 같은 중요한 동영상 이벤트가 발생하거나 동영상이 종료되거나 오류가 발생할 때 앱에서 이러한 콜백을 호출해야 합니다.

이렇게 하려면 SampleVideoPlayer를 확장하여 VideoFragment에서 추가된 동영상 이벤트의 리스너를 추가합니다. 이러한 광고 콜백을 호출하도록 SampleVideoPlayer에 별도의 리스너를 만드는 이유는 SampleVideoPlayer가 광고에 관해 전혀 알지 못하므로 광고를 처리할 수 있는 요소에 동영상 이벤트를 전달해야 하기 때문입니다.

public interface OnVideoEventsListener {
    void onPlay();
    void onResume();
    void onPause();
    void onError();
}

private final List<OnVideoEventsListener> onVideoEventsListeners = new ArrayList<>(1);

public void addVideoEventsListener(OnVideoEventsListener listener) {
    onVideoEventsListeners.add(listener);
}

시작, 일시중지, 다시 시작

새 enum을 만들어 재생 상태를 추적하고 SampleVideoPlayerstart()pause() 메서드에 관한 새 재정의를 추가합니다.

private enum PlaybackState {
    STOPPED, PAUSED, PLAYING
}

private PlaybackState playbackState = PlaybackState.STOPPED;

@Override
public void start() {
    super.start();
    switch (playbackState) {
        case STOPPED:
            for (OnVideoEventsListener listener : onVideoEventsListeners) {
                listener.onPlay();
            }
            break;
        case PAUSED:
            for (OnVideoEventsListener listener : onVideoEventsListeners) {
                listener.onResume();
            }
            break;
        default:
            // Already playing; do nothing.
            break;
    }
    playbackState = PlaybackState.PLAYING;
}

@Override
public void pause() {
    super.pause();
    playbackState = PlaybackState.PAUSED;
    for (OnVideoEventsListener listener : onVideoEventsListeners) {
        listener.onPause();
    }
}

오류 처리

init()에서 설정한 동영상 플레이어의 익명 오류 리스너를 재정의합니다.

@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
    playbackState = PlaybackState.STOPPED;
    for (OnVideoEventsListener listener : onVideoEventsListeners) {
        listener.onError();
    }

    // Returning true signals to MediaPlayer that the error was handled.
    // This  prevents the completion handler from being called.
    return true;
}

리스너 구현

VideoFragment로 돌아가서 SampleVideoPlayer 인스턴스에 익명 OnVideoEventsListener을 추가합니다.

mVideoPlayer.addVideoEventsListener(new OnVideoEventsListener() {
    @Override
    public void onPlay() {
        if (isAdDisplayed) {
            for (VideoAdPlayerCallback callback : adCallbacks) {
                callback.onPlay();
            }
        }
    }

    @Override
    public void onResume() {
        if (isAdDisplayed) {
            for (VideoAdPlayerCallback callback : adCallbacks) {
                callback.onResume();
            }
        }
    }

    @Override
    public void onPause() {
        if (isAdDisplayed) {
            for (VideoAdPlayerCallback callback : adCallbacks) {
                callback.onPause();
            }
        }
    }

    @Override
    public void onError() {
        if (isAdDisplayed) {
            for (VideoAdPlayerCallback callback : adCallbacks) {
                callback.onError();
            }
        }
    }
});

광고 동영상이 종료된 경우를 처리하려면 OnVideoCompletedListeneronVideoCompleted() 메서드를 변경합니다.

public void onVideoCompleted() {
    // Handle completed event for playing post-rolls.
    if (isAdDisplayed) {
        for (VideoAdPlayerCallback callback : adCallbacks) {
            callback.onEnded();
        }
    } else {
        if (adsLoader != null) {
            adsLoader.contentComplete();
    }
}

콘텐츠 및 광고 간 전환

이 예에서는 동일한 동영상 플레이어 인스턴스를 사용하여 콘텐츠와 광고를 모두 재생하므로 플레이어에서 콘텐츠와 광고 간에 전환하려면 몇 가지 로직을 추가해야 합니다. 그러면 콘텐츠 동영상을 새로고침하고 탐색하여 광고가 시작된 지점으로 돌아갈 수 있습니다. 이렇게 하려면 함수 두 개를 추가합니다.

private int savedContentPosition = 0;

private void pauseContent() {
    savedContentPosition = videoPlayer.getCurrentPosition();
    videoPlayer.stopPlayback();
    isAdDisplayed = true;
}

private void resumeContent() {
    videoPlayer.setVideoPath(getString(R.string.content_url));
    videoPlayer.seekTo(mSavedContentPosition);
    videoPlayer.play();
    isAdDisplayed = false;
}

이는 각각 VideoFragment.onAdEvent()에서 CONTENT_PAUSE_REQUESTEDCONTENT_RESUME_REQUESTED 이벤트가 수신될 때 호출됩니다.

case CONTENT_PAUSE_REQUESTED:
    pauseContent();
    break;
case CONTENT_RESUME_REQUESTED:
    resumeContent();
    break;

맞춤 광고 재생 사용 설정

마지막 단계는 맞춤 광고 재생을 사용 중임을 SDK에 알리는 것입니다. 다음과 같이 VideoAdPlayerAdDisplayContainer에 전달하면 됩니다.

adDisplayContainer.setPlayer(videoAdPlayer);

플레이어를 setPlayer()에 전달해야 합니다. 그러지 않으면 SDK가 SDK 소유 재생을 사용합니다.

이제 모두 완료되었습니다. 지금까지 맞춤 광고 재생을 IMA 구현에 추가하는 데 필요한 모든 단계를 살펴봤습니다. 문제가 발생하면 GitHub의 고급 예와 자체 구현을 비교해 볼 수 있습니다.