avformat_seek_file
/**
* Seek to timestamp ts.
* Seeking will be done so that the point from which all active streams
* can be presented successfully will be closest to ts and within min/max_ts.
* Active streams are all streams that have AVStream.discard < AVDISCARD_ALL.
*
* If flags contain AVSEEK_FLAG_BYTE, then all timestamps are in bytes and
* are the file position (this may not be supported by all demuxers).
* If flags contain AVSEEK_FLAG_FRAME, then all timestamps are in frames
* in the stream with stream_index (this may not be supported by all demuxers).
* Otherwise all timestamps are in units of the stream selected by stream_index
* or if stream_index is -1, in AV_TIME_BASE units.
* If flags contain AVSEEK_FLAG_ANY, then non-keyframes are treated as
* keyframes (this may not be supported by all demuxers).
* If flags contain AVSEEK_FLAG_BACKWARD, it is ignored.
*
* @param s media file handle
* @param stream_index index of the stream which is used as time base reference
* @param min_ts smallest acceptable timestamp
* @param ts target timestamp
* @param max_ts largest acceptable timestamp
* @param flags flags
* @return >=0 on success, error code otherwise
*
* @note This is part of the new seek API which is still under construction.
* Thus do not use this yet. It may change at any time, do not expect
* ABI compatibility yet!
*/
int avformat_seek_file(AVFormatContext *s,
int stream_index,
int64_t min_ts,
int64_t ts,
int64_t max_ts,
int flags);
If flags contain AVSEEK_FLAG_BYTE, then all timestamps are in bytes and
are the file position (this may not be supported by all demuxers).
如果flags 中包含AVSEEK_FLAG_BYTE,那么时间戳的单位应该为字节,也就是在文件中的坐标。
av_seek_frame
/**
* Seek to the keyframe at timestamp.
* 'timestamp' in 'stream_index'.
*
* @param s media file handle
* @param stream_index If stream_index is (-1), a default
* stream is selected, and timestamp is automatically converted
* from AV_TIME_BASE units to the stream specific time_base.
* @param timestamp Timestamp in AVStream.time_base units
* or, if no stream is specified, in AV_TIME_BASE units.
* @param flags flags which select direction and seeking mode
* @return >= 0 on success
*/
int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp,
int flags);
Seek to the keyframe at timestamp.
flags可能包含的值如下:
#define AVSEEK_FLAG_BACKWARD 1 ///< seek backward
#define AVSEEK_FLAG_BYTE 2 ///< seeking based on position in bytes
#define AVSEEK_FLAG_ANY 4 ///< seek to any frame, even non-keyframes
#define AVSEEK_FLAG_FRAME 8 ///< seeking based on frame number
AVSEEK_FLAG_BACKWARD是seek到请求的timestamp之前最近的关键帧
AVSEEK_FLAG_BYTE 是基于字节位置的查找
AVSEEK_FLAG_ANY 是可以seek到任意帧,注意不一定是关键帧,因此使用时可能会导致花屏
AVSEEK_FLAG_FRAME是基于帧数量快进
flags可能同时包含以上的多个值。如AVSEEK_FLAG_BACKWARD和AVSEEK_FLAG_BYTE
flags = AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_BYTE;
AVSEEK_FLAG_BACKWARD
av_seek_frame 可以帮我们定位到关键帧和非关键帧。定位到非关键帧肯定是不行的,因为视频的解码需要依赖于关键帧。
如果跳过关键帧,解码出来的图像是会出现马赛克的,影响用户体验。
通常情况下,我们的 seek 接口都是以 ms 为单位的,如果指定的 ms 时间点,刚好不是关键帧(这个概率很大),ffmpeg 会自动往回 seek 到最近的关键帧,这就是 AVSEEK_FLAG_BACKWARD 这个 flag 的含义。
如果不加入这个 flag,av_seek_frame 可以精确的定位到 timestamp 附近的音视频帧上,但是不会保证是关键帧;如果加上这个 flag 可以保证关键帧,但是又没法保证 seek 的精度,因为有可能会往回 seek。
通常使用的seek方式为BACKWARD,这种方式查找虽然定位并不是非常精确,但是能够很好的处理掉马赛克的问题,因为BACKWARD的方式会去向回查找keyframe处,定位到keyframe处。
AVSEEK_FLAG_BYTE
调用堆栈.png
调用堆栈: avformat_seek_file->avformat_seek_file->seek_frame_internal->seek_frame_byte
- avformat_seek_file用法:
int avformat_seek_file(AVFormatContext *s,
int stream_index,
int64_t min_ts,
int64_t ts,
int64_t max_ts,
int flags);
avformat_seek_file (pFormatCtx,-1,INT64_MIN,pFormatCtx->data_offset,INT64_MAX, AVSEEK_FLAG_BYTE);
- av_seek_frame
int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp,
int flags);
- seek_frame_internal
static int seek_frame_internal(AVFormatContext *s, int stream_index,
int64_t timestamp, int flags)
{
int ret;
AVStream *st;
if (flags & AVSEEK_FLAG_BYTE) {
if (s->iformat->flags & AVFMT_NO_BYTE_SEEK)
return -1;
ff_read_frame_flush(s);
return seek_frame_byte(s, stream_index, timestamp, flags);
}
...
}
- seek_frame_byte
static int seek_frame_byte(AVFormatContext *s, int stream_index,
int64_t pos, int flags)
{
int64_t pos_min, pos_max;
pos_min = s->internal->data_offset;
pos_max = avio_size(s->pb) - 1;
if (pos < pos_min)
pos = pos_min;
else if (pos > pos_max)
pos = pos_max;
avio_seek(s->pb, pos, SEEK_SET);
s->io_repositioned = 1;
return 0;
}
References:
https://github.com/rockcarry/ffplayer/wiki/%E9%9A%BE%E7%82%B9-seek-%E6%93%8D%E4%BD%9C
https://blog.csdn.net/m0_37684310/article/details/79544720
http://bbs.chinaffmpeg.com/forum.php?mod=viewthread&tid=14
网友评论