如何在Media3中添加本地SRT字幕?及PlayerView超时设置问题
Media3 实现SRT字幕与PlayerView超时逻辑修改方案
一、加载同名SRT字幕
Media3中加载外部字幕的核心是通过SubtitleMediaItem将字幕文件关联到主媒体项,具体步骤如下:
- 生成字幕文件路径:从视频文件路径替换后缀得到SRT路径,比如视频路径为
file:///storage/emulated/0/video.mp4,则字幕路径为file:///storage/emulated/0/video.srt。 - 配置字幕数据源:使用
LocalUriDataSource.Factory适配本地SRT文件的数据源需求。 - 构建字幕媒体项:指定字幕格式(SRT对应
MimeTypes.APPLICATION_SUBRIP)和数据源,构建SubtitleMediaItem。 - 关联到主媒体项:将字幕媒体项添加到
MediaItem的字幕列表,再加载到播放器。
示例代码:
// 视频文件Uri val videoUri = Uri.parse("file:///storage/emulated/0/video.mp4") // 生成同名SRT字幕Uri val subtitleUri = Uri.parse(videoUri.toString().replace(".mp4", ".srt")) // 字幕数据源工厂 val subtitleDataSourceFactory = LocalUriDataSource.Factory(requireContext()) // 构建字幕媒体项 val subtitleMediaItem = SubtitleMediaItem.Builder() .setUri(subtitleUri) .setMimeType(MimeTypes.APPLICATION_SUBRIP) .setDataSourceFactory(subtitleDataSourceFactory) .build() // 构建主媒体项并关联字幕 val mediaItem = MediaItem.Builder() .setUri(videoUri) .addSubtitle(subtitleMediaItem) .build() // 加载到播放器 player.setMediaItem(mediaItem) player.prepare()
如果需要自动检测目录下的同名字幕,可以封装工具方法遍历视频所在目录,匹配后缀为.srt的同名文件。
二、修改PlayerView控件超时逻辑
app:show_timeout="3000"是默认控制器的初始显示时长,默认逻辑会让进度条先隐藏、整体控件延迟两倍时间隐藏。要自定义超时逻辑,可通过以下两种方案实现:
方案1:自定义ControllerBehavior
继承DefaultPlayerControlView.ControllerBehavior,重写updateVisibility方法,完全自定义显示隐藏的延迟规则:
class CustomControllerBehavior(context: Context, attrs: AttributeSet?) : DefaultPlayerControlView.ControllerBehavior(context, attrs) { private val hideHandler = Handler(Looper.getMainLooper()) private val hideRunnable = Runnable { controlView?.hide() } override fun updateVisibility(isPlaying: Boolean, userInitiated: Boolean) { // 移除之前的延迟任务 hideHandler.removeCallbacks(hideRunnable) controlView?.show() // 自定义延迟规则:播放时5秒后隐藏,暂停时10秒后隐藏 val delayMs = if (isPlaying) 5000L else 10000L hideHandler.postDelayed(hideRunnable, delayMs) } }
然后在布局中指定自定义Behavior,同时禁用默认超时:
<androidx.media3.ui.PlayerView android:id="@+id/playerView" android:layout_width="match_parent" android:layout_height="match_parent" app:controller_behavior="com.yourpackage.CustomControllerBehavior" app:show_timeout="0"/>
方案2:监听控制器可见性
通过setControllerVisibilityListener手动控制显示隐藏时机:
playerView.setControllerVisibilityListener { visibility -> if (visibility == View.VISIBLE && player.isPlaying) { // 播放时4秒后隐藏控制器 Handler(Looper.getMainLooper()).postDelayed({ playerView.hideController() }, 4000) } } // 点击屏幕时重新显示控制器 playerView.setOnClickListener { playerView.showController() }
三、代码优化建议
- 绑定生命周期:将播放器与Fragment生命周期绑定,避免内存泄漏:
lifecycle.addObserver(object : DefaultLifecycleObserver { override fun onStart(owner: LifecycleOwner) { player.playWhenReady = true } override fun onStop(owner: LifecycleOwner) { player.playWhenReady = false } override fun onDestroy(owner: LifecycleOwner) { player.release() } }) - 逻辑分离:将媒体项构建、字幕加载等逻辑抽成单独工具类或方法,避免Fragment代码臃肿。
- 错误处理:添加字幕加载失败的监听,通过
Player.Listener捕获异常:player.addListener(object : Player.Listener { override fun onError(error: PlaybackException) { if (error.errorCode == PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND) { // 提示用户字幕文件不存在 } } }) - 播放器复用:后续迁移到Activity时,建议用ViewModel管理播放器实例,避免重复创建销毁。
内容的提问来源于stack exchange,提问作者tivir13126
相关产品推荐
相关产品推荐

