ExoPlayer 无需任何特殊配置,即可直接播放大多数自适应直播。如需了解详情,请参阅“支持的格式”页面。
自适应直播提供了一个可用媒体窗口,该窗口会定期更新,以随当前实时移动。这意味着,播放位置始终位于此时间范围内,在大多数情况下,播放位置接近生成数据流的当前实时位置。当前实时位置与播放位置之间的差异称为直播偏移量。
检测和监控直播播放
每当实时窗口更新时,已注册的 Player.Listener
实例都会收到 onTimelineChanged
事件。您可以通过查询各种 Player
和 Timeline.Window
方法来检索有关当前直播播放的详细信息,如下所列和下图所示。
Player.isCurrentWindowLive
指示当前正在播放的媒体内容是否为直播。即使直播已结束,此值也仍然为 true。Player.isCurrentWindowDynamic
表示当前正在播放的媒体内容是否仍在更新。通常适用于尚未结束的直播。请注意,在某些情况下,此标志也适用于非直播。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 纪元以来)。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 会通过略微更改播放速度来调整直播偏移。播放器将尝试匹配媒体或应用提供的目标实时偏移量,但也会尝试对不断变化的网络条件做出响应。例如,如果在播放期间发生重新缓冲,播放器会稍微放慢播放速度,以远离直播边缘。如果网络随后变得足够稳定,可以再次在更靠近实时边缘的位置播放,则播放器将加快播放速度并移回目标实时偏移量。
如果不希望自动调整播放速度,可以将 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 } }