In IMA v3.30.0 and lower, the IMA SDK can handle all of the ad-playing logic, while your app focuses on playing content video. This approach is called "SDK-owned ad playback".
If you want to play ads in your video player, the SDK provides an interface for that. We refer to this approach as "custom ad playback" and the rest of this guide goes over its implementation.
Prerequisites
- A basic IMA integration.
We recommend you look at the Advanced Example on github as a starting point if you do not currently have a basic IMA integration. This example already implements custom ad playback. The remainder of this guide will describe the features necessary for custom ad playback with IMA ads.
Interface with VideoAdPlayer
Custom ad playback requires your app to implement the
VideoAdPlayer
interface. This interface is used by the SDK to notify your app to play ad
videos. Your app also uses this interface to inform the SDK of major video ad
events. Follow these steps to implement the interface.
Create a VideoAdPlayer
The first step is to create an anonymous VideoAdPlayer
class
in requestAds()
:
private VideoAdPlayer videoAdPlayer;
...
private void requestAds(String adTagUrl) {
videoAdPlayer = new VideoAdPlayer() {
};
}
Add video methods
Next, add methods that tell your video player to play, load, stop, and pause ad videos. We also add the methods to release the player and get the volume here:
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();
}
};
These methods are thin wrappers around your video player's own similar methods. Note that these methods set an internal variable that's used to keep track of whether an ad is displayed. In custom ad playback, the video player plays both content video and video ads, so you need to keep track of which is currently displayed.
Ad playback progress
The VideoAdPlayer
interface implements another interface,
AdProgressProvider
, so you must implement it as well. It only has
one method, getAdProgress()
, which is used by the SDK to get
playback information for ads. Add it to your anonymous VideoAdPlayer
class, below the other methods:
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()
returns a VideoProgressUpdate
type, which
must consist of the video's current position and duration. If the player
is not playing an ad, or the duration is unavailable, have it return
VideoProgressUpdate.VIDEO_TIME_NOT_READY
as shown in the example.
Manage video callbacks
Custom ad playback requires your app to inform the SDK of major
video events. From the SDK's view, these are callbacks that are
described by the VideoAdPlayer.VideoAdPlayerCallback
interface.
Before getting into the callback methods themselves, you need to be
able to add and remove the callbacks at the SDK's request. This is
done inside VideoAdPlayer
using addCallback()
and 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);
}
};
This implementation uses a List<>
for the callbacks on which to call
the List<>.add()
and remove()
methods.
Call the callbacks
Now that the SDK has a way to tell your app to add and remove callbacks, define the places where the callback is called. Your app needs to call these callbacks when major video events happen, such as playing, pausing or resuming a video, or when a video finishes or experiences an error.
To do this, expand the SampleVideoPlayer
to have a listener for these
video events that are added from VideoFragment
. The reason to make
a separate listener in SampleVideoPlayer
to call these ad callbacks
is because SampleVideoPlayer
doesn't know anything about ads,
so you must forward its video events to something that can deal with ads.
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);
}
Start, pause, and resume
Create a new enum to keep track of playback state and add new overrides
for the start()
and pause()
methods in SampleVideoPlayer
:
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();
}
}
Handle errors
Override the video player's anonymous error listener you set in 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;
}
Implement the listener
Go back to VideoFragment
and add an anonymous OnVideoEventsListener
to your SampleVideoPlayer
instance:
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();
}
}
}
});
Change the onVideoCompleted()
method of the OnVideoCompletedListener
to handle the case that an ad video finished:
public void onVideoCompleted() {
// Handle completed event for playing post-rolls.
if (isAdDisplayed) {
for (VideoAdPlayerCallback callback : adCallbacks) {
callback.onEnded();
}
} else {
if (adsLoader != null) {
adsLoader.contentComplete();
}
}
Switch between content and ads
This example uses the same instance of the video player to play both content and ads, so you need to add some logic to switch between content and ads in your player. You can then reload and seek the content video to return to the point where the ad started. Add two functions to do this:
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;
}
These are called when the CONTENT_PAUSE_REQUESTED
and
CONTENT_RESUME_REQUESTED
events are received, in
VideoFragment.onAdEvent()
, respectively:
case CONTENT_PAUSE_REQUESTED:
pauseContent();
break;
case CONTENT_RESUME_REQUESTED:
resumeContent();
break;
Enable custom ad playback
The last step is to tell the SDK that you're using custom ad playback.
This is done by passing a VideoAdPlayer
to your AdDisplayContainer
:
adDisplayContainer.setPlayer(videoAdPlayer);
It's necessary to pass your player to setPlayer()
. Otherwise, the
SDK uses SDK-owned playback.
That's it. Those are all the steps needed to add custom ad playback to your IMA implementation. You can compare your own implementation with the Advanced Example on github if you run into issue.