前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android FFmpeg系列08--seek和精准seek

Android FFmpeg系列08--seek和精准seek

作者头像
雪月清
发布2022-11-19 09:56:37
2.4K0
发布2022-11-19 09:56:37
举报
文章被收录于专栏:雪月清的随笔

引言

seek功能的基本实现是比较简单的,不过要做到连续正向&逆向seek播放流畅不卡顿要做的优化点其实是比较多的

本篇文章仅讲述如何使用FFmpeg来实现最基本的seek和精准seek功能

seek api

FFmpeg实现seek功能,可以通过avformat.h中提供的两种接口来实现

av_seek_frame

avformat_seek_file

avformat_seek_file函数内部调用链路如下

可以看到内部优先执行read_seek2,不支持则回退到av_seek_frame

两种api的接口参数使用都是类似的,这里我们以avformat_seek_file为例

AVFormatContext *s:媒体文件打开的上下文结构指针

int stream_index:流索引;如果指定流索引,则基于AVStream.time_base,如果为-1,则会默认选择一条stream且基于AV_TIME_BASE

int64_t min_ts:最小可接受时间戳

int64_t max_ts:最大可接受时间戳

int64_t ts:目标时间戳,就是我们要seek到的地方

int flags

代码语言:javascript
复制
// seek backward
// seek到请求的timestamp之前的最近的关键帧
#define AVSEEK_FLAG_BACKWARD 1

// seeking based on position in bytes
// 基于字节位置的查找,如果flags中包含该标志位,那么时间戳的单位要转换为字节,也就是基于文件中的坐标
#define AVSEEK_FLAG_BYTE 2

// seek to any frame, even non-keyframes
// 可以seek到任意帧,不一定是关键帧,可能导致花屏、马赛克等问题
#define AVSEEK_FLAG_ANY 4

// seeking based on frame number
// 基于帧数量快进
#define AVSEEK_FLAG_FRAME 8

seek操作要点:

  • 确定time_base,计算seek的target timestamp
  • 刷新解码器缓冲avcodec_flush_buffers
  • 刷新外部自行控制的缓冲区,比如avpacket queue、avframe queue等
  • 重新计算音画同步中的startTime

Demo实现

基本Seek功能

UI采用SeekBar,此处我们仅在监听到onStopTrackingTouch回调时执行seek操作

代码语言:javascript
复制
override fun onStopTrackingTouch(seekBar: SeekBar?) {
    seekBar?.let {
        val timestamp = seekBar.progress / 100f * mDuration
        mPlayer.seek(timestamp)
    }
}

基于视频流的seek

代码语言:javascript
复制
// pos单位为s,转换为视频流的timestamp
int64_t seekPos = av_rescale_q((int64_t)(pos * AV_TIME_BASE), AV_TIME_BASE_Q, mTimeBase);

// 执行seek
int ret = avformat_seek_file(mFtx, mVideoStreamIndex,
                             INT64_MIN, seekPos, INT64_MAX, AVSEEK_FLAG_BACKWARD);

// 刷新codec缓冲
avcodec_flush_buffers(mCodecContext);

刷新视频avpacket队列

代码语言:javascript
复制
mVideoPacketQueue->clear();

精准Seek

上面的方式执行后,我们看到的画面是seek到target timestamp最近的关键帧的画面

如果要实现精准seek,从seek点最近的关键帧位置处挨个解码到目标时间点为止即可

代码语言:javascript
复制
// precision seek
if (mAvFrame->pts < mSeekPos) {
    // discard
}

通过ffprobe dump出演示视频的所有视频帧信息

ffprobe -show_frames -select_streams v oceans.mp4 > v_info.txt

可以看到该视频总共有28个关键帧,头两个关键帧间隔了5s多时间

也就是说当我们需要精准seek到第2s、第4s时,都是先seek到pts=0的关键帧上,然后挨个解码到目标seek时间点上(不进行优化的话,可以想象画面卡顿时间是比较长的)

Demo效果

拖动SeekBar结束的时候即可触发seek

参考

1.【FFmpeg源码分析:av_seek_frame()与avformat_seek_file()】

https://blog.csdn.net/u011686167/article/details/123029573

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-09-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 雪月清的随笔 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档