直播

ExoPlayer 开箱即可播放大多数自适应直播,无需任何特殊配置。如需了解详情,请参阅支持的格式页面

自适应直播提供一个可用媒体窗口,该窗口定期更新以随当前实时数据移动。这意味着播放位置将始终位于此窗口中,在大多数情况下,接近生成流的当前实时时间。当前实时位置与播放位置之间的差异称为实时偏移量。

检测和监控直播播放

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

实时窗口

  • Player.isCurrentWindowLive 指示当前正在播放的媒体项是否为直播流。即使直播已结束,此值仍然为 true。
  • 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 Epoch 以来的时间,即实时窗口的开始时间。
    • Window.getCurrentUnixTimeMs 是自当前实时的 Unix 纪元以来经过的时间。可以根据服务器和客户端之间的已知时钟差值来更正此值。
    • Window.getDefaultPositionMs 是直播窗口中默认播放器开始播放的位置。

在直播中跳转

您可以使用 Player.seekTo 跳转到实时窗口中的任意位置。传递的跳转位置是相对于实时窗口的起始位置而言的。例如,seekTo(0) 会跳转到实时窗口的起始处。播放器会尝试保持与跳转后定位到的位置相同的实时偏移量。

直播窗口还有一个默认位置,即应该开始播放的内容。此位置通常位于靠近直播边缘的位置。您可以通过调用 Player.seekToDefaultPosition 跳转到默认位置。

直播播放界面

ExoPlayer 的默认界面组件显示了直播窗口的时长及其中的当前播放位置。这意味着,每次实时窗口更新时,位置看起来都会向后跳转。如果您需要其他行为,例如显示 Unix 时间或当前实时偏移量,您可以复刻 PlayerControlView 并根据需要进行修改。

配置直播参数

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

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

  • 每向 MediaItem.Builder.setLiveConfiguration 传递 MediaItem 值。
  • 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
  }
}