ExoPlayer is an application level media player for Android. This guide shows how you can use the ExoPlayer IMA extension, which wraps the IMA DAI SDK, to request and play a media stream with both ads and content.
Here are some of the extension's benefits:
- Simplifies the code needed to integrate IMA with features.
- Reduces development time needed to update to new versions of IMA.
The ExoPlayer IMA extension supports HLS and DASH streaming protocols. Here's a summary:
ExoPlayer-IMA extension stream support | ||
---|---|---|
Livestream | VOD streams | |
HLS | ||
DASH |
DASH live streams are supported on ExoPlayer-IMA version 1.1.0+.
This guide is based on the ExoPlayer
guide, and shows
how to create a full app and integrate the extension. See the
ExoPlayerExample
from
GitHub for an example with
a complete sample app.
Prerequisites
- Android Studio
- AndroidX Media3 ExoPlayer version 1.0.0 or higher for DAI support.
Create a new Android Studio project
To create your Android Studio project, complete the following steps:
- Start Android Studio.
- Select Start a new Android Studio project.
- In the Choose your project page, select the No Activity template.
- Click Next.
In the Configure your project page, name your project and select Java for the language.
Click Finish.
Add the ExoPlayer IMA extension to your project
Add imports for the extension to the application-level build.gradle file
in the dependencies
section.
Configure your app to and enable
multidex. This is necessary due
to the size of the extension, and is required for apps with minSdkVersion
set
to Android 4.4W (API level
20) or lower.
Here's an example:
app/build.gradle
android { ... defaultConfig { applicationId "com.google.ads.interactivemedia.v3.samples.videoplayerapp" minSdkVersion 21 targetSdkVersion 34 multiDexEnabled true versionCode 1 versionName "1.0" } ... } dependencies { implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.media3:media3-ui:1.1.1' implementation 'androidx.media3:media3-exoplayer:1.1.1' implementation 'androidx.media3:media3-exoplayer-hls:1.1.1' implementation 'androidx.media3:media3-exoplayer-dash:1.1.1' // Adding the ExoPlayer IMA extension for ads will also include the IMA // SDK as a dependency. implementation 'androidx.media3:media3-exoplayer-ima:1.1.1' }
Add the user permissions required by the IMA DAI SDK for requesting ads:
app/src/main/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.project name"> <!-- Required permissions for the IMA DAI SDK --> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> ... </manifest>
Add intent declarations
If your app targets Android 11 (API level 30) or higher, the current and recent versions of the IMA DAI SDK require an explicit declaration of intent to open web links. Add the following snippet to your app's manifest file in order to enable ad clickthroughs (users clicking the Learn more button).
<?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>
Set up ExoPlayer's UI
Create the PlayerView
object to be used by ExoPlayer.
Change the androidx.constraintlayout.widget.ConstraintLayout
to a
LinearLayout
, which is recommended for the ExoPlayer IMA extension.
Here's an example:
app/src/main/res/layout/activity_my.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:background="@android:color/black" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MyActivity" tools:ignore="MergeRootFrame"> <androidx.media3.ui.PlayerView android:id="@+id/player_view" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
Add your stream parameters
See the IMA sample stream page for sample stream assets to test your project. Also see the Ad Manager section on DAI for information on setting up your own streams.
This step demonstrates setting up a live stream, but the ExoPlayer IMA extension also supports DAI VOD streams. See the step for video on demand (VOD) streams to see what changes your app needs to handle VOD streams.
Import the ExoPlayer IMA extension
Add the import statements for the ExoPlayer extension.
Add the following private variables to MyActivity.java
:
PlayerView
ExoPlayer
ImaServerSideAdInsertionMediaSource.AdsLoader
ImaServerSideAdInsertionMediaSource.AdsLoader.State
Add the Big Buck Bunny (Live) HLS stream's asset key to test with this stream. More streams are available to test on IMA's sample stream page.
Create a KEY_ADS_LOADER_STATE
constant to save and retrieve the AdsLoader
state.
Here's an example:
app/src/main/java/com/example/project name/MyActivity.java
import static androidx.media3.common.C.CONTENT_TYPE_HLS; import android.app.Activity; import android.net.Uri; import android.os.Bundle; import androidx.annotation.Nullable; import androidx.annotation.OptIn; import androidx.media3.common.MediaItem; import androidx.media3.common.util.Util; import androidx.media3.datasource.DataSource; import androidx.media3.datasource.DefaultDataSource; import androidx.media3.exoplayer.ExoPlayer; import androidx.media3.exoplayer.ima.ImaServerSideAdInsertionMediaSource; import androidx.media3.exoplayer.ima.ImaServerSideAdInsertionUriBuilder; import androidx.media3.exoplayer.source.DefaultMediaSourceFactory; import androidx.media3.exoplayer.util.EventLogger; import androidx.media3.ui.PlayerView; import androidx.multidex.MultiDex; ... public class MyActivity extends Activity { private static final String KEY_ADS_LOADER_STATE = "ads_loader_state"; private static final String SAMPLE_ASSET_KEY = "c-rArva4ShKVIAkNfy6HUQ"; private PlayerView playerView; private ExoPlayer player; private ImaServerSideAdInsertionMediaSource.AdsLoader adsLoader; private ImaServerSideAdInsertionMediaSource.AdsLoader.State adsLoaderState; }
Create an adsLoader
instance
Overwrite the onCreate
method to find the PlayerView
and check for a saved
AdsLoader.State
,
which can be used when initiating the adsLoader
object.
Also, enable multidex if required by your app's method count and minSdkVersion
(as explained in
step 2).
Here's an example:
app/src/main/java/com/example/project name/MyActivity.java
... public class MyActivity extends Activity { private static final String KEY_ADS_LOADER_STATE = "ads_loader_state"; private static final String SAMPLE_ASSET_KEY = "c-rArva4ShKVIAkNfy6HUQ"; private PlayerView playerView; private ExoPlayer player; private ImaServerSideAdInsertionMediaSource.AdsLoader adsLoader; private ImaServerSideAdInsertionMediaSource.AdsLoader.State adsLoaderState; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my); MultiDex.install(this); playerView = findViewById(R.id.player_view); // Checks if there is a saved AdsLoader state to be used later when // initiating the AdsLoader. if (savedInstanceState != null) { Bundle adsLoaderStateBundle = savedInstanceState.getBundle(KEY_ADS_LOADER_STATE); if (adsLoaderStateBundle != null) { adsLoaderState = ImaServerSideAdInsertionMediaSource.AdsLoader.State.fromBundle( adsLoaderStateBundle); } } } }
Add methods to initialize the player
Add a method to initialize the player and do the following:
- Create an
AdsLoader
instance. - Create the
ExoPlayer
. - Create a
MediaItem
with the live stream's asset key. - Set the
MediaItem
to your player.
Here's an example:
app/src/main/java/com/example/project name/MyActivity.java
public class MyActivity extends Activity { ... // Create a server side ad insertion (SSAI) AdsLoader. private ImaServerSideAdInsertionMediaSource.AdsLoader createAdsLoader() { ImaServerSideAdInsertionMediaSource.AdsLoader.Builder adsLoaderBuilder = new ImaServerSideAdInsertionMediaSource.AdsLoader.Builder(this, playerView); // Attempt to set the AdsLoader state if available from a previous session. if (adsLoaderState != null) { adsLoaderBuilder.setAdsLoaderState(adsLoaderState); } return adsLoaderBuilder.build(); } private void initializePlayer() { adsLoader = createAdsLoader(); // Set up the factory for media sources, passing the ads loader. DataSource.Factory dataSourceFactory = new DefaultDataSource.Factory(this); DefaultMediaSourceFactory mediaSourceFactory = new DefaultMediaSourceFactory(dataSourceFactory); // MediaSource.Factory to create the ad sources for the current player. ImaServerSideAdInsertionMediaSource.Factory adsMediaSourceFactory = new ImaServerSideAdInsertionMediaSource.Factory(adsLoader, mediaSourceFactory); // 'mediaSourceFactory' is an ExoPlayer component for the DefaultMediaSourceFactory. // 'adsMediaSourceFactory' is an ExoPlayer component for a MediaSource factory for IMA server // side inserted ad streams. mediaSourceFactory.setServerSideAdInsertionMediaSourceFactory(adsMediaSourceFactory); // Create an ExoPlayer and set it as the player for content and ads. player = new ExoPlayer.Builder(this).setMediaSourceFactory(mediaSourceFactory).build(); playerView.setPlayer(player); adsLoader.setPlayer(player); // Build an IMA SSAI media item to prepare the player with. Uri ssaiLiveUri = new ImaServerSideAdInsertionUriBuilder() .setAssetKey(SAMPLE_ASSET_KEY) .setFormat(CONTENT_TYPE_HLS) // Use CONTENT_TYPE_DASH for dash streams. .build(); // Create the MediaItem to play, specifying the stream URI. MediaItem ssaiMediaItem = MediaItem.fromUri(ssaiLiveUri); // Prepare the content and ad to be played with the ExoPlayer. player.setMediaItem(ssaiMediaItem); player.prepare(); // Set PlayWhenReady. If true, content and ads will autoplay. player.setPlayWhenReady(false); } }
Add a method to release the player
Add a method to release the player in this sequence:
- Set the player references to null and release the player's resources.
- Release the state of the
adsLoader
.
app/src/main/java/com/example/project name/MyActivity.java
public class MyActivity extends Activity { ... private void releasePlayer() { // Set the player references to null and release the player's resources. playerView.setPlayer(null); player.release(); player = null; // Release the adsLoader state so that it can be initiated again. adsLoaderState = adsLoader.release(); }
Handle player events
Finally, create callbacks for the activity's lifecycle events to handle stream playback.
To support Android SDK version 24+:
To support Android SDK versions lower than 24:
- onResume()
- onPause()
onStart()
and onResume()
map to playerView.onResume()
, and onStop()
and
onPause()
map to playerView.onPause()
.
This step also uses the
onSaveInstanceState()
event to attempt to save the adsLoaderState
.
app/src/main/java/com/example/project name/MyActivity.java
public class MyActivity extends Activity { ... @Override public void onStart() { super.onStart(); if (Util.SDK_INT > 23) { initializePlayer(); if (playerView != null) { playerView.onResume(); } } } @Override public void onResume() { super.onResume(); if (Util.SDK_INT <= 23 || player == null) { initializePlayer(); if (playerView != null) { playerView.onResume(); } } } @Override public void onPause() { super.onPause(); if (Util.SDK_INT <= 23) { if (playerView != null) { playerView.onPause(); } releasePlayer(); } } @Override public void onStop() { super.onStop(); if (Util.SDK_INT > 23) { if (playerView != null) { playerView.onPause(); } releasePlayer(); } } @Override public void onSaveInstanceState(Bundle outState) { // Attempts to save the AdsLoader state to handle app backgrounding. if (adsLoaderState != null) { outState.putBundle(KEY_ADS_LOADER_STATE, adsLoaderState.toBundle()); } } ... }
VOD stream setup (optional)
If your app is required to play VOD content with ads, you will need to do the following:
- Add a
CMS ID
andVideo ID
for a VOD test stream. - Create an SSAI VOD URI using
ImaServerSideAdInsertionUriBuilder()
. - Use this new URI as your player's media item.
app/src/main/java/com/example/project name/MyActivity.java
public class MyActivity extends Activity { private static final String KEY_ADS_LOADER_STATE = "ads_loader_state"; private static final String SAMPLE_ASSET_KEY = "c-rArva4ShKVIAkNfy6HUQ"; private static final String SAMPLE_CMS_ID = "2548831"; private static final String SAMPLE_VIDEO_ID = "tears-of-steel"; ... private void initializePlayer() { ... Uri ssaiVodUri = new ImaServerSideAdInsertionUriBuilder() .setContentSourceId(SAMPLE_CMS_ID) .setVideoId(SAMPLE_VIDEO_ID) .setFormat(CONTENT_TYPE_HLS) .build(); // Create the MediaItem to play, specifying the stream URI. MediaItem ssaiMediaItem = MediaItem.fromUri(ssaiVodUri); // Prepare the content and ad to be played with the ExoPlayer. player.setMediaItem(ssaiMediaItem); player.prepare(); // Set PlayWhenReady. If true, content and ads will autoplay. player.setPlayWhenReady(false); }
That's it! You're now requesting and playing a media stream with the ExoPlayer IMA extension. Take a look at Android DAI samples on GitHub for the complete code.