本文章为原创,转载时请注明出处
实现 Android 的视频播放,通常有以下几种方式
1.使用自带的播放器,指定 Action
为 ACTION_VIEW
,Data
为 Uri
,Type
为其 MIME
类型。
2.使用 VideoView 来播放,在布局文件中使用 VideoView 结合
MediaController
来实现对其控制。
3.使用 MediaPlayer
类和 SurfaceView
来实现,这种方式很灵活。
使用 VideoView 播放视频的步骤:
1.在界面布局文件中定义 VideoView 组件,或在程序中创建
VideoView 组件
2.调用 VideoView 的如下两个方法来加载指定的视频
setVidePath(String path) // 加载path文件代表的视
setVideoURI(Uri uri) // 加载uri所对应的视频
3.调用 start()、stop()、pause()
方法来控制视频的播放
4.通过与 MediaController
类结合使用,开发者可以不用自己控制播放与暂停
调用 seekTo 方法跳转不准的问题
典型场景:
当用户从后台恢复播放界面时,需要跳转到之前退出的时间点继续播放原来的视频。其实现逻辑大致上是:
- 在暂停时保存当前
VideoView
的currantPosition
进度 - 恢复播放时,调用
seekTo
方法,传入currantPosition
作为跳转参数
按照官方提供的 API 来看,这是最合理的使用方式。但在某些情况下,我们会遇到视频恢复播放时进度位置不准的问题,甚至有些会重头开始播放。
下面就针对使用 VideoView
播放视频时 seekTo
跳转不准的问题进行分析。
问题定位
1. 消除方法异步执行的影响
首先明确一点: *** VideoView
的 seekTo
方法是异步执行的***,因此会有 seek
未完成但播放已经开始的现象。需要消除 seekTo
对恢复播放的影响,应该在 seek
操作完成的 seekComplete
回调方法中执行 ViedeoView
的 start
方法。
Tip:
seekComplete
属于MediaPlayer
类的OnPreparedListener
监听器的一个回调方法。虽然VideoView
是基于MediaPlayer
实现的,但没提供setOnSeekCompleteListener
设置监听器的方法,所以我们要拿到VideoView
内部持有MediaPlayer
对象。
// 设置 VideoView 的 OnPrepared 监听,拿到 MediaPlayer 对象。
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
//设置 MediaPlayer 的 OnSeekComplete 监听
mp.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() {
@Override
public void onSeekComplete(MediaPlayer mp) {
// seekTo 方法完成时的回调
if(isPause){
videoView.start();
isPause = false;
}
}
});
}
});
2. 消除视频源的问题
其实 seekTo
跳转的位置其实并不是参数所带的 position
,而是离 position
最近的视频关键帧。
关于视频关键帧建议大家可以去了解一下相关知识,大致上就是视频播放时需要从一个关键帧的位置开始。
所以当视频在跳转到相应的 position
位置缺少关键帧的情况下,调用 seekTo
方法是无法在当前位置开始播放。这时会寻找离指定 position
最近的关键帧位置开始播放。
关于视频源造成的问题,可以采取以下解决措施:
- 替换成满足需求的视频源文件(寻找合格的视频文件)
- 对视频源文件进行处理,增加其关键帧数量,比如可以1s设置一个关键帧(基于目前已有的视频文件进行处理)。
如果选择第二种方式,要增加视频的关键帧数量,可以推荐大家使用FFmpeg进行增加关键帧的处理工作。 http://ffmpeg.org/
FFmpeg 工具相关命令行语句:
ffmpeg.exe -i "D:\in.mp4" -c:v libx264 -preset superfast -x264opts keyint=25 -acodec copy -f mp4 "D:\out.mp4"
命令语句大致意思是:在 D 盘路径下把 in.mp4 视频文件每隔 25 帧设置一个关键帧,音轨保持原视频参数,其余使用 FFmpeg 提供的default 值,最后保存为 out.mp4 文件到 D 盘。
总结
在深究问题的原因时不可浅尝而止,也不要一味的怀疑是不是代码造成了问题。很多情况下都选择盲目地替换不同的视频组件出实现,而忽略了视频源文件本身的问题。