必须手动补nalu起始码并提取sps/pps,因h.264裸流无容器、缺annex b格式起始码(0x00000001/0x000001)及关键参数集,直接调avcodec_send_packet()会失败;需搜索起始码定位nalu、解析sps(nal_unit_type=7)和pps(nal_unit_type=8),用avcodec_parameters_set()注入,再同步至avcodeccontext,同时以二进制模式读取文件并手动维护pts。

用 libavcodec 解码 H.264 裸流前必须手动补 NALU 头
直接调 avcodec_send_packet() 会失败,因为 H.264 裸流(.264 文件)通常只含原始 NAL 单元数据,不含起始码(0x00000001 或 0x000001)。FFmpeg 的解码器默认期望输入带起始码的 Annex B 格式,而裸流往往省略了它——哪怕文件里有,也可能不规范。
实操建议:
- 逐帧读取时,先搜索
0x00000001或0x000001定位 NALU 边界;更稳妥的做法是按 H.264 规范解析start_code_prefix+nal_unit_type - 若裸流实际是长度前缀格式(如 MP4 中的 avcC),需先解析
avcCbox 提取sps和pps,再按每个 NALU 长度字段切分——这种情况下不能硬加起始码 - 补起始码后,记得更新
AVPacket的data和size字段,并确保内存可写(必要时用av_packet_ref()+av_buffer_alloc()分配新 buffer)
avcodec_parameters_from_context() 不适用于裸流初始化
裸流没有容器层,无法靠 avformat_open_input() 自动推导编码参数。很多人误以为只要给 AVCodecContext 填了 codec_id = AV_CODEC_ID_H264 就能解码,但缺少 sps 和 pps 会导致 avcodec_open2() 成功、解码首帧却返回 AVERROR_INVALIDDATA。
实操建议:
- 必须从裸流开头手动提取 SPS/PPS:搜索第一个 NALU,检查
nal_unit_type == 7(SPS)或8(PPS),将其完整数据(含起始码)传给avcodec_parameters_set() - 调用
avcodec_parameters_to_context()把参数同步到AVCodecContext,而不是反向操作 - 若裸流不带内嵌 SPS/PPS(极少见),需外部提供 Base64 编码的 SPS/PPS 字符串并用
av_base64_decode()还原
解码后 AVFrame 的时间戳要自己维护
裸流无 PTS/DTS,avcodec_receive_frame() 返回的 frame->pts 恒为 AV_NOPTS_VALUE。强行用 sws_getCachedContext() 转换或送显时依赖时间戳,会导致画面卡顿或音画不同步。
实操建议:
- 按恒定帧率(如 25 fps)递增计算 PTS:
frame->pts = frame_count++ * (AV_TIME_BASE / 25);注意单位是AV_TIME_BASE(微秒) - 若裸流来自摄像头或编码器实时输出,应记录每个 NALU 写入文件的时间戳,而非依赖文件顺序
- 避免用
frame->best_effort_timestamp——它在裸流场景下也无效
Windows 下用 fread() 读裸流要注意换行符截断
用文本模式打开 .264 文件(如 fopen("in.264", "r")),在 Windows 上会把 0x0A 当成换行自动过滤,导致二进制数据错位。现象是解码到某帧突然花屏或报 Invalid data found when processing input。
实操建议:
- 务必用二进制模式:
fopen("in.264", "rb") - 读取时不要用
fgets()或std::getline();用fread(buf, 1, size, fp)或 C++ 的std::ifstream::read()并设file.unsetf(std::ios::skipws) - 验证读取完整性:比对文件
stat()得到的大小与累计读取字节数
裸流解析最易被忽略的是 NALU 边界判定逻辑——看似简单,但起始码可能被零字节填充干扰,SPS/PPS 可能被分片传输(FU-A),这些在真实设备录流中并不罕见。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











