ExoPlayer电台流播放中断后延迟恢复问题(蓝牙场景)求助
ExoPlayer电台流媒体播放故障:蓝牙耳机下播放中断延迟恢复
问题描述
我在使用ExoPlayer进行电台流媒体播放时遇到故障:播放过程中流会中断,延迟一段时间后才恢复,且该问题仅在使用蓝牙耳机时可复现。
相关日志
2020-06-29 12:59:02.577 6159-6645/com.example.player E/AudioTrack: start(): 0x931f7000, mState = 1 2020-06-29 12:59:02.584 6159-6159/com.example.player D/MediaService: onPlayerStateChanged: playWhenReady - true; playbackState - 3 2020-06-29 12:59:02.625 6159-6645/com.example.player D/AudioTrack: correcting timestamp time for pause, currentTimeNanos: 177361604116546 < limitNs: 177364334867316 < mStartNs: 177364574867316 2020-06-29 12:59:02.625 6159-6645/com.example.player W/AudioTrack: retrograde timestamp time corrected, 177364334867316 < 177364578496700 2020-06-29 12:59:02.635 6159-6645/com.example.player D/AudioTrack: correcting timestamp time for pause, currentTimeNanos: 177361604116546 < limitNs: 177364334867316 < mStartNs: 177364574867316 2020-06-29 12:59:02.635 6159-6645/com.example.player W/AudioTrack: retrograde timestamp time corrected, 177364334867316 < 177364578496700 2020-06-29 13:01:58.234 6159-6645/com.example.player D/AudioTrack: pause(): 0x931f7000, mState = 0 2020-06-29 13:01:58.239 6159-6159/com.example.player D/MediaService: onPlayerStateChanged: playWhenReady - true; playbackState - 2 2020-06-29 13:02:05.073 6159-6645/com.example.player E/AudioTrack: start(): 0x931f7000, mState = 2 2020-06-29 13:02:05.079 6159-6159/com.example.player D/MediaService: onPlayerStateChanged: playWhenReady - true; playbackState - 3 2020-06-29 13:02:05.122 6159-6645/com.example.player D/AudioTrack: correcting timestamp time for pause, currentTimeNanos: 177540976640019 < limitNs: 177546830466942 < mStartNs: 177547070466942 2020-06-29 13:02:05.122 6159-6645/com.example.player W/AudioTrack: retrograde timestamp time corrected, 177546830466942 < 177547073697634 2020-06-29 13:02:14.816 6159-6645/com.example.player D/AudioTrack: pause(): 0x931f7000, mState = 0
我的ExoPlayer初始化代码
exoPlayer = SimpleExoPlayer.Builder( this, DefaultRenderersFactory(this), DefaultTrackSelector(this), DefaultLoadControl(), bandwidthMeter, Looper.getMainLooper(), AnalyticsCollector(Clock.DEFAULT), true, Clock.DEFAULT ).build() exoPlayer.playWhenReady = true prepareExoPlayer() setSessionState(PlaybackStateCompat.STATE_PLAYING) ... private fun prepareExoPlayer() { val mediaSource = buildMediaSource() exoPlayer.prepare(mediaSource) } private fun buildMediaSource(): MediaSource { val dataSourceFactory = DefaultDataSourceFactory( this, Util.getUserAgent(baseContext, getString(R.string.app_name)), bandwidthMeter ) var url = currentStation.middleStream return ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(Uri.parse(url)) }
问题分析
从日志可以明确看到关键异常点:
- 13:01:58 AudioTrack进入暂停状态(
pause(): 0x931f7000, mState = 0),此时播放器状态变为playbackState - 2(STATE_BUFFERING) - 直到13:02:05才尝试重新启动AudioTrack,但此时抛出错误
E/AudioTrack: start(): 0x931f7000, mState = 2(mState=2对应AudioTrack的STATE_PAUSED状态,此时调用start属于非法状态) - 后续的时间戳校正警告(
retrograde timestamp time corrected)说明播放时序出现混乱,这是中断后恢复时的典型表现
这种问题在蓝牙耳机场景下出现,核心原因通常是:
- 蓝牙音频路由切换时,系统对AudioTrack的状态管理出现冲突
- 默认的ExoPlayer配置未针对蓝牙音频的高延迟、易中断特性做优化
- 音频焦点的处理不及时,导致播放被系统强制暂停后无法快速恢复
解决方案
1. 配置适配蓝牙的AudioAttributes
为ExoPlayer明确设置音频属性,让系统更好地识别媒体类型并处理蓝牙路由:
val audioAttributes = AudioAttributes.Builder() .setUsage(C.USAGE_MEDIA) .setContentType(C.CONTENT_TYPE_MUSIC) .build() exoPlayer.setAudioAttributes(audioAttributes, true)
添加位置:在exoPlayer.playWhenReady = true之前即可。
2. 优化DefaultLoadControl的缓冲参数
蓝牙传输存在一定延迟,默认的缓冲大小可能不足以应对波动,增大缓冲阈值可以减少中断概率:
val loadControl = DefaultLoadControl.Builder() .setBufferDurationsMs( 3000, // 最小缓冲时长(ms) 10000, // 目标缓冲时长(ms) 1000, // 缓冲不足时的重试间隔 5000 // 播放器后台时的缓冲时长 ) .build() // 初始化ExoPlayer时传入自定义的loadControl exoPlayer = SimpleExoPlayer.Builder( this, DefaultRenderersFactory(this), DefaultTrackSelector(this), loadControl, // 使用自定义配置 bandwidthMeter, Looper.getMainLooper(), AnalyticsCollector(Clock.DEFAULT), true, Clock.DEFAULT ).build()
3. 监听音频焦点变化,主动处理播放状态
注册音频焦点监听器,当蓝牙设备连接/断开时,主动控制ExoPlayer的播放状态,避免系统强制暂停后的状态混乱:
private val audioFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) .setAudioAttributes(audioAttributes) // 复用之前定义的audioAttributes .setOnAudioFocusChangeListener { focusChange -> when (focusChange) { AudioManager.AUDIOFOCUS_LOSS -> { // 失去焦点,暂停播放 exoPlayer.playWhenReady = false } AudioManager.AUDIOFOCUS_GAIN -> { // 重新获得焦点,恢复播放 exoPlayer.playWhenReady = true } AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> { // 短暂失去焦点,降低音量(可选) exoPlayer.volume = 0.3f } AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_CAN_DUCK -> { // 恢复焦点,还原音量 exoPlayer.volume = 1.0f } } } .build() // 在初始化播放器时请求音频焦点 val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager val result = audioManager.requestAudioFocus(audioFocusRequest) if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { // 焦点获取成功,正常初始化播放 exoPlayer.playWhenReady = true }
4. 调整RenderersFactory的音频配置
尝试禁用AudioTrack的某些优化,避免状态冲突:
val renderersFactory = DefaultRenderersFactory(this) .setEnableAudioTrackPlaybackParams(false) // 禁用PlaybackParams,减少状态异常 .setAudioProcessors(arrayOf()) // 禁用额外的音频处理器(如果不需要音效) // 初始化ExoPlayer时传入该factory exoPlayer = SimpleExoPlayer.Builder( this, renderersFactory, // 使用自定义配置 DefaultTrackSelector(this), DefaultLoadControl(), bandwidthMeter, Looper.getMainLooper(), AnalyticsCollector(Clock.DEFAULT), true, Clock.DEFAULT ).build()
验证建议
- 先尝试方案1和方案2,这两个是最常见的解决蓝牙播放中断的手段
- 如果问题仍然存在,再逐步添加方案3和方案4
- 测试时注意观察日志中的AudioState变化,确认是否还有
E/AudioTrack: start()的错误
内容的提问来源于stack exchange,提问作者Роман Уваров
相关产品推荐
相关产品推荐

