ExoPlayer无法播放视频,二次播放仅音频且触发Audio Sink异常
ExoPlayer播放异常问题修复
问题现象
- 首次无法播放视频
- 第二次播放仅能听到音频,无画面
- 错误日志:
MediaCodecAudioRenderer: 音频输出错误
com.google.android.exoplayer2.audio.AudioSink$UnexpectedDiscontinuityException: 意外的音频轨道时间戳不连续:预期值1000001184217,实际值1000001391224
问题根源
- 单例播放器状态残留:VideoPlayer是单例,切换视频时未完全重置之前的播放状态,新旧媒体时间戳冲突触发音频渲染异常。
- 播放流程不规范:
playVideo方法直接设置媒体项并准备,未先停止当前播放、重置播放位置,导致时间戳混乱。 - 未绑定Composable生命周期:播放器未跟随Composable的生命周期做暂停、资源清理操作,资源占用引发状态异常。
- PlayerView缓存未关联URL变化:
getStyledPlayerView用remember缓存,但视频URL变化时未同步更新播放器媒体内容。
修复代码
1. 规范播放逻辑,重置状态再加载新视频
修改playVideo方法,确保切换视频前清空旧状态:
fun playVideo(url: String) { player?.let { exoPlayer -> // 停止当前播放、重置位置、清空旧媒体 exoPlayer.stop() exoPlayer.seekTo(0) exoPlayer.clearMediaItems() val uri = Uri.parse(url) val mediaItem = MediaItem.fromUri(uri) exoPlayer.setMediaItem(mediaItem) exoPlayer.prepare() exoPlayer.play() } }
2. 绑定Composable生命周期管理播放器
在VideoLayout中添加生命周期监听,避免资源泄漏:
@Composable fun VideoLayout(videoUrl: String?, muted: Boolean = false) { if (videoUrl == null) return val context = LocalContext.current val videoPlayer = VideoPlayer.getInstance() val exoPlayer = videoPlayer.getExoplayer(context) // 实时更新静音状态 LaunchedEffect(muted) { exoPlayer.volume = if (muted) 0f else 1f } // 跟随Composable生命周期处理播放与清理 DisposableEffect(videoUrl) { videoPlayer.playVideo(videoUrl) onDispose { // 退出时暂停播放,清空媒体项(全局单例不要在这里release,避免其他页面无法复用) exoPlayer.pause() exoPlayer.clearMediaItems() } } val exoPlayerView = getStyledPlayerView(context, exoPlayer) exoPlayerView.useController = false AndroidView( modifier = Modifier.fillMaxSize().background(Color.Black, shape = RectangleShape), factory = { exoPlayerView } ) }
3. 确保PlayerView随播放器状态更新
修改getStyledPlayerView,依赖播放器实例确保状态同步:
@Composable private fun getStyledPlayerView( context: Context, exoPlayer: ExoPlayer ) = remember(exoPlayer) { // 播放器变化时重新创建View StyledPlayerView(context).apply { player = exoPlayer resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT } }
4. 优化单例播放器初始化
确保getExoplayer仅在播放器为空时初始化:
// VideoPlayer类中的getExoplayer方法示例 fun getExoplayer(context: Context): ExoPlayer { if (player == null) { val dataSourceFactory = DefaultHttpDataSource.Factory() val mediaSourceFactory = DefaultMediaSourceFactory(dataSourceFactory) player = ExoPlayer.Builder(context) .setMediaSourceFactory(mediaSourceFactory) .build() } return player!! }
额外注意事项
- 全局单例播放器不要在单个Composable中调用
release(),建议在应用退出时统一释放。 - 若仍无画面,检查视频格式是否被ExoPlayer支持,可添加日志查看视频渲染器状态。
内容的提问来源于stack exchange,提问作者sreejith
相关产品推荐
相关产品推荐

