使用 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,因此您也必須實作其他介面。此介面只有一種 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 的要求中新增及移除回呼。請在 VideoAdPlayer 中使用 addCallback()removeCallback() 執行此操作:

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<> 做為回呼,以便呼叫 List<>.add()remove() 方法。

呼叫回呼

現在 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);
}

開始、暫停及繼續

建立新的列舉以追蹤播放狀態,並為 SampleVideoPlayer 中的 start()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 並將匿名的 OnVideoEventsListener 新增至 SampleVideoPlayer 執行個體:

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

系統會在收到 CONTENT_PAUSE_REQUESTEDCONTENT_RESUME_REQUESTED 事件時分別在 VideoFragment.onAdEvent() 中呼叫這些事件:

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

啟用自訂廣告播放

最後一步是告知 SDK 您使用的是自訂廣告播放。方法是將 VideoAdPlayer 傳遞至 AdDisplayContainer

adDisplayContainer.setPlayer(videoAdPlayer);

而是將播放器傳遞至 setPlayer()。否則,SDK 會使用 SDK 擁有的播放功能。

就是這麼簡單!這些是將自訂廣告播放加到 IMA 導入作業的所有步驟。如果發生問題,您可以比較自己的實作與 GitHub 上的進階範例