avcodec_send_packet返回-22(EINVAL)最常见原因是送入非关键帧或解码器未就绪;需确保已调用avformat_find_stream_info、正确获取视频流索引、用avcodec_parameters_to_context初始化上下文、成功调用avcodec_open2,并仅对带AV_PKT_FLAG_KEY标志的包调用send_packet。

ffmpeg\_avcodec\_send\_packet 返回 -22(EINVAL)怎么办
抽帧失败最常见的报错就是 avcodec_send_packet 返回 -22,本质是送入了非关键帧(non-keyframe)或解码器尚未就绪。FFmpeg 的 AVCodecContext 默认启用严格解码模式,只接受完整 GOP 起始帧(即 I 帧),而视频流里大量 P/B 帧不能直接送入解码器。
- 确保先调用
avformat_find_stream_info完整探测流参数,否则codecpar->codec_type可能为AVMEDIA_TYPE_UNKNOWN - 必须定位到视频流索引(
video_stream_index),并用该索引从format_context->streams中取出正确的AVStream*,再通过avcodec_parameters_to_context初始化解码器上下文 - 解码前务必调用
avcodec_open2,且检查返回值;未成功打开时调用send_packet必然失败 - 逐包读取时,跳过非视频流(
packet.stream_index != video_stream_index)和空包(packet.size == 0)
如何只抽关键帧(I-frame)并避免花屏
直接按固定时间间隔(如每秒1帧)抽帧,大概率拿到的是 P/B 帧,解码后图像残缺或全绿/全灰——这不是代码 bug,而是 H.264/H.265 编码原理决定的。真正可靠的抽帧方式是「只解码 I 帧」,并跳过所有依赖帧。
- 在
av_read_frame后检查packet.flags & AV_PKT_FLAG_KEY,仅对带 KEY 标志的包调用avcodec_send_packet - 即使只送 I 帧,也要完整走「send → receive」两步:一次
send_packet后需循环调用avcodec_receive_frame直到返回AVERROR(EAGAIN),否则可能漏掉已缓存在解码器内部的最后一帧 - 保存 JPG 前,用
sws_scale将原始像素格式(如AV_PIX_FMT_YUV420P)转成AV_PIX_FMT_RGB24,JPEG 编码器不支持 YUV 直出 - 若需指定抽帧时间点(比如第 5 秒),用
av_seek_frame(format_context, video_stream_index, timestamp, AVSEEK_FLAG_BACKWARD)定位,再手动丢弃若干帧直到遇到下一个 I 帧
用 libjpeg-turbo 快速保存 RGB24 为 JPG
FFmpeg 自带的 avcodec_find_encoder_by_name("mjpeg") 编码路径太重,要建编码器、分配帧内存、处理时间戳……纯抽帧存图完全没必要。更轻量的做法是:解码出 AVFrame 后,用 libjpeg-turbo 直接写 JPG 文件。
- 链接时加上
-ljpeg(或-lturbojpeg),头文件包含 - 申请
JSAMPARRAY作为行指针数组,每行指向frame->data[0] + y * frame->linesize[0],注意linesize[0]可能大于图像宽度(内存对齐) - 设置
cinfo.image_width / image_height为frame->width / frame->height,cinfo.input_components = 3,cinfo.in_color_space = JCS_RGB - 关键细节:libjpeg 默认按 top-down 存储,但 FFmpeg 的
AVFrame在RGB24下是 bottom-up(首地址是最后一行),需反转行指针顺序或手动拷贝
抽帧性能瓶颈常卡在 sws\_scale 和文件 IO
单帧耗时超过 20ms 很可能不是解码慢,而是缩放和磁盘写入拖累。特别是高分辨率视频(4K+),sws_getContext 创建的转换器若未复用,每次新建都会触发内部 cache 初始化。
- 把
struct SwsContext*提到循环外,初始化一次,反复用于所有帧;不同尺寸间切换时才需重建 - 避免每帧都 fopen/fclose:用
fopen(..., "wb")打开一次,抽完一帧 fwrite 一次,最后 fclose —— 但要注意文件名唯一性(加序号或时间戳) - 如果目标只是快速预览,可跳过
sws_scale,直接用libyuv的I420ToJPEG(需先转 YUV420P)或硬解输出 NV12 后用 OpenCV cv::imwrite,速度提升明显 - Windows 下注意
fopen的 filename 必须是 UTF-8 编码(或转 GBK),否则中文路径会 fopen 失败返回 NULL
实际跑通的关键在于:先确认能否正确识别视频流并解码出第一帧 I 帧,再逐个打通缩放、RGB 排布、JPG 写入三个环节。中间任意一步的像素数据错位(比如宽高颠倒、通道顺序混乱、YUV 未转 RGB),都会导致 JPG 文件能生成但打不开或颜色异常。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











