阅读 458

关于Android VideoView seekTo不准确的解决方案

实现 Android 的视频播放,通常有以下几种方式

  1. 使用自带的播放器,指定 ActionACTION_VIEWDataUriType 为其 MIME 类型。
  2. 使用 VideoView 来播放,在布局文件中使用 VideoView 结合 MediaController 来实现对其控制。
  3. 使用 MediaPlayer 类和 SurfaceView 来实现,这种方式很灵活。

使用 VideoView 播放视频的步骤:

  1. 在界面布局文件中定义 VideoView 组件,或在程序中创建 VideoView 组件
  2. 调用 VideoView 的如下两个方法来加载指定的视频

setVidePath(String path) // 加载 path 文件路径对应的视频 setVideoURI(Uri uri) // 加载 uri 所对应的视频

  1. 调用 start()、stop()、pause()方法来控制视频的播放
  2. 通过与 MediaController 类结合使用,开发者可以不用自己控制播放与暂停

调用 seekTo 方法跳转不准的问题

典型场景:

当用户从后台恢复播放界面时,需要跳转到之前退出的时间点继续播放原来的视频。其实现逻辑大致上是:

  1. 在暂停时保存当前 VideoViewcurrantPosition 进度
  2. 恢复播放时,调用 seekTo 方法,传入 currantPosition 作为跳转参数

按照官方提供的 API 来看,这是最合理的使用方式。但在某些情况下,我们会遇到视频恢复播放时进度位置不准的问题,甚至有些会重头开始播放。

下面就针对使用 VideoView 播放视频时 seekTo 跳转不准的问题进行分析。

问题定位

1. 消除方法异步执行的影响

首先明确一点: VideoViewseekTo 方法是异步执行的,因此会有 seek 未完成但播放已经开始的现象。需要消除 seekTo 对恢复播放的影响,应该在 seek 操作完成的 seekComplete 回调方法中执行 ViedeoViewstart 方法。

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 盘。

总结

在深究问题的原因时不可浅尝而止,也不要一味的怀疑是不是代码造成了问题。很多情况下都选择盲目地替换不同的视频组件出实现,而忽略了视频源文件本身的问题。