如何使用 ffmpeg av_seek_frame() 在具有帧号的情况下读取任何帧

问题描述 投票:0回答:3
int64_t timeBase;
timeBase = (int64_t(pavStrm-> time_base.num) * AV_TIME_BASE) / int64_t(pavStrm->time_base.den);
int64_t seekTarget = int64_t(iFrameNumber) * timeBase;
av_seek_frame(fmt_ctx, -1, seekTarget, AVSEEK_FLAG_FRAME);

这里我想读取 iFrameNumebr 之后的下 5 帧

for(int iCnt = 0; iCnt <= 4; iCnt++)
{
    iRet = av_read_frame(fmt_ctx, &pkt);
        do 
        {
            ret = decode_packet(&got_frame, 0);
            if (ret < 0)
                break;
            pkt.data += ret;
            pkt.size -= ret;

        }while (pkt.size > 0);
    av_free_packet(&pkt);
}

static int decode_packet(int *got_frame, int cached)
{
int ret = 0;
int decoded = pkt.size;
*got_frame = 0;

if (pkt.stream_index == video_stream_idx)
{
    /* decode video frame */
    ret = avcodec_decode_video2(video_dec_ctx, frame, got_frame, &pkt);
}

当我使用 AVSEEK_FLAG_BACKWARD 时,它返回 5 个数据包和 5 个帧,前两个是空白但正确的。

当我使用 AVSEEK_FLAG_FRAME 时,它返回 5 个数据包和 3 个帧(不是第一个 3 帧),它返回视频中的特定帧。

对于任何 iFrameNumber

所以请帮助我如何在有帧号的情况下获取帧以及av_seek_frame()的seektarget第三个参数的确切值是多少

我在将帧转换为 rgb24 格式时也遇到问题

ffmpeg fluent-ffmpeg
3个回答
23
投票

我认为

av_seek_frame()
是最常见但难以理解的函数之一,也没有得到足够的注释。

如果设置了标志

AVSEEK_FLAG_FRAME
,则第三个参数应该是您想要查找的帧号,您做得很好。

让我们看一个例子来更好地理解

av_seek_frame()
:

假设我有一个 10 帧的视频,fps=10。第一帧和第五帧是关键帧(

I Frame
intra frame
)。其他的是P帧甚至某种格式的B帧。

0 1 2 3 4 5 6 7 8 9(帧数)

0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9(时基)

av_seek_frame(fmt_ctx, -1, 2, AVSEEK_FLAG_FRAME);
av_seek_frame(fmt_ctx, -1, 0.15, 0);
// These will seek to the fifth frame. Cause `AVSEEK_FLAG_ANY` is not given. Seeking to the next key frame after third parameter.

av_seek_frame(fmt_ctx, -1, 2, AVSEEK_FLAG_FRAME | AVSEEK_FLAG_ANY);
// This will seek to exactly the third parameter specified. But probably only a frame with no actual meaning. (We can't get a meaningful image if no related I/P/B frames given.)

av_seek_frame(fmt_ctx, -1, 0.15, AVSEEK_FLAG_ANY);
// Seek to 0.2. Nothing interesting as above.

av_seek_frame(fmt_ctx, -1, 0.15, AVSEEK_FLAG_ANY | AVSEEK_FLAG_BACKWARD);
// Seek to 0.1. Also nothing interesting.

av_seek_frame(fmt_ctx, -1, 2, AVSEEK_FLAG_FRAME | AVSEEK_FLAG_BACKWARD);
// Got the first frame. Seeking to the nearest key frame before the third parameter.

因此,如果我想获得任意帧,通常首先使用

AVSEEK_FLAG_BACKWARD
进行查找,然后照常解码。然后检查前几个数据包的点和持续时间,看看是否需要丢弃它们。


10
投票
int64_t FrameToPts(AVStream* pavStream, int frame) const
{
    return (int64_t(frame) * pavStream->r_frame_rate.den *
        pavStream->time_base.den) / (int64_t(pavStream->r_frame_rate.num)
        * pavStream->time_base.num);
}

iSeekTarget = FrameToPts(m_pAVVideoStream, max(0, lFrame));
iSuccess = av_seek_frame(m_pAVFmtCtx, m_iVideo_Stream_idx, 
iSeekTarget, iSeekFlag);

AVPacket avPacket;
iRet = av_read_frame(m_pAVFmtCtx, &avPacket);

1
投票
timeBase = (int64_t(video_stream-> time_base.num) * AV_TIME_BASE) / int64_t(video_stream->time_base.den);
int64_t seekTarget = int64_t(iFrameNumber) * timeBase * (video_stream->time_base.den / video_stream->avg_frame_rate.num);


int iiiret = av_seek_frame(fmt_ctx, -1, seekTarget, AVSEEK_FLAG_FRAME);
© www.soinside.com 2019 - 2024. All rights reserved.