Прямой эфир

ExoPlayer воспроизводит большинство адаптивных прямых трансляций без каких-либо специальных настроек. Подробную информацию можно найти на странице Поддерживаемые форматы.

Адаптивные трансляции предлагают окно доступных медиа, которое обновляется через регулярные интервалы, чтобы соответствовать текущему моменту в реальном времени. Это означает, что позиция воспроизведения всегда будет находиться где-то в этом окне, в большинстве случаев близко к текущему реальному времени, в котором создается поток. Разница между текущим временем в реальном времени и позицией воспроизведения называется смещением трансляции.

Как выявлять и отслеживать воспроизведения в прямом эфире

Каждый раз, когда обновляется окно прямого эфира, зарегистрированные экземпляры Player.Listener получают событие onTimelineChanged. Вы можете получить информацию о текущем воспроизведении трансляции, отправив запрос с помощью различных методов Player и Timeline.Window, как описано ниже и показано на следующем рисунке.

Live Window Query API
Рисунок 1. Live window.
  • Player.isCurrentWindowLive указывает, является ли воспроизводимый в данный момент мультимедийный объект прямой трансляцией. Это значение сохраняется, даже если трансляция уже завершена.
  • Player.isCurrentWindowDynamic указывает, обновляется ли воспроизводимый в данный момент мультимедийный объект. Обычно это происходит, если трансляция ещё не завершена. Обратите внимание, что в некоторых случаях этот флаг также имеет значение true для трансляций, которые не проводятся в прямом эфире.
  • Player.getCurrentLiveOffset возвращает разницу между текущим реальным временем и позицией воспроизведения (если доступно).
  • Player.getDuration возвращает продолжительность текущего окна трансляции.
  • Player.getCurrentPosition возвращает позицию воспроизведения относительно начала окна прямой трансляции.
  • Player.getCurrentMediaItem возвращает текущий мультимедийный объект, где MediaItem.liveConfiguration содержит предоставленные приложением переопределения для целевого смещения и параметров корректировки смещения.
  • Player.getCurrentTimeline возвращает текущую структуру медиаконтента в Timeline. Текущий объект Timeline.Window можно получить из объекта Timeline с помощью методов Player.getCurrentMediaItemIndex и Timeline.getWindow. В Window:
    • Window.liveConfiguration содержит целевое смещение и параметры его корректировки. Эти значения основаны на информации в медиафайле и любых переопределениях, заданных приложением в MediaItem.liveConfiguration.
    • Window.windowStartTimeMs – время, прошедшее с начала эпохи Unix до начала окна трансляции.
    • Window.getCurrentUnixTimeMs – время, прошедшее с начала эпохи Unix до текущего момента. Это значение может быть скорректировано с учетом известной разницы во времени между сервером и клиентом.
    • Window.getDefaultPositionMs – позиция в окне прямого эфира, с которой проигрыватель по умолчанию начинает воспроизведение.

Перемотка в прямых трансляциях

Вы можете перейти к любому моменту в окне трансляции с помощью Player.seekTo. Переданная позиция перемотки относится к началу окна трансляции. Например, seekTo(0) – это начало окна трансляции. После перемотки проигрыватель попытается сохранить то же смещение, что и в позиции, к которой был выполнен переход.

У окна прямого эфира также есть позиция по умолчанию, с которой должно начинаться воспроизведение. Обычно эта позиция находится близко к текущему моменту. Чтобы перейти к позиции по умолчанию, вызовите метод Player.seekToDefaultPosition.

Интерфейс воспроизведения трансляций

Компоненты пользовательского интерфейса ExoPlayer по умолчанию показывают продолжительность окна трансляции и текущую позицию воспроизведения в нем. Это означает, что позиция будет сдвигаться назад при каждом обновлении окна. Если вам нужно другое поведение, например показ времени эпохи Unix или текущего смещения, вы можете создать форк PlayerControlView и изменить его в соответствии со своими потребностями.

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

В ExoPlayer есть параметры, которые позволяют управлять смещением позиции воспроизведения относительно текущего момента и диапазоном скоростей воспроизведения, которые можно использовать для корректировки этого смещения.

ExoPlayer получает значения для этих параметров из трех источников в порядке убывания приоритета (используется первое найденное значение):

  • Значения MediaItem, переданные в MediaItem.Builder.setLiveConfiguration.
  • Глобальные значения по умолчанию заданы DefaultMediaSourceFactory.
  • Значения, считанные непосредственно из медиафайла.

Kotlin

// Global settings.
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(DefaultMediaSourceFactory(context).setLiveTargetOffsetMs(5000))
    .build()

// Per MediaItem settings.
val mediaItem =
  MediaItem.Builder()
    .setUri(mediaUri)
    .setLiveConfiguration(
      MediaItem.LiveConfiguration.Builder().setMaxPlaybackSpeed(1.02f).build()
    )
    .build()
player.setMediaItem(mediaItem)

Java

// Global settings.
ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context).setLiveTargetOffsetMs(5000))
        .build();

// Per MediaItem settings.
MediaItem mediaItem =
    new MediaItem.Builder()
        .setUri(mediaUri)
        .setLiveConfiguration(
            new MediaItem.LiveConfiguration.Builder().setMaxPlaybackSpeed(1.02f).build())
        .build();
player.setMediaItem(mediaItem);

Доступные значения конфигурации:

  • targetOffsetMs – целевое смещение относительно текущего момента. Проигрыватель будет пытаться приблизиться к этому значению во время воспроизведения.
  • minOffsetMs – минимально допустимое смещение от текущего момента. Даже если смещение будет скорректировано с учетом текущих условий сети, проигрыватель не будет пытаться опуститься ниже этого значения во время воспроизведения.
  • maxOffsetMs – максимально допустимое смещение относительно текущего момента. Даже при корректировке смещения в соответствии с текущими условиями сети проигрыватель не будет пытаться превысить это смещение во время воспроизведения.
  • minPlaybackSpeed – минимальная скорость воспроизведения, которую может использовать проигрыватель, чтобы достичь целевого отставания от трансляции.
  • maxPlaybackSpeed – максимальная скорость воспроизведения, которую может использовать проигрыватель, чтобы достичь целевого смещения.

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

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

Если автоматическая регулировка скорости воспроизведения не требуется, ее можно отключить, задав для свойств minPlaybackSpeed и maxPlaybackSpeed значение 1.0f. Аналогичным образом можно включить DVR для трансляций без низкой задержки, явно задав для этих параметров значения, отличные от 1.0f. Подробнее о том, как задать эти свойства, рассказывается в разделе о настройке.

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

Если корректировка скорости включена, LivePlaybackSpeedControl определяет, какие корректировки вносятся. Вы можете реализовать собственный интерфейс LivePlaybackSpeedControl или настроить реализацию по умолчанию – DefaultLivePlaybackSpeedControl. В обоих случаях экземпляр можно задать при создании проигрывателя:

Kotlin

val player =
  ExoPlayer.Builder(context)
    .setLivePlaybackSpeedControl(
      DefaultLivePlaybackSpeedControl.Builder().setFallbackMaxPlaybackSpeed(1.04f).build()
    )
    .build()

Java

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setLivePlaybackSpeedControl(
            new DefaultLivePlaybackSpeedControl.Builder()
                .setFallbackMaxPlaybackSpeed(1.04f)
                .build())
        .build();

Для DefaultLivePlaybackSpeedControl доступны следующие параметры:

  • fallbackMinPlaybackSpeed и fallbackMaxPlaybackSpeed – минимальная и максимальная скорость воспроизведения, которая может использоваться для корректировки, если ни медиаконтент, ни предоставленный приложением MediaItem не определяют ограничения.
  • proportionalControlFactor – определяет плавность изменения скорости. При высоком значении скорость меняется более резко и быстро, но это может быть заметно на слух. Чем меньше значение, тем плавнее переход между скоростями, но тем больше времени он занимает.
  • targetLiveOffsetIncrementOnRebufferMs – это значение добавляется к целевому смещению при каждом повторном буферировании, чтобы действовать более осторожно. Чтобы отключить эту функцию, задайте значение 0.
  • minPossibleLiveOffsetSmoothingFactor – экспоненциальный коэффициент сглаживания, который используется для отслеживания минимально возможного отставания от трансляции на основе медиаконтента, находящегося в буфере. Значение, близкое к 1, означает, что оценка более осторожная и может занять больше времени для адаптации к улучшенным условиям сети, тогда как более низкое значение означает, что оценка будет корректироваться быстрее с более высоким риском повторной буферизации.

BehindLiveWindowException и ERROR_CODE_BEHIND_LIVE_WINDOW

Позиция воспроизведения может отставать от окна прямой трансляции, например если проигрыватель приостановлен или буферизация длится слишком долго. В этом случае воспроизведение не удастся, и будет зарегистрировано исключение с кодом ошибки ERROR_CODE_BEHIND_LIVE_WINDOW с помощью Player.Listener.onPlayerError. В таких случаях можно возобновить воспроизведение с позиции по умолчанию. Пример такого подхода можно найти в классе PlayerActivity демонстрационного приложения.

Kotlin

override fun onPlayerError(error: PlaybackException) {
  if (error.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) {
    // Re-initialize player at the live edge.
    player.seekToDefaultPosition()
    player.prepare()
  } else {
    // Handle other errors
  }
}

Java

@Override
public void onPlayerError(PlaybackException error) {
  if (error.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) {
    // Re-initialize player at the live edge.
    player.seekToDefaultPosition();
    player.prepare();
  } else {
    // Handle other errors
  }
}