FFmpeg C++视频定位后获取当前帧索引的方法求助
FFmpeg视频定位后获取当前帧索引/时间戳的解决方案
问题背景
我目前用FFmpeg实现视频定位,代码如下:
int64_t pts = (int64_t)( ((float) timestamp_to_go / 1000)* (double)time_base.den / (double)time_base.num); if(av_seek_frame(av_format_ctx, video_stream_index, pts, AVSEEK_FLAG_BACKWARD) < 0 ) exit(0);
这段代码能定位到最近的I帧(比如目标10秒,实际跳到8.5秒),这个情况可接受,但定位后无法获取当前帧的索引或时间戳,导致不知道需要解码多少帧才能到达目标位置(比如30fps视频中,8.5秒对应255帧,目标10秒对应300帧,需要明确当前帧索引来计算解码量)。
实现步骤
要获取定位后的帧信息,必须在av_seek_frame后重置解码器并读取解码第一帧,从帧数据中提取关键信息:
1. 重置解码器缓存
定位操作会打乱解码器的内部状态,必须先清空缓存:
avcodec_flush_buffers(av_codec_ctx);
2. 读取并解码定位后的首帧
循环读取数据包并解码,直到拿到第一个有效视频帧:
AVPacket pkt; AVFrame* frame = av_frame_alloc(); int ret; double fps = av_q2d(av_stream->avg_frame_rate); // 获取视频帧率 double target_seconds = 10.0; // 你的目标时间戳(秒) while (av_read_frame(av_format_ctx, &pkt) >= 0) { if (pkt.stream_index != video_stream_index) { av_packet_unref(&pkt); continue; } // 发送数据包到解码器 ret = avcodec_send_packet(av_codec_ctx, &pkt); if (ret < 0) { av_packet_unref(&pkt); continue; } // 接收解码后的帧 while (ret >= 0) { ret = avcodec_receive_frame(av_codec_ctx, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { av_frame_unref(frame); av_packet_unref(&pkt); goto cleanup; } // 提取当前帧的时间戳和索引 if (frame->pts != AV_NOPTS_VALUE) { double current_seconds = frame->pts * av_q2d(av_stream->time_base); int current_frame_idx = round(current_seconds * fps); int target_frame_idx = round(target_seconds * fps); int frames_to_decode = target_frame_idx - current_frame_idx; // 这里可以处理当前帧,然后继续解码frames_to_decode帧到达目标位置 printf("当前时间戳:%.2f秒,当前帧索引:%d,还需解码%d帧\n", current_seconds, current_frame_idx, frames_to_decode); // 拿到所需信息后可退出循环,或继续解码 goto cleanup; } av_frame_unref(frame); } av_packet_unref(&pkt); } cleanup: av_frame_free(&frame);
3. 注意事项
- 如果是可变帧率(VFR)视频,不要用帧索引判断进度,直接比较时间戳更可靠
frame->pts是基于视频流的time_base的,必须通过av_q2d()转换为实际秒数- 定位后的第一个解码帧就是你找到的I帧,无需额外判断帧类型
内容的提问来源于stack exchange,提问作者Turgut
相关产品推荐
相关产品推荐

