ЗОЖ

ExoPlayer поддерживает HLS с несколькими форматами контейнеров. Также должны поддерживаться содержащиеся в нём форматы аудио- и видеоматериалов (подробнее см. в разделе «Форматы материалов» ). Мы настоятельно рекомендуем производителям HLS-контента создавать высококачественные потоки HLS, как описано в этой записи блога .

Особенность Поддерживается Комментарии
Контейнеры
MPEG-TS ДА
FMP4/CMAF ДА
ADTS (AAC) ДА
МП3 ДА
Скрытые субтитры
CEA-608 ДА
CEA-708 ДА
ВебВТТ ДА
Метаданные
ID3 ДА
СКТЭ-35 НЕТ
Защита контента
АЕС-128 ДА
Образец АЕС-128 НЕТ
Уайдвайн ДА API 19+ (схема «cenc») и 25+ (схема «cbcs»)
PlayReady SL2000 ДА только Android TV
Управление сервером
Дельта-обновления ДА
Блокировка перезагрузки плейлиста ДА
Блокировка загрузки подсказок предварительной загрузки ДА За исключением байтовых диапазонов с неопределенной длиной.
Вставка рекламы
Вставка рекламы под управлением сервера (межстраничная реклама) Частично Только VOD с X-ASSET-URI . Прямые трансляции и X-ASSET-LIST будут добавлены позже.
Реклама IMA на стороне сервера и клиента ДА Руководство по вставке рекламы
Живое воспроизведение
Регулярное живое воспроизведение ДА
HLS с низкой задержкой (Apple) ДА
HLS с низкой задержкой (Сообщество) НЕТ
Общие данные клиента медиа CMCD ДА Руководство по интеграции CMCD

Использование MediaItem

Для воспроизведения потока HLS вам необходим модуль HLS.

Котлин

implementation("androidx.media3:media3-exoplayer-hls:1.7.1")

Круто

implementation "androidx.media3:media3-exoplayer-hls:1.7.1"

Затем вы можете создать MediaItem для URI плейлиста HLS и передать его проигрывателю.

Котлин

// Create a player instance.
val player = ExoPlayer.Builder(context).build()
// Set the media item to be played.
player.setMediaItem(MediaItem.fromUri(hlsUri))
// Prepare the player.
player.prepare()

Ява

// Create a player instance.
ExoPlayer player = new ExoPlayer.Builder(context).build();
// Set the media item to be played.
player.setMediaItem(MediaItem.fromUri(hlsUri));
// Prepare the player.
player.prepare();

Если ваш URI не заканчивается на .m3u8 , вы можете передать MimeTypes.APPLICATION_M3U8 в setMimeType MediaItem.Builder , чтобы явно указать тип содержимого.

URI медиа-элемента может указывать либо на список воспроизведения медиа-файлов, либо на многовариантный список воспроизведения. Если URI указывает на многовариантный список воспроизведения, в котором объявлено несколько тегов #EXT-X-STREAM-INF , ExoPlayer автоматически адаптируется к вариантам с учётом доступной пропускной способности и возможностей устройства.

Использование HlsMediaSource

Для дополнительных возможностей настройки вы можете создать HlsMediaSource и передать его непосредственно проигрывателю вместо MediaItem .

Котлин

// Create a data source factory.
val dataSourceFactory: DataSource.Factory = DefaultHttpDataSource.Factory()
// Create a HLS media source pointing to a playlist uri.
val hlsMediaSource =
  HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(hlsUri))
// Create a player instance.
val player = ExoPlayer.Builder(context).build()
// Set the HLS media source as the playlist with a single media item.
player.setMediaSource(hlsMediaSource)
// Prepare the player.
player.prepare()

Ява

// Create a data source factory.
DataSource.Factory dataSourceFactory = new DefaultHttpDataSource.Factory();
// Create a HLS media source pointing to a playlist uri.
HlsMediaSource hlsMediaSource =
    new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(hlsUri));
// Create a player instance.
ExoPlayer player = new ExoPlayer.Builder(context).build();
// Set the HLS media source as the playlist with a single media item.
player.setMediaSource(hlsMediaSource);
// Prepare the player.
player.prepare();

Доступ к манифесту

Вы можете получить текущий манифест, вызвав Player.getCurrentManifest . Для HLS необходимо привести возвращаемый объект к HlsManifest . Обратный вызов onTimelineChanged метода Player.Listener также вызывается при каждой загрузке манифеста. Это происходит один раз для контента по запросу и, возможно, несколько раз для контента в режиме реального времени. Следующий фрагмент кода показывает, как приложение может выполнять какие-либо действия при каждой загрузке манифеста.

Котлин

player.addListener(
  object : Player.Listener {
    override fun onTimelineChanged(timeline: Timeline, @TimelineChangeReason reason: Int) {
      val manifest = player.currentManifest
      if (manifest is HlsManifest) {
        // Do something with the manifest.
      }
    }
  }
)

Ява

player.addListener(
    new Player.Listener() {
      @Override
      public void onTimelineChanged(
          Timeline timeline, @Player.TimelineChangeReason int reason) {
        Object manifest = player.getCurrentManifest();
        if (manifest != null) {
          HlsManifest hlsManifest = (HlsManifest) manifest;
          // Do something with the manifest.
        }
      }
    });

Воспроизведение потоков HLS с полноэкранной вставкой

Спецификация HLS определяет полноэкранные объявления HLS, которые можно использовать для включения информации в медиаплейлист. ExoPlayer по умолчанию игнорирует такие объявления. Поддержку можно добавить с помощью HlsInterstitialsAdsLoader . Мы не поддерживаем все функции спецификации изначально. Если вам не хватает поддержки для вашего потока, сообщите нам об этом , опубликовав issue на GitHub и отправив URI вашего потока, чтобы мы могли добавить поддержку для вашего потока.

Используйте MediaItem с API плейлиста

Самый удобный способ воспроизведения HLS-потоков с полноэкранной вставкой — создание экземпляра ExoPlayer с HlsInterstitialsAdsLoader.AdsMediaSourceFactory . Это позволяет использовать API плейлистов на основе MediaItem из интерфейса Player для воспроизведения полноэкранной вставки HLS.

MediaSource.Factory ExoPlayer можно внедрить в конструктор при создании экземпляра проигрывателя:

Котлин

hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(context)
// Create a MediaSource.Factory for HLS streams with interstitials.
var hlsMediaSourceFactory =
  HlsInterstitialsAdsLoader.AdsMediaSourceFactory(
    hlsInterstitialsAdsLoader,
    playerView,
    DefaultMediaSourceFactory(context),
  )

// Build player with interstitials media source factory
player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(hlsMediaSourceFactory)
    .build()

// Set the player on the ads loader.
hlsInterstitialsAdsLoader.setPlayer(player)
playerView.setPlayer(player)

Ява

hlsInterstitialsAdsLoader = new HlsInterstitialsAdsLoader(context);
// Create a MediaSource.Factory for HLS streams with interstitials.
MediaSource.Factory hlsMediaSourceFactory =
      new HlsInterstitialsAdsLoader.AdsMediaSourceFactory(
          hlsInterstitialsAdsLoader, playerView, new DefaultMediaSourceFactory(context));

// Build player with interstitials media source factory
player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(hlsMediaSourceFactory)
        .build();

// Set the player on the ads loader.
hlsInterstitialsAdsLoader.setPlayer(player);
playerView.setPlayer(player);

При такой настройке проигрывателя воспроизведение полноэкранной рекламы HLS сводится к настройке медиа-элемента с помощью AdsConfiguration на проигрывателе:

Котлин

player.setMediaItem(
  MediaItem.Builder()
    .setUri("https://www.example.com/media.m3u8")
    .setAdsConfiguration(
      AdsConfiguration.Builder(Uri.parse("hls://interstitials"))
        .setAdsId("ad-tag-0") // must be unique within playlist
        .build())
    .build())

player.prepare();
player.play();

Ява

player.setMediaItem(
    new MediaItem.Builder()
        .setUri("https://www.example.com/media.m3u8")
        .setAdsConfiguration(
            new AdsConfiguration.Builder(Uri.parse("hls://interstitials"))
                .setAdsId("ad-tag-0") // must be unique within playlist
                .build())
        .build());
player.prepare();
player.play();

Используйте API на основе медиа-источников

В качестве альтернативы, экземпляр ExoPlayer можно создать без переопределения фабрики источников медиа по умолчанию. Для поддержки полноэкранной рекламы приложение может напрямую использовать HlsInterstitialsAdsLoader.AdsMediaSourceFactory для создания MediaSource и предоставления его ExoPlayer через API плейлистов на основе источников медиа:

Котлин

hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(context)
// Create a MediaSource.Factory for HLS streams with interstitials.
var hlsMediaSourceFactory =
  HlsInterstitialsAdsLoader.AdsMediaSourceFactory(hlsInterstitialsAdsLoader, playerView, context)

// Build player with default media source factory.
player = new ExoPlayer.Builder(context).build();

// Create an media source from an HLS media item with ads configuration.
val mediaSource =
  hlsMediaSourceFactory.createMediaSource(
    MediaItem.Builder()
      .setUri("https://www.example.com/media.m3u8")
      .setAdsConfiguration(
        MediaItem.AdsConfiguration.Builder(Uri.parse("hls://interstitials"))
          .setAdsId("ad-tag-0")
          .build()
      )
      .build()
  )

// Set the media source on the player.
player.setMediaSource(mediaSource)
player.prepare()
player.play()

Ява

HlsInterstitialsAdsLoader hlsInterstitialsAdsLoader = new HlsInterstitialsAdsLoader(context);
// Create a MediaSource.Factory for HLS streams with interstitials.
MediaSource.Factory hlsMediaSourceFactory =
    new HlsInterstitialsAdsLoader.AdsMediaSourceFactory(
      hlsInterstitialsAdsLoader, playerView, context);

// Build player with default media source factory.
player = new ExoPlayer.Builder(context).build();

// Create an media source from an HLS media item with ads configuration.
MediaSource mediaSource =
    hlsMediaSourceFactory.createMediaSource(
      new MediaItem.Builder()
        .setUri("https://www.example.com/media.m3u8")
        .setAdsConfiguration(
            new MediaItem.AdsConfiguration.Builder(Uri.parse("hls://interstitials"))
                .setAdsId("ad-tag-0")
                .build())
        .build());

// Set the media source on the player.
exoPlayer.setMediaSource(mediaSource);
exoPlayer.prepare();
exoPlayer.play();

Слушайте рекламные события

В HlsInterstitialsAdsLoader можно добавить Listener для отслеживания событий, связанных с изменением статуса воспроизведения полноэкранных объявлений HLS. Это позволяет приложению или SDK отслеживать воспроизведение рекламы, загрузку списков ресурсов, подготовку источников рекламных медиа, а также обнаруживать ошибки загрузки списков ресурсов и подготовки рекламы. Кроме того, метаданные, передаваемые источниками рекламных медиа, можно получать для детальной проверки воспроизведения рекламы или отслеживания хода ее воспроизведения.

Котлин

class AdsLoaderListener : HlsInterstitialsAdsLoader.Listener {

  override fun onStart(mediaItem: MediaItem, adsId: Any, adViewProvider: AdViewProvider) {
    // Do something when HLS media item with interstitials is started.
  }

  override fun onMetadata(
    mediaItem: MediaItem,
    adsId: Any,
    adGroupIndex: Int,
    adIndexInAdGroup: Int,
    metadata: Metadata,
  ) {
    // Do something with metadata that is emitted by the ad media source of the given ad.
  }

  override fun onAdCompleted(
    mediaItem: MediaItem,
    adsId: Any,
    adGroupIndex: Int,
    adIndexInAdGroup: Int,
  ) {
    // Do something when ad completed playback.
  }

  // ... See JavaDoc for further callbacks of HlsInterstitialsAdsLoader.Listener.

  override fun onStop(mediaItem: MediaItem, adsId: Any, adPlaybackState: AdPlaybackState) {
    // Do something with the resulting ad playback state when stopped.
  }
}

Ява

private class AdsLoaderListener
    implements HlsInterstitialsAdsLoader.Listener {

  // implement HlsInterstitialsAdsLoader.Listener

  @Override
  public void onStart(MediaItem mediaItem, Object adsId, AdViewProvider adViewProvider) {
    // Do something when HLS media item with interstitials is started.
  }

  @Override
  public void onMetadata(
      MediaItem mediaItem,
      Object adsId,
      int adGroupIndex,
      int adIndexInAdGroup,
      Metadata metadata) {
    // Do something with metadata that is emitted by the ad media source of the given ad.
  }

  @Override
  public void onAdCompleted(
      MediaItem mediaItem, Object adsId, int adGroupIndex, int adIndexInAdGroup) {
    // Do something when ad completed playback.
  }

  // ... See JavaDoc for further callbacks

  @Override
  public void onStop(MediaItem mediaItem, Object adsId, AdPlaybackState adPlaybackState) {
    // Do something with the resulting ad playback state when stopped.
  }
}

Подробную документацию по всем доступным обратным вызовам см. в JavaDoc HlsInterstitialsAdsLoader.Listener .

Затем прослушиватель можно добавить в загрузчик рекламы:

Котлин

var listener  = AdsLoaderListener();
// Add the listener to the ads loader to receive ad loader events.
hlsInterstitialsAdsLoader.addListener(listener);

Ява

AdsLoaderListener listener = new AdsLoaderListener();
// Add the listener to the ads loader to receive ad loader events.
hlsInterstitialsAdsLoader.addListener(listener);

Жизненный цикл HlsInterstitialsAdsLoader

Экземпляр HlsInterstitialsAdsLoader или HlsInterstitialsAdsLoader.AdsMediaSourceFactory можно повторно использовать для нескольких экземпляров проигрывателя, создающих несколько источников медиа, для которых необходимо загрузить рекламу.

Экземпляр можно создать, например, в методе onCreate объекта Activity , а затем использовать повторно для нескольких экземпляров проигрывателя. Это работает, пока он используется одним экземпляром проигрывателя одновременно. Это полезно в распространённом случае, когда приложение переходит в фоновый режим, экземпляр проигрывателя уничтожается, а затем создаётся новый экземпляр при повторном выходе приложения на передний план.

Котлин

// Create the ads loader instance (for example onCreate).
hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(context);

// Build a player and set it on the ads loader (for example onStart).
player = ExoPlayer.Builder(context).build();
hlsInterstitialsAdsLoader.setPlayer(player);

// Release the player and unset it on the ads loader (for example onStop).
player.release();
hlsInterstitialsAdsLoader.setPlayer(null);

// Build another player and set it on the ads loader (for example onStart).
player = ExoPlayer.Builder(context).build();
hlsInterstitialsAdsLoader.setPlayer(player);

// Release the player and unset it on the ads loader (for example onStop).
player.release();
hlsInterstitialsAdsLoader.setPlayer(null);

// Release the ads loader when not used anymore  (for example onDestroy).
hlsInterstitialsAdsLoader.release();

Ява

// Create the ads loader instance (for example onCreate).
hlsInterstitialsAdsLoader = new HlsInterstitialsAdsLoader(context);

// Build a player and set it on the ads loader (for example onStart).
player = new ExoPlayer.Builder(context).build();
hlsInterstitialsAdsLoader.setPlayer(player);

// Release the player and unset it on the ads loader (for example onStop).
player.release();
hlsInterstitialsAdsLoader.setPlayer(null);

// Build another player and set it on the ads loader (for example onStart).
player = new ExoPlayer.Builder(context).build();
hlsInterstitialsAdsLoader.setPlayer(player);

// Release the player and unset it on the ads loader (for example onStop).
player.release();
hlsInterstitialsAdsLoader.setPlayer(null);

// Release the ads loader when not used anymore  (for example onDestroy).
hlsInterstitialsAdsLoader.release();

Как правило, перед установкой нового экземпляра проигрывателя в загрузчике рекламы необходимо освободить старый экземпляр проигрывателя. После освобождения загрузчика рекламы его использование становится невозможным.

Настройка воспроизведения

ExoPlayer предоставляет множество способов настроить воспроизведение в соответствии с потребностями вашего приложения. Примеры см. на странице настроек .

Отключение безблочной подготовки

По умолчанию ExoPlayer использует безблочную подготовку. Это означает, что ExoPlayer будет использовать для подготовки потока только информацию из многовариантного плейлиста. Это работает, если теги #EXT-X-STREAM-INF содержат атрибут CODECS .

Вам может потребоваться отключить эту функцию, если ваши медиасегменты содержат мультиплексированные дорожки субтитров, которые не объявлены в многовариантном плейлисте с тегом #EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS . В противном случае эти дорожки субтитров не будут обнаружены и воспроизведены. Вы можете отключить безблочную подготовку в HlsMediaSource.Factory , как показано в следующем фрагменте кода. Обратите внимание, что это увеличит время запуска, так как ExoPlayer потребуется загрузить медиасегмент для обнаружения этих дополнительных дорожек, поэтому предпочтительнее объявлять дорожки субтитров в многовариантном плейлисте.

Котлин

val hlsMediaSource =
  HlsMediaSource.Factory(dataSourceFactory)
    .setAllowChunklessPreparation(false)
    .createMediaSource(MediaItem.fromUri(hlsUri))

Ява

HlsMediaSource hlsMediaSource =
    new HlsMediaSource.Factory(dataSourceFactory)
        .setAllowChunklessPreparation(false)
        .createMediaSource(MediaItem.fromUri(hlsUri));

Создание высококачественного HLS-контента

Чтобы максимально эффективно использовать ExoPlayer, следуйте определённым рекомендациям по улучшению качества HLS-контента. Подробнее читайте в нашей статье на Medium о воспроизведении HLS в ExoPlayer . Основные моменты:

  • Используйте точную длительность сегментов.
  • Используйте непрерывный поток медиа; избегайте изменений в структуре медиа в разных сегментах.
  • Используйте тег #EXT-X-INDEPENDENT-SEGMENTS .
  • Отдавайте предпочтение демультиплексированным потокам, а не файлам, содержащим и видео, и аудио.
  • Включите всю возможную информацию в многовариантный плейлист.

Следующие правила применяются специально для прямых трансляций:

  • Используйте тег #EXT-X-PROGRAM-DATE-TIME .
  • Используйте тег #EXT-X-DISCONTINUITY-SEQUENCE .
  • Обеспечьте себе продолжительное окно. Одна минута или больше — это отлично.