ExoPlayer直播流:如何检测直播状态并自定义进度条时间显示?
我来帮你搞定ExoPlayer直播流进度条的时间显示问题,针对你提到的两个核心需求,咱们一步步实现:
1. 检测当前媒体是否为直播流
ExoPlayer的Timeline和Window类已经提供了判断直播流的属性,咱们可以通过Player.Listener的onTimelineChanged回调来实时检测:
首先,在你的类里定义一个完整的播放状态监听器,替换原来的playbackStateListener:
private val playbackStateListener = object : Player.Listener { override fun onTimelineChanged(timeline: Timeline, reason: Int) { super.onTimelineChanged(timeline, reason) // 判断是否为直播流:检查Timeline的第一个Window是否标记为Live val isLive = timeline.windowCount > 0 && timeline.getWindow(0, Window()).isLive // 触发进度条时间显示更新 updateProgressBarTimeDisplay(isLive) } override fun onPlaybackStateChanged(playbackState: Int) { // 控制时间更新的定时任务启停 when (playbackState) { Player.STATE_READY -> updateHandler.post(updateRunnable) Player.STATE_ENDED, Player.STATE_IDLE -> updateHandler.removeCallbacks(updateRunnable) } } }
这个逻辑的核心是Window.isLive属性,MPD直播流的元数据会被ExoPlayer解析并标记这个属性,所以能准确判断当前是否为直播场景。
2. 自定义直播流的进度条时间显示逻辑
你的需求是:直播时进度条左端显示负数值形式的缓存时长(比如-00:59:51),右端显示直播节点(比如LIVE)。咱们需要自定义时间格式,并定时更新进度条的文本。
步骤2.1:编写时间格式化工具函数
先写一个把毫秒转换成时分秒格式的函数,支持负数显示:
private fun formatTimeMs(timeMs: Long): String { val isNegative = timeMs < 0 val absTimeMs = kotlin.math.abs(timeMs) val hours = absTimeMs / 3600000 val minutes = (absTimeMs % 3600000) / 60000 val seconds = (absTimeMs % 60000) / 1000 return if (isNegative) { String.format("-%02d:%02d:%02d", hours, minutes, seconds) } else { String.format("%02d:%02d:%02d", hours, minutes, seconds) } }
步骤2.2:实现时间更新逻辑
接下来写更新进度条左右文本的方法,区分直播和非直播场景:
private fun updateProgressBarTimeDisplay(isLive: Boolean) { playerView?.let { safePlayerView -> // 获取默认进度条的左右时间TextView(ExoPlayer默认id是exo_position和exo_duration) val positionTextView = safePlayerView.findViewById<TextView>(R.id.exo_position) val durationTextView = safePlayerView.findViewById<TextView>(R.id.exo_duration) player?.let { safePlayer -> if (isLive) { val window = Window() safePlayer.currentTimeline.getWindow(safePlayer.currentWindowIndex, window) // 直播节点时间(live edge) val liveEdgeMs = window.liveEdgeMs // 当前播放位置 val currentPositionMs = safePlayer.currentPosition // 缓存时长 = 直播节点 - 当前位置,左端显示负的这个值 val cacheDurationMs = liveEdgeMs - currentPositionMs positionTextView.text = formatTimeMs(-cacheDurationMs) // 右端显示直播标识 durationTextView.text = "LIVE" } else { // 非直播场景用默认逻辑:显示当前位置和总时长 positionTextView.text = formatTimeMs(safePlayer.currentPosition) durationTextView.text = formatTimeMs(safePlayer.duration) } } } }
步骤2.3:定时更新时间
因为直播流的播放位置和直播节点一直在变化,咱们需要定时触发更新:
private val updateHandler = Handler(Looper.getMainLooper()) private val updateRunnable = object : Runnable { override fun run() { player?.let { safePlayer -> val timeline = safePlayer.currentTimeline if (timeline.windowCount > 0) { val window = Window() timeline.getWindow(safePlayer.currentWindowIndex, window) updateProgressBarTimeDisplay(window.isLive) } // 每秒更新一次 updateHandler.postDelayed(this, 1000) } } }
步骤2.4:清理资源
最后,记得在页面销毁时停止定时任务,避免内存泄漏:
override fun onDestroy() { super.onDestroy() updateHandler.removeCallbacks(updateRunnable) player?.release() player = null }
整合到你的初始化代码里
把上面定义的playbackStateListener替换你原来的监听器,确保在初始化播放器时添加:
// 替换你原有的监听器添加代码 player!!.addListener(playbackStateListener)
这样就完成了!现在当播放MPD直播流时,进度条会自动切换到你想要的时间显示模式,非直播流则保持默认逻辑。
内容的提问来源于stack exchange,提问作者Caner Güneş




