Как настроить IMA SDK для динамической вставки объявлений

Выберите платформу: HTML5 Android iOS tvOS Cast Roku

IMA SDK позволяет легко интегрировать мультимедийные объявления на сайты и в приложения. IMA SDK могут запрашивать объявления у любого совместимого с VAST сервера объявлений и управлять воспроизведением рекламы в ваших приложениях. При использовании IMA DAI SDK приложения отправляют запрос на показ рекламы и видеоконтента (видео по запросу или прямых трансляций). Затем SDK возвращает объединенный видеопоток, чтобы вам не приходилось переключаться между видеообъявлением и контентом в приложении.

Выберите решение для динамической вставки объявлений

Динамическая вставка объявлений с полной поддержкой

В этом руководстве рассказывается, как интегрировать IMA DAI SDK в простой видеопроигрыватель. Если вы хотите посмотреть или повторить пример интеграции, скачайте BasicExample с GitHub.

Общие сведения о динамической вставке объявлений с помощью IMA SDK

Внедрение IMA DAI включает четыре основных компонента SDK, как показано в этом руководстве:

  • StreamDisplayContainer: Объект-контейнер, который находится над элементом воспроизведения видео и содержит элементы интерфейса объявления.
  • AdsLoader: объект, который запрашивает потоки и обрабатывает события, вызванные объектами ответа на запрос потока. Вам нужно создать только один экземпляр загрузчика объявлений, который можно использовать повторно в течение всего жизненного цикла приложения.
  • StreamRequest: объект, определяющий запрос потока. Запросы потока могут быть для видео по запросу или прямых трансляций. В запросах трансляций указывается ключ объекта, а в запросах видео по запросу – идентификатор CMS и идентификатор видео. В запросах обоих типов можно указать ключ API, необходимый для доступа к определенным потокам, и код сети Google Менеджера рекламы, чтобы IMA SDK обрабатывал идентификаторы объявлений в соответствии с настройками Google Менеджера рекламы.
  • StreamManager: Объект, который обрабатывает потоки динамической вставки объявлений и взаимодействия с серверной частью DAI. Менеджер потока также обрабатывает пинги отслеживания и пересылает издателю события потока и объявлений.

Требования

  • Android Studio
  • Пример приложения с видеопроигрывателем для интеграции SDK

Скачайте и запустите пример приложения видеопроигрывателя

В примере приложения есть рабочий видеопроигрыватель, который воспроизводит видео HLS. Используйте его в качестве отправной точки для интеграции функций DAI из IMA DAI SDK.

  1. Скачайте пример приложения видеопроигрывателя и извлеките его.

  2. Запустите Android Studio и выберите Open an existing Android Studio project (Открыть существующий проект Android Studio). Если Android Studio уже запущена, выберите File > New > Import Project (Файл > Создать > Импортировать проект). Затем выберите SampleVideoPlayer/build.gradle.

  3. Запустите синхронизацию Gradle, выбрав Tools (Инструменты) > Android > Sync Project with Gradle Files (Синхронизировать проект с файлами Gradle).

  4. Убедитесь, что приложение проигрывателя компилируется и запускается на физическом устройстве Android или виртуальном устройстве Android с помощью команды Run > Run 'app' (Запустить > Запустить приложение). Для загрузки видеопотока перед воспроизведением может потребоваться некоторое время.

Изучите образец видеопроигрывателя

В образце видеопроигрывателя пока нет кода интеграции IMA DAI SDK. Пример приложения состоит из двух основных частей:

  1. samplevideoplayer/SampleVideoPlayer.java: проигрыватель HLS на основе ExoPlayer, который используется для интеграции IMA DAI.

  2. videoplayerapp/MyActivity.java: это действие создает видеопроигрыватель и передает ему Context и media3.ui.PlayerView.

Как добавить IMA DAI SDK в приложение проигрывателя

Также необходимо добавить ссылку на IMA DAI SDK. В Android Studio добавьте в файл build.gradle на уровне приложения, расположенный в каталоге app/build.gradle, следующие строки: Для IMA SDK требуется включить десахаризацию библиотеки. Для этого нужно задать coreLibraryDesugaringEnabled true и добавить 'com.android.tools:desugar_jdk_libs' в качестве зависимости в файле build.gradle. Подробнее о Java 11+ API, доступных через десахаризацию со спецификацией nio…

repositories {
    google()
    mavenCentral()
}

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")
    implementation("androidx.mediarouter:mediarouter:1.8.1")
    implementation("com.google.ads.interactivemedia.v3:interactivemedia:3.40.0")
}

Интегрируйте IMA DAI SDK

  1. Создайте новый класс SampleAdsWrapper в пакете videoplayerapp (в app/java/com.google.ads.interactivemedia.v3.samples/videoplayerapp/), чтобы упаковать существующий SampleVideoPlayer и добавить логику, реализующую IMA DAI. Для этого сначала нужно создать AdsLoader, который будет использоваться для запроса трансляции с динамической вставкой объявлений.

    В этом фрагменте кода приведены примеры параметров для потоков HLS и DASH, а также для прямых трансляций и видео по запросу. Чтобы указать, какой поток воспроизводится, обновите переменную CONTENT_TYPE.

    package com.google.ads.interactivemedia.v3.samples.videoplayerapp;
    
    import android.annotation.SuppressLint;
    import android.content.Context;
    import android.view.ViewGroup;
    import android.webkit.WebView;
    import androidx.annotation.Nullable;
    import com.google.ads.interactivemedia.v3.api.AdErrorEvent;
    import com.google.ads.interactivemedia.v3.api.AdEvent;
    import com.google.ads.interactivemedia.v3.api.AdsLoader;
    import com.google.ads.interactivemedia.v3.api.AdsManagerLoadedEvent;
    import com.google.ads.interactivemedia.v3.api.CuePoint;
    import com.google.ads.interactivemedia.v3.api.ImaSdkFactory;
    import com.google.ads.interactivemedia.v3.api.StreamDisplayContainer;
    import com.google.ads.interactivemedia.v3.api.StreamManager;
    import com.google.ads.interactivemedia.v3.api.StreamRequest;
    import com.google.ads.interactivemedia.v3.api.StreamRequest.StreamFormat;
    import com.google.ads.interactivemedia.v3.api.player.VideoProgressUpdate;
    import com.google.ads.interactivemedia.v3.api.player.VideoStreamPlayer;
    import com.google.ads.interactivemedia.v3.samples.samplevideoplayer.SampleVideoPlayer;
    import com.google.ads.interactivemedia.v3.samples.samplevideoplayer.SampleVideoPlayer.SampleVideoPlayerCallback;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    
    /** This class adds ad-serving support to Sample HlsVideoPlayer */
    @SuppressLint("UnsafeOptInUsageError")
    /* @SuppressLint is needed for new media3 APIs. */
    public class SampleAdsWrapper
        implements AdEvent.AdEventListener, AdErrorEvent.AdErrorListener, AdsLoader.AdsLoadedListener {
    
      // Live HLS stream asset key.
      private static final String TEST_HLS_ASSET_KEY = "c-rArva4ShKVIAkNfy6HUQ";
    
      // Live DASH stream asset key.
      private static final String TEST_DASH_ASSET_KEY = "PSzZMzAkSXCmlJOWDmRj8Q";
    
      // VOD HLS content source and video IDs.
      private static final String TEST_HLS_CONTENT_SOURCE_ID = "2548831";
      private static final String TEST_HLS_VIDEO_ID = "tears-of-steel";
    
      // VOD DASH content source and video IDs.
      private static final String TEST_DASH_CONTENT_SOURCE_ID = "2559737";
      private static final String TEST_DASH_VIDEO_ID = "tos-dash";
    
      private static final String NETWORK_CODE = "21775744923";
    
      private static final String PLAYER_TYPE = "DAISamplePlayer";
    
      private enum ContentType {
        LIVE_HLS,
        LIVE_DASH,
        VOD_HLS,
        VOD_DASH,
      }
    
      // Set CONTENT_TYPE to the associated enum for the stream type you would like to test.
      private static final ContentType CONTENT_TYPE = ContentType.VOD_HLS;
    
      /** Log interface, so we can output the log commands to the UI or similar. */
      public interface Logger {
        void log(String logMessage);
      }
    
      private final ImaSdkFactory sdkFactory;
      private AdsLoader adsLoader;
      private StreamManager streamManager;
      private final List<VideoStreamPlayer.VideoStreamPlayerCallback> playerCallbacks;
    
      private final SampleVideoPlayer videoPlayer;
      private final Context context;
      private final ViewGroup adUiContainer;
    
      private String fallbackUrl;
      private Logger logger;
    
      /**
       * Creates a new SampleAdsWrapper that implements IMA direct-ad-insertion.
       *
       * @param context the app's context.
       * @param videoPlayer underlying HLS video player.
       * @param adUiContainer ViewGroup in which to display the ad's UI.
       */
      public SampleAdsWrapper(Context context, SampleVideoPlayer videoPlayer, ViewGroup adUiContainer) {
        this.videoPlayer = videoPlayer;
        this.context = context;
        this.adUiContainer = adUiContainer;
        sdkFactory = ImaSdkFactory.getInstance();
        playerCallbacks = new ArrayList<>();
        createAdsLoader();
      }
    
      private void enableWebViewDebugging() {
        WebView.setWebContentsDebuggingEnabled(true);
      }
    
      private void createAdsLoader() {
        enableWebViewDebugging();
        VideoStreamPlayer videoStreamPlayer = createVideoStreamPlayer();
        StreamDisplayContainer displayContainer =
            ImaSdkFactory.createStreamDisplayContainer(adUiContainer, videoStreamPlayer);
        videoPlayer.setSampleVideoPlayerCallback(createSampleVideoPlayerCallback());
        adsLoader =
            sdkFactory.createAdsLoader(context, MyActivity.getImaSdkSettings(), displayContainer);
      }
    
      public void requestAndPlayAds() {
        adsLoader.addAdErrorListener(this);
        adsLoader.addAdsLoadedListener(this);
        adsLoader.requestStream(buildStreamRequest());
      }
    
  2. Создайте вспомогательный метод createSampleVideoPlayerCallback() для создания экземпляра интерфейса SampleVideoPlayerCallback, который расширяет VideoStreamPlayer.VideoStreamPlayerCallback.

    Чтобы использовать DAI, проигрыватель должен передавать события ID3 в IMA DAI SDK. В приведенном ниже примере кода это делается с помощью метода callback.onUserTextReceived().

    private SampleVideoPlayerCallback createSampleVideoPlayerCallback() {
      return new SampleVideoPlayerCallback() {
        @Override
        public void onUserTextReceived(String userText) {
          for (VideoStreamPlayer.VideoStreamPlayerCallback callback : playerCallbacks) {
            callback.onUserTextReceived(userText);
          }
        }
    
        @Override
        public void onSeek(int windowIndex, long positionMs) {
          // See if we would seek past an ad, and if so, jump back to it.
          long newSeekPositionMs = positionMs;
          if (streamManager != null) {
            CuePoint prevCuePoint = streamManager.getPreviousCuePointForStreamTimeMs(positionMs);
            if (prevCuePoint != null && !prevCuePoint.isPlayed()) {
              newSeekPositionMs = prevCuePoint.getStartTimeMs();
            }
          }
          videoPlayer.seekTo(windowIndex, newSeekPositionMs);
        }
    
        @Override
        public void onContentComplete() {
          for (VideoStreamPlayer.VideoStreamPlayerCallback callback : playerCallbacks) {
            callback.onContentComplete();
          }
        }
    
        @Override
        public void onPause() {
          for (VideoStreamPlayer.VideoStreamPlayerCallback callback : playerCallbacks) {
            callback.onPause();
          }
        }
    
        @Override
        public void onResume() {
          for (VideoStreamPlayer.VideoStreamPlayerCallback callback : playerCallbacks) {
            callback.onResume();
          }
        }
    
        @Override
        public void onVolumeChanged(int percentage) {
          for (VideoStreamPlayer.VideoStreamPlayerCallback callback : playerCallbacks) {
            callback.onVolumeChanged(percentage);
          }
        }
      };
    }
    
  3. Добавьте метод buildStreamRequest(), чтобы создать SteamRequest. Этот метод переключается между разными потоками в зависимости от того, как вы задали переменную CONTENT_TYPE. В этом руководстве используется поток VOD HLS из IMA.

    @Nullable
    private StreamRequest buildStreamRequest() {
      StreamRequest request;
      switch (CONTENT_TYPE) {
        case LIVE_HLS:
          // Live HLS stream request.
          return sdkFactory.createLiveStreamRequest(TEST_HLS_ASSET_KEY, null, NETWORK_CODE);
        case LIVE_DASH:
          // Live DASH stream request.
          return sdkFactory.createLiveStreamRequest(TEST_DASH_ASSET_KEY, null, NETWORK_CODE);
        case VOD_HLS:
          // VOD HLS request.
          request =
              sdkFactory.createVodStreamRequest(
                  TEST_HLS_CONTENT_SOURCE_ID, TEST_HLS_VIDEO_ID, null, NETWORK_CODE);
          request.setFormat(StreamFormat.HLS);
          return request;
        case VOD_DASH:
          // VOD DASH request.
          request =
              sdkFactory.createVodStreamRequest(
                  TEST_DASH_CONTENT_SOURCE_ID, TEST_DASH_VIDEO_ID, null, NETWORK_CODE);
          request.setFormat(StreamFormat.DASH);
          return request;
      }
      // Content type not selected.
      return null;
    }
    
  4. Для воспроизведения потока также нужен VideoStreamPlayer, поэтому добавьте метод createVideoStreamPlayer(), который создает анонимный класс, реализующий VideoStreamPlayer.

    private VideoStreamPlayer createVideoStreamPlayer() {
      return new VideoStreamPlayer() {
        @Override
        public void loadUrl(String url, List<HashMap<String, String>> subtitles) {
          videoPlayer.setStreamUrl(url);
          videoPlayer.play();
        }
    
        @Override
        public void pause() {
          // Pause player.
          videoPlayer.pause();
        }
    
        @Override
        public void resume() {
          // Resume player.
          videoPlayer.play();
        }
    
        @Override
        public int getVolume() {
          // Make the video player play at the current device volume.
          return 100;
        }
    
        @Override
        public void addCallback(VideoStreamPlayerCallback videoStreamPlayerCallback) {
          playerCallbacks.add(videoStreamPlayerCallback);
        }
    
        @Override
        public void removeCallback(VideoStreamPlayerCallback videoStreamPlayerCallback) {
          playerCallbacks.remove(videoStreamPlayerCallback);
        }
    
        @Override
        public void onAdBreakStarted() {
          // Disable player controls.
          videoPlayer.enableControls(false);
          log("Ad Break Started\n");
        }
    
        @Override
        public void onAdBreakEnded() {
          // Re-enable player controls.
          if (videoPlayer != null) {
            videoPlayer.enableControls(true);
          }
          log("Ad Break Ended\n");
        }
    
        @Override
        public void onAdPeriodStarted() {
          log("Ad Period Started\n");
        }
    
        @Override
        public void onAdPeriodEnded() {
          log("Ad Period Ended\n");
        }
    
        @Override
        public void seek(long timeMs) {
          // An ad was skipped. Skip to the content time.
          videoPlayer.seekTo(timeMs);
          log("seek");
        }
    
        @Override
        public VideoProgressUpdate getContentProgress() {
          return new VideoProgressUpdate(
              videoPlayer.getCurrentPositionMs(), videoPlayer.getDuration());
        }
      };
    }
    
  5. Реализуйте необходимые прослушиватели и добавьте поддержку обработки ошибок.

    Обратите внимание на реализацию AdErrorListener, поскольку она вызывает резервный URL, если не удается воспроизвести рекламу. Поскольку контент и реклама находятся в одном потоке, вы должны быть готовы вызвать резервный поток, если в потоке с динамической вставкой объявлений произойдет ошибка.

    /** AdErrorListener implementation */
    @Override
    public void onAdError(AdErrorEvent event) {
      log(String.format("Error: %s\n", event.getError().getMessage()));
      // play fallback URL.
      log("Playing fallback Url\n");
      videoPlayer.setStreamUrl(fallbackUrl);
      videoPlayer.enableControls(true);
      videoPlayer.play();
    }
    
    /** AdEventListener implementation */
    @Override
    public void onAdEvent(AdEvent event) {
      switch (event.getType()) {
        case AD_PROGRESS:
          // Do nothing or else log will be filled by these messages.
          break;
        default:
          log(String.format("Event: %s\n", event.getType()));
          break;
      }
    }
    
    /** AdsLoadedListener implementation */
    @Override
    public void onAdsManagerLoaded(AdsManagerLoadedEvent event) {
      streamManager = event.getStreamManager();
      streamManager.addAdErrorListener(this);
      streamManager.addAdEventListener(this);
      streamManager.init();
    }
    
    /** Sets fallback URL in case ads stream fails. */
    void setFallbackUrl(String url) {
      fallbackUrl = url;
    }
    
  6. Добавьте код для ведения журнала.

    /** Sets logger for displaying events to screen. Optional. */
    void setLogger(Logger logger) {
      this.logger = logger;
    }
    
    private void log(String message) {
      if (logger != null) {
        logger.log(message);
      }
    }
  7. Измените метод MyActivity в классе videoplayerapp, чтобы создать экземпляр и вызвать метод SampleAdsWrapper. Также вызовите ImaSdkFactory.initialize(), используя вспомогательный метод для создания экземпляра ImaSdkSettings.

    package com.google.ads.interactivemedia.v3.samples.videoplayerapp;
    
    import android.annotation.SuppressLint;
    import android.app.Activity;
    import android.content.res.Configuration;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.widget.ImageButton;
    import android.widget.ScrollView;
    import android.widget.TextView;
    import com.google.ads.interactivemedia.v3.api.ImaSdkFactory;
    import com.google.ads.interactivemedia.v3.api.ImaSdkSettings;
    import com.google.ads.interactivemedia.v3.samples.samplevideoplayer.SampleVideoPlayer;
    
    /** Main Activity that plays media using {@link SampleVideoPlayer}. */
    @SuppressLint("UnsafeOptInUsageError")
    /* @SuppressLint is needed for new media3 APIs. */
    public class MyActivity extends Activity {
    
      private static final String DEFAULT_STREAM_URL =
          "https://storage.googleapis.com/interactive-media-ads/media/bbb.m3u8";
      private static final String APP_LOG_TAG = "ImaDaiExample";
      private static final String PLAYER_TYPE = "DAISamplePlayer";
      private static ImaSdkSettings imaSdkSettings;
    
      protected SampleVideoPlayer sampleVideoPlayer;
      protected ImageButton playButton;
    
      private boolean contentHasStarted = false;
    
      @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());
    
        View rootView = findViewById(R.id.videoLayout);
        sampleVideoPlayer =
            new SampleVideoPlayer(rootView.getContext(), rootView.findViewById(R.id.playerView));
        sampleVideoPlayer.enableControls(false);
        playButton = rootView.findViewById(R.id.playButton);
        final SampleAdsWrapper sampleAdsWrapper =
            new SampleAdsWrapper(this, sampleVideoPlayer, rootView.findViewById(R.id.adUiContainer));
        sampleAdsWrapper.setFallbackUrl(DEFAULT_STREAM_URL);
    
        final ScrollView scrollView = findViewById(R.id.logScroll);
        final TextView textView = findViewById(R.id.logText);
    
        sampleAdsWrapper.setLogger(
            logMessage -> {
              Log.i(APP_LOG_TAG, logMessage);
              if (textView != null) {
                textView.append(logMessage);
              }
              if (scrollView != null) {
                scrollView.post(() -> scrollView.fullScroll(View.FOCUS_DOWN));
              }
            });
    
        // Set up play button listener to play video then hide play button.
        playButton.setOnClickListener(
            view -> {
              if (contentHasStarted) {
                sampleVideoPlayer.play();
              } else {
                contentHasStarted = true;
                sampleVideoPlayer.enableControls(true);
                sampleAdsWrapper.requestAndPlayAds();
              }
              playButton.setVisibility(View.GONE);
            });
        orientVideoDescription(getResources().getConfiguration().orientation);
      }
    
  8. Добавьте вспомогательный метод getImaSdkSettings(), чтобы создать экземпляр ImaSdkSettings.

    public static ImaSdkSettings getImaSdkSettings() {
      if (imaSdkSettings == null) {
        imaSdkSettings = ImaSdkFactory.getInstance().createImaSdkSettings();
        imaSdkSettings.setPlayerType(PLAYER_TYPE);
        // Set any additional IMA SDK settings here.
      }
      return imaSdkSettings;
    }
  9. Измените файл макета Activity activity_my.xml, чтобы добавить элементы интерфейса для регистрации.

    <!-- UI element for viewing SDK event log -->
    <ScrollView
        android:id="@+id/logScroll"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.5"
        android:padding="5dp"
        android:background="#DDDDDD">
    
        <TextView
            android:id="@+id/logText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
        </TextView>
    </ScrollView>

Поздравляем! Теперь вы можете запрашивать и показывать видеообъявления в приложении для Android. Чтобы настроить реализацию, ознакомьтесь с информацией о закладках, возврате к началу и документации по API.

Устранение неполадок

Если у вас возникли проблемы с воспроизведением видеообъявления, попробуйте скачать готовый пример BasicExample. Если в BasicExample все работает правильно, то, скорее всего, проблема в коде интеграции IMA в вашем приложении.

Если у вас по-прежнему возникают проблемы, посетите форум IMA SDK.