Introdução à extensão de IMA do ExoPlayer

O ExoPlayer é um player de mídia no nível do app para Android. Este guia mostra como usar a extensão do IMA do ExoPlayer, que envolve o SDK de DAI do IMA, para solicitar e reproduzir um stream de mídia com anúncios e conteúdo.

Confira alguns dos benefícios da extensão:

  • Simplifica o código necessário para integrar o IMA aos recursos.
  • Reduz o tempo de desenvolvimento necessário para atualizar para novas versões do IMA.

A extensão do IMA do ExoPlayer oferece suporte aos protocolos de streaming HLS e DASH. Aqui está um resumo:

Suporte a stream de extensão do ExoPlayer-IMA
Transmissões ao vivo Streams de VOD
HLS Marca de seleção Marca de seleção
DASH Marca de seleção Marca de seleção

As transmissões ao vivo do DASH são compatíveis com o ExoPlayer-IMA versão 1.1.0 ou mais recente.

Este guia é baseado no guia do ExoPlayer e mostra como criar um app completo e integrar a extensão. Consulte o ExoPlayerExample do GitHub para conferir um exemplo com um app de exemplo completo.

Pré-requisitos

Criar um novo projeto do Android Studio

Para criar o projeto do Android Studio, siga estas etapas:

  • Inicie o Android Studio.
  • Selecione Iniciar um novo projeto do Android Studio.
  • Na página Choose your project, selecione o modelo No Activity.
  • Clique em Próxima.
  • Na página Configure your project, dê um nome ao projeto e selecione Java como a linguagem.

  • Selecione Concluir.

Adicionar a extensão do IMA do ExoPlayer ao seu projeto

Adicione importações para a extensão ao arquivo build.gradle do aplicativo na seção dependencies.

Configure seu app e ative o multidex. Isso é necessário devido ao tamanho da extensão e para apps com minSdkVersion definido como Android 4.4W (nível 20 da API) ou uma versão anterior.

Veja um exemplo:

app/build.gradle

android {

  ...

  defaultConfig {
      applicationId "com.google.ads.interactivemedia.v3.samples.videoplayerapp"
      minSdkVersion 19
      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'
}

Adicione as permissões de usuário exigidas pelo SDK de DAI do IMA para solicitar anúncios:

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>

Adicionar declarações de intent

Se o app for direcionado ao Android 11 (nível 30 da API) ou versões mais recentes, as versões atuais e recentes do SDK de DAI do IMA vão exigir uma declaração de intenção explícita para abrir links da Web. Adicione o snippet a seguir ao arquivo de manifesto do seu app para ativar as taxas de cliques no anúncio (usuários que clicam no botão Saiba mais).

  <?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>

Configurar a interface do ExoPlayer

Crie o objeto PlayerView que será usado pelo ExoPlayer.

Mude o androidx.constraintlayout.widget.ConstraintLayout para um LinearLayout, o que é recomendado para a extensão de IMA do ExoPlayer.

Veja um exemplo:

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>

Adicionar seus parâmetros de stream

Consulte a página de fluxo de amostra do IMA para ver exemplos de recursos de fluxo para testar seu projeto. Consulte também a seção do Ad Manager sobre DAI para informações sobre como configurar seus próprios streams.

Esta etapa demonstra a configuração de uma transmissão ao vivo, mas a extensão ExoPlayer IMA também oferece suporte a streams VOD para DAI. Consulte a etapa para streams de vídeo on demand (VOD) para saber quais mudanças seu app precisa para processar streams de VOD.

Importar a extensão do IMA do ExoPlayer

Adicione as instruções de importação para a extensão do ExoPlayer.

Adicione as variáveis particulares abaixo a MyActivity.java:

Adicione a chave de recurso HLS do stream Big Buck Bunny (ao vivo) para testar com ela. Mais streams estão disponíveis para teste na página de fluxos de amostra do IMA.

Crie uma constante KEY_ADS_LOADER_STATE para salvar e recuperar o estado AdsLoader.

Veja um exemplo:

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;

}

Criar uma instância adsLoader

Substitua o método onCreate para encontrar o PlayerView e verifique se há um AdsLoader.State salvo, que pode ser usado ao iniciar o objeto adsLoader.

Além disso, ative o multidex se necessário pela contagem de métodos do app e minSdkVersion, conforme explicado na etapa 2.

Veja um exemplo:

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

}

Adicionar métodos para inicializar o player

Adicione um método para inicializar o player e faça o seguinte:

  • Crie uma instância AdsLoader.
  • Crie o ExoPlayer.
  • Crie uma MediaItem com a chave de recurso da transmissão ao vivo.
  • Defina o MediaItem como seu player.

Veja um exemplo:

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

Adicionar um método para liberar o player

Adicione um método para liberar o player nesta sequência:

  • Defina as referências do player como nulas e libere os recursos dele.
  • Libera o estado do 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();
  }

Processar eventos do jogador

Por fim, crie callbacks para os eventos de ciclo de vida da atividade para processar a reprodução do stream.

Para oferecer suporte ao SDK do Android versão 24 ou mais recente:

Para oferecer suporte a versões do SDK do Android anteriores à 24: - onResume() - onPause()

onStart() e onResume() mapeiam para playerView.onResume(), e onStop() e onPause() mapeiam para playerView.onPause().

Esta etapa também usa o evento onSaveInstanceState() para tentar salvar o 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());
    }
  }

  ...

}

Configuração do stream de VOD (opcional)

Se o app for obrigado a reproduzir conteúdo VOD com anúncios, será necessário fazer o seguinte:

  1. Adicione CMS ID e Video ID a um stream de teste de VOD.
  2. Crie um URI de VOD SSAI usando ImaServerSideAdInsertionUriBuilder().
  3. Use esse novo URI como o item de mídia do player.

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

Pronto! Agora você está solicitando e reproduzindo um stream de mídia com a extensão do IMA do ExoPlayer. Confira os exemplos de DAI do Android no GitHub (link em inglês) para ver o código completo.