gpt4 book ai didi

c++ - 如何在 av_seek_frame 之后获取当前的 AVFrame 序号?

转载 作者:行者123 更新时间:2023-12-04 23:11:19 28 4
gpt4 key购买 nike

我是解码器和 FFmpeg 的新手。我需要的是实现可以通过某个步骤读取帧的逻辑(例如:20),换句话说,我有一个文件,我需要读取帧 0、20、40、60 ......
我所做的是


AVFrame * m_pAVFrame = nullptr;
int firstFrameIdx = 0;

while(true)
{

if(firstFrameIdx > 0)
{
int64_t seekTarget = FrameToPts(m_pAVStream, firstFrameIdx);
nRet = av_seek_frame(m_pAVFormatCtx, m_streamIdx, seekTarget, AVSEEK_FLAG_FRAME);
}

nRet = av_read_frame(m_pAVFormatCtx, m_pAVPkt);
ret = avcodec_send_packet(m_pAVCodecCtx, m_pAVPkt);
ret = avcodec_receive_frame(m_pAVCodecCtx, m_pAVFrame);

firstFrameIdx+=20;
}

但问题是 av_seek_frame将指针移动到 Iframe,假设视频文件有每个 15 的关键帧(当然它可能是不同的数字),例如 0、15、30 ......所以这意味着如果我尝试寻找第 20 帧,实际上我得到了到第 15 帧。
我看到 AVFrame有房产 coded_picture_number这在我的情况下可能很有用,我尝试将这些返回值放入 vector 中,我发现这些值无关紧要
enter image description here
我期望看到的是 0, 15, 30, 45...例如,如果我可以进行搜索然后获取一个帧来询问订单号(例如:15),那么我可以理解 Iframe 是 15,为了达到第 20 帧,我需要阅读并跳过 5 帧所以结果,我到了第 20 帧,但是正如你在上面看到的那样,我询问订单号并得到奇怪的值,如 0, 2, 1, 3...没有任何关系...
我觉得我错过了一些基本知识,有人可以解释如何制定搜索逻辑并找到正确的框架吗?
更新
初始化逻辑
bool FFmpegDecoder::Init(unsigned char const * pData, int dataSize, int reqId, bool bUseHWAccel, FFmpegDecoderCallback * pCB)
{
Deinit();

// From memory:
if (pData == nullptr || dataSize == 0)
{
printf("FFmpegDecoder::Init FAILED: neither filename nor memory data were given !\n");
return false;
}
m_pIoCtx = std::make_shared<AVIOContextMem>(pData, dataSize);

if (m_pIoCtx->IsValid() == false)
{
printf("FFmpegDecoder::Init FAILED: m_pIoCtx is nullptr !\n");
return false;
}

m_reqId = reqId;
m_bUseHWAccel = bUseHWAccel;
m_pCB = pCB;
m_pData = pData;
m_dataSize = dataSize;

m_bRequestedAbort = false;

m_pAVPkt = av_packet_alloc();
av_init_packet(m_pAVPkt);

m_pAVFrame = av_frame_alloc();
m_pAVFrameRGB = av_frame_alloc();

if (m_bUseHWAccel)
{
m_pSwAVFrameForHw = av_frame_alloc();
}

m_pAVFormatCtx = avformat_alloc_context();
m_pIoCtx->initAVFormatContext(m_pAVFormatCtx);

if (avformat_open_input(&m_pAVFormatCtx, "", nullptr, nullptr) != 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in avformat_open_input\n");
return false;
}

if (avformat_find_stream_info(m_pAVFormatCtx, nullptr) < 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in avformat_find_stream_info\n");
return false;
}


//av_dump_format(ctx_format, 0, "", false);
for (int i = 0; i < (int)m_pAVFormatCtx->nb_streams; i++)
{
if (m_pAVFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
m_streamIdx = i;
m_pAVStream = m_pAVFormatCtx->streams[i];
break;
}
}
if (m_pAVStream == nullptr)
{
printf("FFmpegDecoder::InitFFmpeg: failed to find video stream\n");
return false;
}

m_pAVCodec = avcodec_find_decoder(m_pAVStream->codecpar->codec_id);
if (!m_pAVCodec)
{
printf("FFmpegDecoder::InitFFmpeg: error in avcodec_find_decoder\n");
return false;
}

m_pAVCodecCtx = avcodec_alloc_context3(m_pAVCodec);
if (!m_pAVCodecCtx)
{
printf("FFmpegDecoder::InitFFmpeg: error in avcodec_alloc_context3\n");
return false;
}

if (avcodec_parameters_to_context(m_pAVCodecCtx, m_pAVStream->codecpar) < 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in avcodec_parameters_to_context\n");
return false;
}

if (m_bUseHWAccel)
{
AVHWDeviceType hwDevType = AV_HWDEVICE_TYPE_DXVA2;
g_hwPixFormat = find_fmt_by_hw_type(hwDevType);
m_pAVCodecCtx->get_format = get_hw_format;
av_opt_set_int(m_pAVCodecCtx, "refcounted_frames", 1, 0);
if (av_hwdevice_ctx_create(&m_pBufferRefForHw, hwDevType, NULL, NULL, 0) < 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in av_hwdevice_ctx_create\n");
return false;
}
m_pAVCodecCtx->hw_device_ctx = av_buffer_ref(m_pBufferRefForHw);
}

if (avcodec_open2(m_pAVCodecCtx, m_pAVCodec, nullptr) < 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in avcodec_open2\n");
return false;
}

m_pAVFrameRGB->format = AV_PIX_FMT_BGR24;
m_pAVFrameRGB->width = m_pAVCodecCtx->width;
m_pAVFrameRGB->height = m_pAVCodecCtx->height;
if (av_frame_get_buffer(m_pAVFrameRGB, 32) != 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in av_frame_get_buffer\n");
return false;
}

m_streamRotationDegrees = GetAVStreamRotation(m_pAVStream);
m_estimatedFramesCount = 0;
assert(m_pAVFormatCtx->nb_streams > 0);
if (m_pAVFormatCtx->nb_streams > 0)
{
m_estimatedFramesCount = m_pAVFormatCtx->streams[0]->nb_frames;
}

//InitConvertColorSpace
// Init converter from YUV420p to BGR:
if (m_bUseHWAccel)
{
m_pSwsCtxConvertImg = sws_getContext(m_pAVCodecCtx->width, m_pAVCodecCtx->height, AV_PIX_FMT_NV12, m_pAVCodecCtx->width, m_pAVCodecCtx->height, AV_PIX_FMT_RGB24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
}
else
{
m_pSwsCtxConvertImg = sws_getContext(m_pAVCodecCtx->width, m_pAVCodecCtx->height, m_pAVCodecCtx->pix_fmt, m_pAVCodecCtx->width, m_pAVCodecCtx->height, AV_PIX_FMT_RGB24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
}
if (!m_pSwsCtxConvertImg)
{
printf("FFmpegDecoder::InitFFmpeg: error in sws_getContext\n");
return false;
}

m_bInitOK = true;
return true;
}
具有最后更改的解码逻辑
void FFmpegDecoder::DecodeWithStep(int step)
{
step = 20;
int currentFramePos = 0;
int number_of_errors = 0;
const int MAX_ERROR_NUM = 10;

while (true)
{
if (step > 0)
{
int seekPos = currentFramePos + step;
int64_t seekTarget = FrameToPts(m_pAVStream, seekPos);

if (av_seek_frame(m_pAVFormatCtx, m_streamIdx, seekTarget, AVSEEK_FLAG_FRAME) < 0)
{
number_of_errors++;
}
else
{
currentFramePos = seekPos;
m_is_seeked = true;
}
}

if (av_read_frame(m_pAVFormatCtx, m_pAVPkt) == 0)
{
if (m_pAVPkt->stream_index == m_streamIdx) //to make sure that I dont get packets from other streams
{
if (m_is_seeked)
{
avcodec_flush_buffers(m_pAVCodecCtx);
m_is_seeked = false;
}

if (avcodec_send_packet(m_pAVCodecCtx, m_pAVPkt) == 0)
{
printf("----- BATCH\n");

while (avcodec_receive_frame(m_pAVCodecCtx, m_pAVFrame) == 0)
{
ProcessFrame(m_pAVFrame);
av_frame_unref(m_pAVFrame);
currentFramePos++;
printf("----- cur position (%d) \n", currentFramePos);
}
}

av_packet_unref(m_pAVPkt);
}
}
else
{
number_of_errors++;
}

if (number_of_errors == MAX_ERROR_NUM)
{
printf("EXIT\n");
break;
}
}
}
更新
初始化逻辑
bool FFmpegDecoder::Init(unsigned char const * pData, int dataSize, int reqId, bool bUseHWAccel, FFmpegDecoderCallback * pCB)
{
Deinit();

// From memory:
if (pData == nullptr || dataSize == 0)
{
printf("FFmpegDecoder::Init FAILED: neither filename nor memory data were given !\n");
return false;
}
m_pIoCtx = std::make_shared<AVIOContextMem>(pData, dataSize);

if (m_pIoCtx->IsValid() == false)
{
printf("FFmpegDecoder::Init FAILED: m_pIoCtx is nullptr !\n");
return false;
}

m_reqId = reqId;
m_bUseHWAccel = bUseHWAccel;
m_pCB = pCB;
m_pData = pData;
m_dataSize = dataSize;

m_bRequestedAbort = false;

m_pAVPkt = av_packet_alloc();
av_init_packet(m_pAVPkt);

m_pAVFrame = av_frame_alloc();
m_pAVFrameRGB = av_frame_alloc();

if (m_bUseHWAccel)
{
m_pSwAVFrameForHw = av_frame_alloc();
}

m_pAVFormatCtx = avformat_alloc_context();
m_pIoCtx->initAVFormatContext(m_pAVFormatCtx);

if (avformat_open_input(&m_pAVFormatCtx, "", nullptr, nullptr) != 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in avformat_open_input\n");
return false;
}

if (avformat_find_stream_info(m_pAVFormatCtx, nullptr) < 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in avformat_find_stream_info\n");
return false;
}


//av_dump_format(ctx_format, 0, "", false);
for (int i = 0; i < (int)m_pAVFormatCtx->nb_streams; i++)
{
if (m_pAVFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
m_streamIdx = i;
m_pAVStream = m_pAVFormatCtx->streams[i];
break;
}
}
if (m_pAVStream == nullptr)
{
printf("FFmpegDecoder::InitFFmpeg: failed to find video stream\n");
return false;
}

m_pAVCodec = avcodec_find_decoder(m_pAVStream->codecpar->codec_id);
if (!m_pAVCodec)
{
printf("FFmpegDecoder::InitFFmpeg: error in avcodec_find_decoder\n");
return false;
}

m_pAVCodecCtx = avcodec_alloc_context3(m_pAVCodec);
if (!m_pAVCodecCtx)
{
printf("FFmpegDecoder::InitFFmpeg: error in avcodec_alloc_context3\n");
return false;
}

if (avcodec_parameters_to_context(m_pAVCodecCtx, m_pAVStream->codecpar) < 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in avcodec_parameters_to_context\n");
return false;
}

if (m_bUseHWAccel)
{
AVHWDeviceType hwDevType = AV_HWDEVICE_TYPE_DXVA2;
g_hwPixFormat = find_fmt_by_hw_type(hwDevType);
m_pAVCodecCtx->get_format = get_hw_format;
av_opt_set_int(m_pAVCodecCtx, "refcounted_frames", 1, 0);
if (av_hwdevice_ctx_create(&m_pBufferRefForHw, hwDevType, NULL, NULL, 0) < 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in av_hwdevice_ctx_create\n");
return false;
}
m_pAVCodecCtx->hw_device_ctx = av_buffer_ref(m_pBufferRefForHw);
}

if (avcodec_open2(m_pAVCodecCtx, m_pAVCodec, nullptr) < 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in avcodec_open2\n");
return false;
}

m_pAVFrameRGB->format = AV_PIX_FMT_BGR24;
m_pAVFrameRGB->width = m_pAVCodecCtx->width;
m_pAVFrameRGB->height = m_pAVCodecCtx->height;
if (av_frame_get_buffer(m_pAVFrameRGB, 32) != 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in av_frame_get_buffer\n");
return false;
}

m_streamRotationDegrees = GetAVStreamRotation(m_pAVStream);
m_estimatedFramesCount = 0;
assert(m_pAVFormatCtx->nb_streams > 0);
if (m_pAVFormatCtx->nb_streams > 0)
{
m_estimatedFramesCount = m_pAVFormatCtx->streams[0]->nb_frames;
}

//InitConvertColorSpace
// Init converter from YUV420p to BGR:
if (m_bUseHWAccel)
{
m_pSwsCtxConvertImg = sws_getContext(m_pAVCodecCtx->width, m_pAVCodecCtx->height, AV_PIX_FMT_NV12, m_pAVCodecCtx->width, m_pAVCodecCtx->height, AV_PIX_FMT_RGB24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
}
else
{
m_pSwsCtxConvertImg = sws_getContext(m_pAVCodecCtx->width, m_pAVCodecCtx->height, m_pAVCodecCtx->pix_fmt, m_pAVCodecCtx->width, m_pAVCodecCtx->height, AV_PIX_FMT_RGB24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
}
if (!m_pSwsCtxConvertImg)
{
printf("FFmpegDecoder::InitFFmpeg: error in sws_getContext\n");
return false;
}

m_bInitOK = true;
return true;
}
void FFmpegDecoder::DecodeWithStep(int step)
{
step = 20;
int currentFramePos = 0;
int number_of_errors = 0;
const int MAX_ERROR_NUM = 10;
int seekPos = 0;

while (true)
{
if (step > 1)
{
seekPos = currentFramePos + step;
int64_t seekTarget = FrameToPts(m_pAVStream, seekPos);

if (av_seek_frame(m_pAVFormatCtx, m_streamIdx, seekTarget, AVSEEK_FLAG_FRAME) < 0)
{
number_of_errors++;
}
else
{
m_is_seeked = true;
}
}

while (true)
{
if (av_read_frame(m_pAVFormatCtx, m_pAVPkt) == 0)
{
if (m_pAVPkt->stream_index == m_streamIdx) //to make sure that I dont get packets from other streams
{
if (m_is_seeked)
{
avcodec_flush_buffers(m_pAVCodecCtx);
m_is_seeked = false;
}

if (avcodec_send_packet(m_pAVCodecCtx, m_pAVPkt) == 0)
{
int ret = 0;
while (ret >= 0)
{
ret = avcodec_receive_frame(m_pAVCodecCtx, m_pAVFrame);

if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
av_frame_unref(m_pAVFrame);
break;
}

currentFramePos = m_pAVFrame->display_picture_number; //In order to get position of currect frame (seek move poiter to the key frame)

if (currentFramePos < seekPos) //Some frames need to be skiped in order to reach needed frame
{
printf("----- SKIP : cur position (%d) \n", currentFramePos);
av_frame_unref(m_pAVFrame);
continue;
}

ProcessFrame(m_pAVFrame); //needed frame was processed
av_frame_unref(m_pAVFrame);
printf("----- cur position (%d) \n", currentFramePos);
break;
}
}

av_packet_unref(m_pAVPkt);
}
else
{
av_packet_unref(m_pAVPkt); //we got a frame from the wrong stream
}
}
else
{
number_of_errors++;
}

if (number_of_errors == MAX_ERROR_NUM)
{
printf("EXIT1\n");
break;
}
}

if (number_of_errors == MAX_ERROR_NUM)
{
printf("EXIT2\n");
break;
}
}
}
int64_t FrameToPts(AVStream* pavStream, int frame)
{
return (int64_t(frame) * pavStream->r_frame_rate.den * pavStream->time_base.den) /
(int64_t(pavStream->r_frame_rate.num) * pavStream->time_base.num);
}

最佳答案

您寻求“准确”到特定帧......因此您使用向后标志寻找您想要的帧,以确保您获得该帧或前一个帧。如果案例是前一个案例,您将解码直到获得实际请求的帧。
我可以看到您缺少两个重要步骤:-

  • 在每个 av_seek_frame (demux) 之后和下一个 avcodec_send_packet (decode) 之前,您需要使用 avcodec_flush_buffers 刷新解码器
  • 在每个 avcodec_send_packet (解码)之后,您需要使用 avcodec_receive_frame 接收所有帧(可以多于一个),例如:-
  • while (avcodec_receive_frame(...) == 0) { process frame here }
    解决方案 1(无 Seek - 小步骤或未知 fps 并确保准确性)
    让我们从 step = 1 开始进行简单的解复用以及我们得到什么
    public void Aleksey()
    {
    AVPacket* m_pAVPkt = av_packet_alloc();
    AVFrame* m_pAVFrame= av_frame_alloc();
    int ret;
    int step = 1;
    int curFrameNumber = 0;

    while (true)
    {
    ret = av_read_frame(demuxer.FormatContext, m_pAVPkt);
    if (m_pAVPkt->stream_index != 0) { av_packet_unref(m_pAVPkt); continue; }

    ret = avcodec_send_packet(codecCtx, m_pAVPkt);
    av_packet_unref(m_pAVPkt);

    while (true)
    {
    ret = avcodec_receive_frame(codecCtx, m_pAVFrame);
    if (ret != 0) { av_frame_unref(m_pAVFrame); break; }

    curFrameNumber++;

    if (curFrameNumber % step == 0)
    Console.WriteLine($"[pts: {m_pAVFrame->pts}] [time: {Utils.TicksToTime((long)(m_pAVFrame->pts * demuxer.VideoStreams[0].Timebase))}] [displaynumber: {m_pAVFrame->display_picture_number}] [codednumber: {m_pAVFrame->coded_picture_number}]");

    av_frame_unref(m_pAVFrame);
    }
    }
    }
    输出(我们看到正确的 pts/cur 帧时间,但我们没有得到带有 display_picture_number/coded_picture_number 的帧号)。看来你可以用 m_pAVCodecCtx->frame_number 尽管如此,但我敢打赌它将在每个 av_seek_frame 上重置,然后在 avcodec_flush_buffers 上重置。
    [pts: 0] [time: 00:00:00:000] [displaynumber: 0] [codednumber: 0] [framenumber: 1]
    [pts: 42] [time: 00:00:00:042] [displaynumber: 0] [codednumber: 3] [framenumber: 2]
    [pts: 83] [time: 00:00:00:083] [displaynumber: 0] [codednumber: 2] [framenumber: 3]
    [pts: 125] [time: 00:00:00:125] [displaynumber: 0] [codednumber: 4] [framenumber: 4]
    [pts: 167] [time: 00:00:00:167] [displaynumber: 0] [codednumber: 1] [framenumber: 5]
    [pts: 209] [time: 00:00:00:209] [displaynumber: 0] [codednumber: 7] [framenumber: 6]
    [pts: 250] [time: 00:00:00:250] [displaynumber: 0] [codednumber: 6] [framenumber: 7]
    [pts: 292] [time: 00:00:00:292] [displaynumber: 0] [codednumber: 8] [framenumber: 8]
    [pts: 334] [time: 00:00:00:334] [displaynumber: 0] [codednumber: 5] [framenumber: 9]
    [pts: 375] [time: 00:00:00:375] [displaynumber: 0] [codednumber: 11] [framenumber: 10]
    此代码确保您将准确执行 X 步帧,但它根本不使用搜索,这对性能不利。如果您的查找步骤很小,那很好,但如果您想使用大步骤,那么我们可以使用包含的查找。
    解决方案 2(使用 Seek - 更好的性能)
    AVFormatContext*m_pAVFormatCtx;
    AVCodecContext* m_pAVCodecCtx;
    AVStream* pavStream;
    AVPacket* m_pAVPkt;
    AVFrame* m_pAVFrame;
    int m_streamIdx;
    long startTime;
    double avgFrameDuration;
    double m_streamTimebase;

    public void Prepare()
    {
    // Using your variable names (mapped with mine)
    m_pAVFormatCtx = demuxer.FormatContext;
    m_pAVCodecCtx = codecCtx;
    pavStream = demuxer.VideoStreams[0].AVStream;
    m_streamIdx = pavStream->index;

    m_streamTimebase= av_q2d(pavStream->time_base) * 1000.0 * 10000.0; // Convert timebase to ticks so we can easily convert stream's timestamps to ticks
    startTime = pavStream->start_time != AV_NOPTS_VALUE ? (long)(pavStream->start_time * m_streamTimebase) : 0; // We will need this when we seek (adding it to seek timestamp)
    avgFrameDuration= 10000000 / av_q2d(pavStream->avg_frame_rate); // eg. 1 sec / 25 fps = 400.000 ticks (40ms)

    // Prepare packet/frame for demux/decode
    m_pAVPkt = av_packet_alloc();
    m_pAVFrame = av_frame_alloc();
    }

    public void GetFrame(int index) // Zero-based frame index
    {
    int ret;
    long frameTimestamp = (long) (index * avgFrameDuration); // Calculation of FrameX timestamp (based on fps/avgFrameDuration)
    Console.WriteLine($"Searching for {Utils.TicksToTime(frameTimestamp)}");

    // Seeking at frameTimestamp or previous I/Key frame and flushing codec
    ret = av_seek_frame(m_pAVFormatCtx, -1, (startTime + frameTimestamp) / 10, AVSEEK_FLAG_FRAME | AVSEEK_FLAG_BACKWARD);
    avcodec_flush_buffers(m_pAVCodecCtx);
    if (ret < 0) return; // handle seek error

    while (true)
    {
    // Demux Packet
    ret = av_read_frame(m_pAVFormatCtx, m_pAVPkt);
    if (ret != 0) break; // handle EOF/error
    if (m_pAVPkt->stream_index != m_streamIdx) { av_packet_unref(m_pAVPkt); continue; } // Exclude other streams

    // Send Packet for decoding
    ret = avcodec_send_packet(codecCtx, m_pAVPkt);
    av_packet_unref(m_pAVPkt);
    if (ret != 0) break; // handle EOF/error

    while (true)
    {
    // Receive all available frames for the decoder
    ret = avcodec_receive_frame(codecCtx, m_pAVFrame);
    if (ret != 0) { av_frame_unref(m_pAVFrame); break; }

    // Get frame pts (prefer best_effort_timestamp)
    long curPts = m_pAVFrame->best_effort_timestamp == AV_NOPTS_VALUE ? m_pAVFrame->pts : m_pAVFrame->best_effort_timestamp;
    if (curPts == AV_NOPTS_VALUE) { { av_frame_unref(m_pAVFrame); continue; } }

    // Skip frames before our actual requested frame
    Console.WriteLine($"[Skip] [pts: {curPts}] [time: {Utils.TicksToTime((long)(curPts * m_streamTimebase))}]");
    if ((long)(curPts * m_streamTimebase) / 10000 < frameTimestamp / 10000) { av_frame_unref(m_pAVFrame); continue; }

    Console.WriteLine($"[Found] [pts: {curPts}] [time: {Utils.TicksToTime((long)(curPts * m_streamTimebase))}]");
    av_frame_unref(m_pAVFrame);
    return;
    }
    }
    }
    使用以下代码进行测试
    Prepare();
    GetFrame(100);
    GetFrame(200);
    GetFrame(300);
    GetFrame(100);
    给出输出
    Searching for 00:00:04:129
    [Skip] [pts: 4004] [time: 00:00:04:004]
    [Skip] [pts: 4046] [time: 00:00:04:046]
    [Skip] [pts: 4087] [time: 00:00:04:087]
    [Skip] [pts: 4129] [time: 00:00:04:129]
    [Found] [pts: 4129] [time: 00:00:04:129]
    Searching for 00:00:08:299
    [Skip] [pts: 8008] [time: 00:00:08:008]
    [Skip] [pts: 8050] [time: 00:00:08:050]
    [Skip] [pts: 8091] [time: 00:00:08:091]
    [Skip] [pts: 8133] [time: 00:00:08:133]
    [Skip] [pts: 8175] [time: 00:00:08:175]
    [Skip] [pts: 8217] [time: 00:00:08:217]
    [Skip] [pts: 8258] [time: 00:00:08:258]
    [Skip] [pts: 8300] [time: 00:00:08:300]
    [Found] [pts: 8300] [time: 00:00:08:300]
    Searching for 00:00:12:470
    [Skip] [pts: 12012] [time: 00:00:12:012]
    [Skip] [pts: 12054] [time: 00:00:12:054]
    [Skip] [pts: 12095] [time: 00:00:12:095]
    [Skip] [pts: 12137] [time: 00:00:12:137]
    [Skip] [pts: 12179] [time: 00:00:12:179]
    [Skip] [pts: 12221] [time: 00:00:12:221]
    [Skip] [pts: 12262] [time: 00:00:12:262]
    [Skip] [pts: 12304] [time: 00:00:12:304]
    [Skip] [pts: 12346] [time: 00:00:12:346]
    [Skip] [pts: 12387] [time: 00:00:12:387]
    [Skip] [pts: 12429] [time: 00:00:12:429]
    [Skip] [pts: 12471] [time: 00:00:12:471]
    [Found] [pts: 12471] [time: 00:00:12:471]
    Searching for 00:00:04:129
    [Skip] [pts: 4004] [time: 00:00:04:004]
    [Skip] [pts: 4046] [time: 00:00:04:046]
    [Skip] [pts: 4087] [time: 00:00:04:087]
    [Skip] [pts: 4129] [time: 00:00:04:129]
    [Found] [pts: 4129] [time: 00:00:04:129]
    注意:对于第二种解决方案,仍有空间可以更好地处理小步骤(都在相同关键帧内的帧)。您可以通过存储最后一次搜索的关键帧并将其与当前搜索的关键帧进行比较来验证这一点。如果是这种情况,您可以避免刷新编解码器并使用 ANY 标志在前一帧的位置重新搜索以进行精确搜索(从原来的位置继续解码器)。不过,新帧的时间戳必须大于前一个。

    关于c++ - 如何在 av_seek_frame 之后获取当前的 AVFrame 序号?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67397968/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com