android seekto 黑屏,Android-VideoView.seekTo() 不正确问题原因分析

困惑:

最近项目里出现了一些视频,当我们使用系统自带的VideoView去播放的时候,暂停播放后再次播放,或者拖动进度条改变seekTo之后,都无法正确的回到预期位置重新开始。而且奇怪的是,它总会总相邻的固定的几个时间点开始播放。比如我把进度拖动到10-19秒,手一松开就自动从10秒开始播放。那具体原因是什么呢,我们今天就来一探究竟。

9ac774b72aee

QQ20201110-172456@2x.png

9ac774b72aee

QQ20201110-172514@2x.png

大致意思就是要从 VideoView 的 MediaPlayer 的 onSeekComplete() 入手,把VideoView.start() 放在它之后

// 设置 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;

}

}

});

}

});

第二步

改完之后发现问题还是存在,奇怪了,why?

原因有二:

1、上述操作是针对边下边播、或者来源于网络端的视频的,而楼主遇到的视频都是已经下好在本地的,所以药不对口;

2、真正的原因还是因为视频的关键帧不足,我们看一下VideoView.seekTo()的源码。

@Override

public void seekTo(int msec) {

if (isInPlaybackState()) {

mMediaPlayer.seekTo(msec);

mSeekWhenPrepared = 0;

} else {

mSeekWhenPrepared = msec;

}

}

/**

* Seeks to specified time position.

* Same as {@link #seekTo(long, int)} with {@code mode = SEEK_PREVIOUS_SYNC}.

*

* @param msec the offset in milliseconds from the start to seek to

* @throws IllegalStateException if the internal player engine has not been

* initialized

*/

public void seekTo(int msec) throws IllegalStateException {

seekTo(msec, SEEK_PREVIOUS_SYNC /* mode */);

}

/**

* Seek modes used in method seekTo(long, int) to move media position

* to a specified location.

*

* Do not change these mode values without updating their counterparts

* in include/media/IMediaSource.h!

*/

/**

* This mode is used with {@link #seekTo(long, int)} to move media position to

* a sync (or key) frame associated with a data source that is located

* right before or at the given time.

*

* @see #seekTo(long, int)

*/

public static final int SEEK_PREVIOUS_SYNC = 0x00;

seekTo 默认使用了 SEEK_PREVIOUS_SYNC 作为 SeekMode,而SEEK_PREVIOUS_SYNC 的作用就是将位置移动到关键帧。

好了,明确了原因,我们就通过 ffmpeg 的指令来验证这一猜想吧。

首先,确保你电脑上编译了ffmpeg,然后在控制台输入查看帧信息的指令

ffprobe -show_frames -select_streams video input.mp4

接下来给大家贴一下我有问题的视频帧信息

9ac774b72aee

QQ20201110-174736@2x.png

30秒的视频才4个关键帧(还包括了0秒的第一个关键帧),难怪一直seekTo不正确。

通过ffmpeg的指令

ffmpeg -i input.mp4 -keyint_min 60 -g 60 -sc_threshold 0 -y output.mp4

对关键帧数量进行处理后得到

9ac774b72aee

QQ20201110-175224@2x.png

经过我的测试,发现只要符合每隔2.5秒一个关键帧的规律,视频seekTo都可以正确播放。

当然,由于对ffmpeg的指令还不是特别熟悉,而且ffmpeg不同版本对命令对支持也不尽相同,这里声明一下,应该还有别的指令可以对关键帧进行转化,不必拘泥于我上面写的。

当然,作为前端开发人员也可以考虑和后端、UGC同学沟通,在视频源头解决此问题。毕竟无论是要解决在线播放seekTo不准,或是本地先转化再播放都是不太合理的,对吧?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值