Transmisión en vivo

ExoPlayer reproduce la mayoría de las transmisiones en vivo adaptativas de forma predeterminada sin necesidad de una configuración especial. Para obtener más detalles, consulta la página Formatos admitidos.

Las transmisiones en vivo adaptativas ofrecen una ventana de contenido multimedia disponible que se actualiza en intervalos regulares para seguir el tiempo real actual. Esto significa que la posición de reproducción siempre estará en algún punto de esta ventana, y, en la mayoría de los casos, cerca del tiempo real actual en el que se produce la transmisión. La diferencia entre la posición actual en tiempo real y la posición de reproducción se denomina compensación en vivo.

Cómo detectar y supervisar las reproducciones en vivo

Cada vez que se actualiza una ventana en vivo, las instancias de Player.Listener registradas recibirán un evento onTimelineChanged. Puedes recuperar detalles sobre la reproducción en vivo actual consultando varios métodos Player y Timeline.Window, como se indica a continuación y se muestra en la siguiente figura.

Ventana en vivo

  • Player.isCurrentWindowLive indica si el elemento multimedia que se está reproduciendo actualmente es una transmisión en vivo. Este valor sigue siendo verdadero incluso si finalizó la transmisión en vivo.
  • Player.isCurrentWindowDynamic indica si el elemento multimedia que se está reproduciendo actualmente se sigue actualizando. Esto suele ser cierto para las transmisiones en vivo que aún no finalizaron. Ten en cuenta que esta marca también es verdadera para las transmisiones que no son en vivo en algunos casos.
  • Player.getCurrentLiveOffset devuelve el desplazamiento entre la hora actual en tiempo real y la posición de reproducción (si está disponible).
  • Player.getDuration devuelve la longitud de la ventana en vivo actual.
  • Player.getCurrentPosition devuelve la posición de reproducción relativa al inicio de la ventana en vivo.
  • Player.getCurrentMediaItem devuelve el elemento multimedia actual, en el que MediaItem.liveConfiguration contiene anulaciones proporcionadas por la app para los parámetros de ajuste y desplazamiento en vivo objetivo.
  • Player.getCurrentTimeline devuelve la estructura de medios actual en un objeto Timeline. El Timeline.Window actual se puede recuperar del Timeline con Player.getCurrentMediaItemIndex y Timeline.getWindow. En Window, haz lo siguiente:
    • Window.liveConfiguration contiene los parámetros de ajuste del desplazamiento en vivo y el desplazamiento en vivo de destino. Estos valores se basan en la información de los medios y en cualquier anulación proporcionada por la app que se haya establecido en MediaItem.liveConfiguration.
    • Window.windowStartTimeMs es la hora desde la época de Unix en la que comienza el período en vivo.
    • Window.getCurrentUnixTimeMs es el tiempo transcurrido desde la época de Unix del tiempo real actual. Este valor se puede corregir según una diferencia de reloj conocida entre el servidor y el cliente.
    • Window.getDefaultPositionMs es la posición en la ventana en vivo en la que el reproductor comenzará la reproducción de forma predeterminada.

Cómo buscar en las transmisiones en vivo

Puedes avanzar a cualquier punto dentro de la ventana en vivo con Player.seekTo. La posición de búsqueda que se pasa es relativa al inicio de la ventana en vivo. Por ejemplo, seekTo(0) buscará el inicio de la ventana en vivo. Después de una búsqueda, el reproductor intentará mantener el mismo desplazamiento en vivo que la posición a la que se buscó.

La ventana en vivo también tiene una posición predeterminada en la que se supone que debe comenzar la reproducción. Por lo general, esta posición se encuentra cerca del borde en vivo. Puedes buscar la posición predeterminada llamando a Player.seekToDefaultPosition.

IU de reproducción en vivo

Los componentes de IU predeterminados de ExoPlayer muestran la duración de la ventana en vivo y la posición de reproducción actual dentro de ella. Esto significa que la posición parecerá retroceder cada vez que se actualice la ventana en vivo. Si necesitas un comportamiento diferente, por ejemplo, mostrar la hora Unix o el desplazamiento en vivo actual, puedes bifurcar PlayerControlView y modificarlo para que se adapte a tus necesidades.

Cómo configurar los parámetros de reproducción en vivo

ExoPlayer usa algunos parámetros para controlar el desplazamiento de la posición de reproducción desde el borde en vivo y el rango de velocidades de reproducción que se pueden usar para ajustar este desplazamiento.

ExoPlayer obtiene los valores de estos parámetros de tres lugares, en orden descendente de prioridad (se usa el primer valor que se encuentra):

  • Son los valores de MediaItem que se pasan a MediaItem.Builder.setLiveConfiguration.
  • Valores predeterminados globales establecidos en DefaultMediaSourceFactory.
  • Son los valores que se leen directamente de los medios.

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

Los valores de configuración disponibles son los siguientes:

  • targetOffsetMs: Es el desplazamiento en vivo de destino. Si es posible, el reproductor intentará acercarse a este desplazamiento en vivo durante la reproducción.
  • minOffsetMs: Es el desplazamiento en vivo mínimo permitido. Incluso cuando se ajusta la compensación a las condiciones de red actuales, el reproductor no intentará reducirla durante la reproducción.
  • maxOffsetMs: Es el desplazamiento en vivo máximo permitido. Incluso cuando se ajusta la compensación según las condiciones actuales de la red, el reproductor no intentará superar esta compensación durante la reproducción.
  • minPlaybackSpeed: Es la velocidad de reproducción mínima que el reproductor puede usar como alternativa cuando intenta alcanzar el desplazamiento en vivo objetivo.
  • maxPlaybackSpeed: Es la velocidad de reproducción máxima que puede usar el reproductor para alcanzar el desfase en vivo objetivo.

Ajuste de la velocidad de reproducción

Cuando se reproduce una transmisión en vivo de baja latencia, ExoPlayer ajusta el desplazamiento en vivo cambiando ligeramente la velocidad de reproducción. El reproductor intentará coincidir con el desplazamiento en vivo objetivo proporcionado por el contenido multimedia o la app, pero también intentará reaccionar a las condiciones cambiantes de la red. Por ejemplo, si se producen interrupciones durante la reproducción, el reproductor la ralentizará ligeramente para alejarse más del borde en vivo. Si la red vuelve a ser lo suficientemente estable como para permitir la reproducción más cerca del borde activo, el reproductor acelerará la reproducción para volver al desplazamiento en vivo objetivo.

Si no deseas el ajuste automático de la velocidad de reproducción, puedes inhabilitarlo configurando las propiedades minPlaybackSpeed y maxPlaybackSpeed en 1.0f. Del mismo modo, se puede habilitar para transmisiones en vivo que no sean de baja latencia si se configuran explícitamente en valores distintos de 1.0f. Consulta la sección de configuración anterior para obtener más detalles sobre cómo se pueden establecer estas propiedades.

Cómo personalizar el algoritmo de ajuste de la velocidad de reproducción

Si el ajuste de velocidad está habilitado, un LivePlaybackSpeedControl define qué ajustes se realizan. Es posible implementar un LivePlaybackSpeedControl personalizado o personalizar la implementación predeterminada, que es DefaultLivePlaybackSpeedControl. En ambos casos, se puede establecer una instancia cuando se compila el reproductor:

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

Los parámetros de personalización relevantes de DefaultLivePlaybackSpeedControl son los siguientes:

  • fallbackMinPlaybackSpeed y fallbackMaxPlaybackSpeed: Son las velocidades de reproducción mínima y máxima que se pueden usar para el ajuste si ni el contenido multimedia ni el MediaItem proporcionado por la app definen límites.
  • proportionalControlFactor: Controla qué tan suave es el ajuste de velocidad. Un valor alto hace que los ajustes sean más repentinos y reactivos, pero también es más probable que se escuchen. Un valor más pequeño genera una transición más fluida entre las velocidades, pero a costa de ser más lento.
  • targetLiveOffsetIncrementOnRebufferMs: Este valor se agrega a la compensación en vivo objetivo cada vez que se produce un rebuffer, para proceder con más cautela. Para inhabilitar esta función, establece el valor en 0.
  • minPossibleLiveOffsetSmoothingFactor: Es un factor de suavización exponencial que se usa para hacer un seguimiento de la compensación en vivo mínima posible en función del contenido multimedia almacenado en búfer actualmente. Un valor muy cercano a 1 significa que la estimación es más cautelosa y puede tardar más en ajustarse a las mejores condiciones de la red, mientras que un valor más bajo significa que la estimación se ajustará más rápido con un mayor riesgo de interrupciones.

BehindLiveWindowException y ERROR_CODE_BEHIND_LIVE_WINDOW

La posición de reproducción puede quedar rezagada con respecto a la ventana en vivo, por ejemplo, si el reproductor se pausa o se almacena en el búfer durante un período lo suficientemente largo. Si esto sucede, fallará la reproducción y se informará una excepción con el código de error ERROR_CODE_BEHIND_LIVE_WINDOW a través de Player.Listener.onPlayerError. El código de la aplicación puede controlar estos errores reanudando la reproducción en la posición predeterminada. La PlayerActivity de la app de demostración ejemplifica este enfoque.

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