直播

ExoPlayer 在沒有任何特殊配置的情況下,可以播放大多數自適應直播流。有關更多詳細信息,請參閱 支持的格式頁面

自适应直播流提供了一个不断更新的媒体窗口,以便与当前实时情况保持一致。这意味着播放位置始终位于此窗口内,在大多数情况下接近流正在生成的当前实时时间。当前实时时间与播放位置之间的差异称为 *实时偏移量*。

检测和监控直播回放

每次实时窗口更新时,注册的 Player.Listener 实例将收到一个 onTimelineChanged 事件。您可以通过查询各种 PlayerTimeline.Window 方法来检索有关当前直播回放的详细信息,如下所示,并如下图所示。

Live window

  • Player.isCurrentWindowLive 指示当前播放的媒体项目是否为直播流。即使直播流已结束,此值仍然为真。
  • Player.isCurrentWindowDynamic 指示当前播放的媒体项目是否仍在更新。对于尚未结束的直播流,这通常为真。请注意,在某些情况下,此标志对于非直播流也为真。
  • Player.getCurrentLiveOffset 返回当前实时时间与播放位置之间的偏移量(如果可用)。
  • Player.getDuration 返回当前实时窗口的长度。
  • Player.getCurrentPosition 返回相对于实时窗口开头的播放位置。
  • Player.getCurrentMediaItem 返回当前媒体项目,其中 MediaItem.liveConfiguration 包含应用提供的目标实时偏移量和实时偏移量调整参数的覆盖。
  • Player.getCurrentTimeline 返回 Timeline 中的当前媒体结构。可以使用 Player.getCurrentWindowIndexTimeline.getWindowTimeline 中检索当前 Timeline.Window。在 Window
    • Window.liveConfiguration 包含目标实时偏移量和实时偏移量调整参数。这些值基于媒体中的信息和在 MediaItem.liveConfiguration 中设置的任何应用提供的覆盖。
    • Window.windowStartTimeMs 是实时窗口开始时自 Unix 纪元以来的时间。
    • Window.getCurrentUnixTimeMs 是当前实时时间自 Unix 纪元以来的时间。此值可能会根据服务器和客户端之间已知的时钟差异进行校正。
    • Window.getDefaultPositionMs 是播放器默认情况下在实时窗口中开始播放的位置。

在直播流中进行跳转

您可以使用 Player.seekTo 跳转到实时窗口内的任何位置。传递的跳转位置相对于实时窗口的开头。例如,seekTo(0) 将跳转到实时窗口的开头。播放器将尝试在跳转后保持与跳转到的位置相同的实时偏移量。

实时窗口还有一个默认位置,播放器应该从该位置开始播放。此位置通常靠近实时边缘。您可以通过调用 Player.seekToDefaultPosition 跳转到默认位置。

直播回放 UI

ExoPlayer 的 默认 UI 组件 显示实时窗口的持续时间以及其中的当前播放位置。这意味着每次实时窗口更新时,位置看起来都会向后跳。如果您需要不同的行为,例如显示 Unix 时间或当前实时偏移量,您可以分叉 PlayerControlView 并根据需要对其进行修改。

配置直播回放参数

ExoPlayer 使用一些参数来控制播放位置相对于实时边缘的偏移量,以及可用于调整此偏移量的播放速度范围。

ExoPlayer 从三个地方获取这些参数的值,按优先级降序排列(使用找到的第一个值)

  • 每个传递给 MediaItem.Builder.setLiveConfigurationMediaItem 值。
  • 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 通过略微改变播放速度来调整实时偏移量。播放器将尝试匹配媒体或应用提供的目标实时偏移量,但也会尝试对不断变化的网络条件做出反应。例如,如果在播放过程中发生重新缓冲,播放器将略微降低播放速度以远离实时边缘。如果网络随后变得稳定到足以支持再次更靠近实时边缘播放,播放器将加快播放速度以回到目标实时偏移量。

如果不需要自动播放速度调整,可以通过将 minPlaybackSpeedmaxPlaybackSpeed 属性设置为 1.0f 来禁用它。类似地,可以通过将这些属性明确设置为除 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 的相关自定义参数是

  • fallbackMinPlaybackSpeedfallbackMaxPlaybackSpeed:如果媒体或应用提供的 MediaItem 未定义限制,则可用于调整的最小和最大播放速度。
  • proportionalControlFactor:控制速度调整的平滑程度。较高的值使调整更加突然和敏感,但也更容易被听到。较小的值会导致速度之间更平滑的过渡,但代价是速度较慢。
  • targetLiveOffsetIncrementOnRebufferMs:每次发生重新缓冲时,将此值添加到目标实时偏移量,以便更谨慎地进行操作。可以通过将此值设置为 0 来禁用此功能。
  • minPossibleLiveOffsetSmoothingFactor:一个指数平滑因子,用于根据当前缓冲的媒体跟踪可能的最小实时偏移量。非常接近 1 的值意味着估计更加谨慎,可能需要更长时间才能适应网络条件的改善,而较低的值意味着估计将更快地调整,但存在更大的风险遇到重新缓冲。

BehindLiveWindowException 和 ERROR_CODE_BEHIND_LIVE_WINDOW

播放位置可能会落后于实时窗口,例如,如果播放器暂停或缓冲时间过长。如果发生这种情况,则播放将失败,并将通过 Player.Listener.onPlayerError 报告错误代码为 ERROR_CODE_BEHIND_LIVE_WINDOW 的异常。应用程序代码可能希望通过在默认位置恢复播放来处理此类错误。演示应用程序的 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
  }
}