ffmpeg # How to seek in mp4/mkv/ts/flv

ffmpeg # How to seek in mp4/mkv/ts/flv_第1张图片
调用堆栈
ffmpeg # How to seek in mp4/mkv/ts/flv_第2张图片
seek的秘密

s->iformat->read_seek

static int seek_frame_internal(AVFormatContext *s, int stream_index,
                               int64_t timestamp, int flags)
{

...
    /* first, we try the format specific seek */
    if (s->iformat->read_seek) {
        ff_read_frame_flush(s);
        ret = s->iformat->read_seek(s, stream_index, timestamp, flags);
    } else
        ret = -1;
    if (ret >= 0)
        return 0;

...
}
mp4
mkv

ff_seek_frame_binary

static int seek_frame_internal(AVFormatContext *s, int stream_index,
                               int64_t timestamp, int flags)
{
...
if (s->iformat->read_timestamp &&
        !(s->iformat->flags & AVFMT_NOBINSEARCH)) {
        ff_read_frame_flush(s);
        return ff_seek_frame_binary(s, stream_index, timestamp, flags);
    }
...
}
ts

seek_frame_generic

flv with keyframe list
static int seek_frame_generic(AVFormatContext *s, int stream_index,
                              int64_t timestamp, int flags)
{
    int index;
    int64_t ret;
    AVStream *st;
    AVIndexEntry *ie;

    st = s->streams[stream_index];

    index = av_index_search_timestamp(st, timestamp, flags); // 成功 找到目标关键帧

...
}
flv without keyframe list
static int seek_frame_generic(AVFormatContext *s, int stream_index,
                              int64_t timestamp, int flags)
{
    int index;
    int64_t ret;
    AVStream *st;
    AVIndexEntry *ie;

    st = s->streams[stream_index];

    index = av_index_search_timestamp(st, timestamp, flags); // 失败

    ...

        // 逐帧读取
        for (;;) {
            int read_status;
            do {
                read_status = av_read_frame(s, &pkt); // 读取一帧
            } while (read_status == AVERROR(EAGAIN));
            if (read_status < 0)
                break;
            // 如果获取的dts大于目标timestamp
            if (stream_index == pkt.stream_index && pkt.dts > timestamp) { 
                if (pkt.flags & AV_PKT_FLAG_KEY) { // 并且是关键帧
                    av_packet_unref(&pkt);
                    break;
                }
                if (nonkey++ > 1000 && st->codecpar->codec_id != AV_CODEC_ID_CDGRAPHICS) {
                    av_log(s, AV_LOG_ERROR,"seek_frame_generic failed as this stream seems to contain no keyframes after the target timestamp, %d non keyframes found\n", nonkey);
                    av_packet_unref(&pkt);
                    break;
                }
            }
            av_packet_unref(&pkt);
        }
        index = av_index_search_timestamp(st, timestamp, flags); // 找到目标关键帧
}

注: ffmpeg的版本如下:

ffmpeg version N-92079-gab492f9322 Copyright (c) 2000-2018 the FFmpeg developers
  built with Apple LLVM version 10.0.0 (clang-1000.11.45.2)
  configuration: --pkg-config-flags=--static --disable-doc --enable-version3 --enable-debug=3 --enable-gpl --disable-optimizations --disable-stripping --disable-ffplay --disable-ffprobe --disable-decoder=vp9
  libavutil      56. 19.101 / 56. 19.101
  libavcodec     58. 31.101 / 58. 31.101
  libavformat    58. 18.103 / 58. 18.103
  libavdevice    58.  4.104 / 58.  4.104
  libavfilter     7. 33.100 /  7. 33.100
  libswscale      5.  2.100 /  5.  2.100
  libswresample   3.  2.100 /  3.  2.100
  libpostproc    55.  2.100 / 55.  2.100

你可能感兴趣的:(ffmpeg # How to seek in mp4/mkv/ts/flv)