IMA SDK for Android を使用したカスタム広告再生

IMA SDK for Android を最も迅速かつ簡単にアプリに統合するには、アプリでコンテンツ動画の再生に集中しながら、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 という別のインターフェースが実装されているため、同じように実装する必要があります。この API には getAdProgress() メソッドのみがあり、SDK が広告の再生情報を取得するために使用します。匿名の 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);
}

開始、一時停止、再開

再生状態を追跡するために新しい列挙型を作成し、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 に戻り、匿名の OnVideoEventsListenerSampleVideoPlayer インスタンスに追加します。

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();
    }
}

コンテンツと広告を切り替える

この例では、動画プレーヤーの同じインスタンスを使用してコンテンツと広告の両方を再生しているため、プレーヤーでコンテンツと広告を切り替えるためのロジックを追加する必要があります。その後、コンテンツ動画を再読み込みしてシークし、広告が開始した位置まで戻ります。これを行うには、次の 2 つの関数を追加します。

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_REQUESTED イベントと CONTENT_RESUME_REQUESTED イベントが受信されたときに呼び出されます。

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

カスタム広告再生を有効にする

最後のステップでは、カスタム広告再生を使用していることを SDK に伝えます。これを行うには、VideoAdPlayerAdDisplayContainer に渡します。

adDisplayContainer.setPlayer(videoAdPlayer);

プレーヤーを setPlayer() に渡す必要があります。それ以外の場合、SDK は SDK 所有の再生を使用します。

これで完了です。IMA の実装にカスタム広告再生を追加するために必要な手順は以上です。問題が発生した場合は、独自の実装を GitHub の高度な例と比較できます。