[ExoPlayer](https://developer.android.com/guide/topics/media/exoplayer) is an Android
media player. This guide shows you how to use the
[ExoPlayer IMA extension](https://github.com/google/ExoPlayer/tree/release-v2/extensions/ima).
This extension uses the IMA DAI SDK to request and play media streams with both
ads and content.

The following lists benefits of the extension:

- Simplifies the code needed to integrate IMA features.
- Reduces the time required to update to new IMA versions.

The ExoPlayer IMA extension supports HLS and DASH streaming protocols. Here's a
summary:

| ExoPlayer-IMA extension stream support |||
|---|---|---|
|   | Livestream | VOD streams |
| HLS | ![Checkmark](https://developers.google.com/static/interactive-media-ads/images/check.png "Checkmark") | ![Checkmark](https://developers.google.com/static/interactive-media-ads/images/check.png "Checkmark") |
| DASH | ![Checkmark](https://developers.google.com/static/interactive-media-ads/images/check.png "Checkmark") | ![Checkmark](https://developers.google.com/static/interactive-media-ads/images/check.png "Checkmark") |

ExoPlayer-IMA version 1.1.0 and later supports DASH live streams.

This guide uses the
[ExoPlayer guide](https://exoplayer.dev/ad-insertion.html#server-side-ad-insertion)
to help you create a full app and integrate the extension. For a complete sample
app, see the
[`ExoPlayerExample` on GitHub](https://github.com/googleads/googleads-ima-android-dai).

> [!IMPORTANT]
> **Key Point:** The ExoPlayer IMA extension requires ExoPlayer. If you prefer a different player, follow the [Get started guide](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/android) to integrate the IMA DAI SDK into your app.

## Prerequisites

- [Android Studio](https://developer.android.com/studio)
- [AndroidX Media3 ExoPlayer version 1.0.0](https://github.com/androidx/media/releases/tag/1.0.0) or later for DAI support.

## Create a new Android Studio project

To create your Android Studio project, follow these steps:

1. Start Android Studio.
2. Select **Start a new Android Studio project**.
3. On the **Choose your project** page, select the **No Activity** template.
4. Click **Next**.
5. On the **Configure your project** page, name your project and select **Java** as the language. Note: The IMA DAI SDK works with Kotlin, but this guide uses Java examples.

- Click **Finish**.

## Add the ExoPlayer IMA extension to your project

To add the ExoPlayer IMA extension, do the following:

1. Include the following imports in the `dependencies` section of your app's
   `build.gradle` file:

   ```kotlin
   dependencies {
       def media3_version = "1.11.0"
       coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
       implementation(platform("org.jetbrains.kotlin:kotlin-bom:2.3.21"))
       implementation("androidx.appcompat:appcompat:1.8.0")
       implementation("androidx.media3:media3-ui:$media3_version")
       implementation("androidx.media3:media3-exoplayer:$media3_version")
       implementation("androidx.media3:media3-exoplayer-hls:$media3_version")
       implementation("androidx.media3:media3-exoplayer-dash:$media3_version")

       // The library adds the IMA ExoPlayer integration for ads.
       implementation("androidx.media3:media3-exoplayer-ima:$media3_version")
   }
   ```
2. Add the user permissions that the IMA DAI SDK needs to request ads:

   ```xml
   <uses-permission android:name="android.permission.INTERNET"/>
   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
   ```

## Set up the ExoPlayer UI

To set up the ExoPlayer UI, do the following:

1. Create the `PlayerView` object for ExoPlayer.

2. Change the `androidx.constraintlayout.widget.ConstraintLayout` view to a
   `LinearLayout` view, as recommended by the ExoPlayer IMA extension:

   ```xml
   <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:tools="http://schemas.android.com/tools"
       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:fitsSystemWindows="true"
           android:layout_width="match_parent"
           android:layout_height="wrap_content" />

       <!-- UI element for viewing SDK event log -->
       <TextView
           android:id="@+id/logText"
           android:gravity="bottom"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:maxLines="100"
           android:scrollbars="vertical"
           android:textSize="@dimen/font_size">
       </TextView>

   </LinearLayout>

   ```

## Add your stream parameters

See the
[IMA sample stream page](https://developers.google.com/interactive-media-ads/docs/sdks/html5/dai/streams)
for sample stream assets to test your project. To set up your own streams, see
the [Ad Manager section on DAI](https://support.google.com/admanager/topic/7062524).

This step sets up a livestream. The ExoPlayer IMA extension also
supports DAI VOD streams. To learn what changes your app needs for VOD streams,
see the
[step for video on demand (VOD) streams](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/android/exoplayer-extension#vod_stream_setup).

## Import the ExoPlayer IMA extension

1. Add the following import statements for the ExoPlayer extension:

   ```java
   import static androidx.media3.common.C.CONTENT_TYPE_HLS;

   import android.annotation.SuppressLint;
   import android.app.Activity;
   import android.net.Uri;
   import android.os.Bundle;
   import android.text.method.ScrollingMovementMethod;
   import android.util.Log;
   import android.widget.TextView;
   import androidx.media3.common.MediaItem;
   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.ui.PlayerView;
   import com.google.ads.interactivemedia.v3.api.AdEvent;
   import com.google.ads.interactivemedia.v3.api.ImaSdkFactory;
   import com.google.ads.interactivemedia.v3.api.ImaSdkSettings;
   import java.util.HashMap;
   import java.util.Map;

   ```
2. In `MyActivity.java`, add these private variables:

   - [`PlayerView`](https://developer.android.com/jetpack/androidx/releases/media3)
   - [`ExoPlayer`](https://developer.android.com/jetpack/androidx/releases/media3)
   - [`ImaServerSideAdInsertionMediaSource.AdsLoader`](https://exoplayer.dev/doc/reference/com/google/android/exoplayer2/ext/ima/ImaServerSideAdInsertionMediaSource.AdsLoader.html)
   - [`ImaServerSideAdInsertionMediaSource.AdsLoader.State`](https://exoplayer.dev/doc/reference/com/google/android/exoplayer2/ext/ima/ImaServerSideAdInsertionMediaSource.AdsLoader.State.html)

   To test with the **Big Buck Bunny (Live)** HLS stream, add its asset key.
   You can find more streams to test on
   [IMA's sample stream page](https://developers.google.com/interactive-media-ads/docs/sdks/html5/dai/streams).
3. Create a `KEY_ADS_LOADER_STATE` constant to save and retrieve the
   `AdsLoader` state:

   ```java
   /** Main Activity. */
   @SuppressLint("UnsafeOptInUsageError")
   /* @SuppressLint is needed for new media3 APIs. */
   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 LOG_TAG = "ImaExoPlayerExample";
     private static final String NETWORK_CODE = "21775744923";

     private PlayerView playerView;
     private TextView logText;
     private ExoPlayer player;
     private ImaServerSideAdInsertionMediaSource.AdsLoader adsLoader;
     private ImaServerSideAdInsertionMediaSource.AdsLoader.State adsLoaderState;
     private ImaSdkSettings imaSdkSettings;

   ```

## Create an `adsLoader` instance

Override the `onCreate` method. In it, find the `PlayerView` and check for a
saved
[`AdsLoader.State`](https://exoplayer.dev/doc/reference/com/google/android/exoplayer2/ext/ima/ImaServerSideAdInsertionMediaSource.AdsLoader.State.html).
You can use this state when you initialize the `adsLoader` object.

```java
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_my);

  // Initialize the IMA SDK as early as possible when the app starts. If your app already
  // overrides Application.onCreate(), call this method inside the onCreate() method.
  // https://developer.android.com/topic/performance/vitals/launch-time#app-creation
  ImaSdkFactory.getInstance().initialize(this, getImaSdkSettings());

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

private ImaSdkSettings getImaSdkSettings() {
  if (imaSdkSettings == null) {
    imaSdkSettings = ImaSdkFactory.getInstance().createImaSdkSettings();
    // Set any IMA SDK settings here.
  }
  return imaSdkSettings;
}

```
**Important:** To improve IMA load times, call the [`ImaSdkFactory.initialize()`](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/android/api/reference/com/google/ads/interactivemedia/v3/api/ImaSdkFactory.md.txt#initialize(android.content.Context,com.google.ads.interactivemedia.v3.api.ImaSdkSettings)) method as early as possible in the app lifecycle. Calling the method early maximizes the time for IMA to load necessary resources before the first stream request. The `ImaSdkFactory.initialize()` method call accepts an `ImaSdkSettings` instance. Make sure to use the same settings values here, and when you create an `AdsLoader` instance. This example uses the `getImaSdkSettings()` helper method to ensure the settings values are the same. For more information, see the [Optimize IMA load time guide\>](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/android/load-time.md.txt).

## Add methods to initialize the player

Add a method to initialize the player. This method must do the following:

- Create an `AdsLoader` instance.
- Create the `ExoPlayer`.
- Create a `MediaItem` using the live stream's asset key.
- Set the `MediaItem` for your player.

```java
// Create a server side ad insertion (SSAI) AdsLoader.
private ImaServerSideAdInsertionMediaSource.AdsLoader createAdsLoader() {
  ImaServerSideAdInsertionMediaSource.AdsLoader.Builder adsLoaderBuilder =
      new ImaServerSideAdInsertionMediaSource.AdsLoader.Builder(this, playerView);

  // Attempts to set the AdsLoader state if available from a previous session.
  if (adsLoaderState != null) {
    adsLoaderBuilder.setAdsLoaderState(adsLoaderState);
  }

  return adsLoaderBuilder
      .setAdEventListener(buildAdEventListener())
      .setImaSdkSettings(getImaSdkSettings())
      .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 a SimpleExoPlayer and set it as the player for content and ads.
  player = new ExoPlayer.Builder(this).setMediaSourceFactory(mediaSourceFactory).build();
  playerView.setPlayer(player);
  adsLoader.setPlayer(player);

  // Create the MediaItem to play, specifying the stream URI.
  Uri ssaiUri = buildLiveStreamUri(SAMPLE_ASSET_KEY, CONTENT_TYPE_HLS);
  MediaItem ssaiMediaItem = MediaItem.fromUri(ssaiUri);

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

/**
 * Builds an IMA SSAI live stream URI for the given asset key and format.
 *
 * @param assetKey The asset key of the live stream.
 * @param format The format of the live stream request, either {@code CONTENT_TYPE_HLS} or {@code
 *     CONTENT_TYPE_DASH}.
 * @return The URI of the live stream.
 */
public Uri buildLiveStreamUri(String assetKey, int format) {
  Map<String, String> adTagParams = new HashMap<String, String>();
  // Update the adTagParams map with any parameters.
  // For more information, see https://support.google.com/admanager/answer/7320899

  return new ImaServerSideAdInsertionUriBuilder()
      .setAssetKey(assetKey)
      .setFormat(format)
      .setNetworkCode(NETWORK_CODE)
      .setAdTagParameters(adTagParams)
      .build();
}

```
**Note:** This section uses methods with names that refer to server-side ad insertion (SSAI) concepts.

- [Google Ad Manager DAI](https://support.google.com/admanager/answer/6147120) is an example of SSAI.
- This guide's ExoPlayer IMA extension works specifically with Ad Manager DAI. The ExoPlayer IMA extension also supports different SSAI solutions.

## Add a method to release the player

Add a method to release the player. This method must perform the following
actions in sequence:

- Set the player references to null and release the player's resources.
- Release the `adsLoader` state.

```java
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

To handle player events, create callbacks for the activity's lifecycle events
to manage stream playback.

For Android API level 24 and later, use the following methods:

- [`onStart()`](https://developer.android.com/reference/android/app/Activity#onStart())
- [`onStop()`](https://developer.android.com/reference/android/app/Activity#onStop())

For Android API levels earlier than 24, use the following methods:

- [`onResume()`](https://developer.android.com/reference/android/app/Activity#onResume())
- [`onPause()`](https://developer.android.com/reference/android/app/Activity#onPause())

The `onStart()` and `onResume()` methods map to `playerView.onResume()`, while
`onStop()` and `onPause()` map to `playerView.onPause()`.

> [!IMPORTANT]
> **Key Point:** This example supports Android SDK version 24+ and earlier versions. Use `onStart()`/`onStop()` for Android SDK version 24+, and `onResume()`/`onPause()` for versions earlier than 24.

This step also uses the
[`onSaveInstanceState()`](https://developer.android.com/reference/android/app/Activity#onSaveInstanceState())
event to save the `adsLoaderState`.

```java
@Override
public void onStart() {
  super.onStart();
  initializePlayer();
  if (playerView != null) {
    playerView.onResume();
  }
}

@Override
public void onResume() {
  super.onResume();
  if (player == null) {
    initializePlayer();
    if (playerView != null) {
      playerView.onResume();
    }
  }
}

@Override
public void onPause() {
  super.onPause();
}

@Override
public void onStop() {
  super.onStop();
  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 needs to play VOD content with ads, follow these steps:

1. Add a `CMS ID` and `Video ID` for a VOD stream. For testing, use these stream parameters:
   - **CMS ID** : `"2548831"`
   - **Video ID** : `"tears-of-steel"`
2. Create an SSAI VOD URI using the `ImaServerSideAdInsertionUriBuilder()` method:

   ```java
   /**
    * Builds an IMA SSAI VOD stream URI for the given CMS ID, video ID, and format.
    *
    * @param cmsId The CMS ID of the VOD stream.
    * @param videoId The video ID of the VOD stream.
    * @param format The format of the VOD stream request, either {@code CONTENT_TYPE_HLS} or {@code
    *     CONTENT_TYPE_DASH}.
    * @return The URI of the VOD stream.
    */
   public Uri buildVodStreamUri(String cmsId, String videoId, int format) {
     Map<String, String> adTagParams = new HashMap<String, String>();
     // Update the adTagParams map with any parameters.
     // For more information, see https://support.google.com/admanager/answer/7320899

     return new ImaServerSideAdInsertionUriBuilder()
         .setContentSourceId(cmsId)
         .setVideoId(videoId)
         .setFormat(format)
         .setNetworkCode(NETWORK_CODE)
         .setAdTagParameters(adTagParams)
         .build();
   }

   ```
3. Set the new VOD stream URI as your player's media item using the
   `MediaItem.fromUri()` method.

If successful, you can request and play a media stream with the ExoPlayer IMA
extension. For the complete example, see
[Android DAI samples on GitHub](https://github.com/googleads/googleads-ima-android).