ExoPlayer 无需任何特殊配置即可开箱即用地播放大多数自适应直播流。有关更多详细信息,请参阅支持的格式页面。
自适应直播流提供了一个可用媒体窗口,该窗口会定期更新,以随当前实时时间移动。这意味着播放位置将始终在该窗口内,在大多数情况下,靠近流生成时的当前实时时间。当前实时时间与播放位置之间的差值称为直播偏移。
检测和监控直播播放
每次直播窗口更新时,已注册的 Player.Listener
实例都会收到一个 onTimelineChanged
事件。您可以通过查询各种 Player
和 Timeline.Window
方法来检索有关当前直播播放的详细信息,如下所示并显示在下图中。
Player.isCurrentWindowLive
指示当前正在播放的媒体项是否为直播流。即使直播流已结束,此值仍然为 true。Player.isCurrentWindowDynamic
指示当前正在播放的媒体项是否仍在更新。对于尚未结束的直播流,这通常为 true。请注意,在某些情况下,对于非直播流,此标志也为 true。Player.getCurrentLiveOffset
返回当前实时时间与播放位置之间的偏移量(如果可用)。Player.getDuration
返回当前直播窗口的长度。Player.getCurrentPosition
返回相对于直播窗口开头的播放位置。Player.getCurrentMediaItem
返回当前媒体项,其中MediaItem.liveConfiguration
包含应用提供的目标直播偏移量和直播偏移量调整参数的覆盖。Player.getCurrentTimeline
以Timeline
形式返回当前媒体结构。可以使用Player.getCurrentMediaItemIndex
和Timeline.getWindow
从Timeline
中检索当前的Timeline.Window
。在Window
中Window.liveConfiguration
包含目标直播偏移量和直播偏移量调整参数。这些值基于媒体中的信息以及在MediaItem.liveConfiguration
中设置的任何应用提供的覆盖。Window.windowStartTimeMs
是直播窗口开始的 Unix Epoch 时间。Window.getCurrentUnixTimeMs
是当前实时时间的 Unix Epoch 时间。此值可能会通过服务器和客户端之间已知的时钟差进行校正。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 会通过略微改变播放速度来调整直播偏移量。播放器将尝试匹配媒体或应用提供的目标直播偏移量,但也会尝试对不断变化的网络条件做出反应。例如,如果在播放期间发生重新缓冲,播放器会略微减慢播放速度,以便远离直播边缘。如果网络随后变得足够稳定,可以再次支持更接近直播边缘的播放,播放器将加快播放速度,以便返回到目标直播偏移量。
如果不需要自动播放速度调整,可以通过将 minPlaybackSpeed
和 maxPlaybackSpeed
属性设置为 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
的相关自定义参数为
fallbackMinPlaybackSpeed
和fallbackMaxPlaybackSpeed
:如果媒体或应用提供的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 } }