RTMP协议解析需依赖librtmp或FFmpeg等第三方库,C++标准库不支持;librtmp可直接调用,关键是从FLV tag中准确提取音视频帧并处理前缀转换;FFmpeg底层仍基于RTMP实现,但不暴露控制消息;手写解析易踩chunk size变更、时间戳回绕、AMF0解码等坑。

RTMP协议解析不是C++标准库能直接干的事
RTMP是应用层流媒体协议,C++标准库不提供rtmp_connect()、rtmp_read_packet()这类接口。你得依赖第三方库或自己实现底层状态机——而后者意味着要啃完 Adobe 的《RTMP Specification 1.0》并处理 TCP 粘包、AMF0/AMF3 编码、chunk stream 复用、时间戳校正等细节。绝大多数项目应该直接用 librtmp 或 ffmpeg(libavformat)。
用 librtmp 解析视频/音频帧最简路径
librtmp 是最轻量、最贴近协议原语的 C 库,C++ 可直接调用。关键不是“怎么连”,而是“怎么从收到的 chunk 流里准确切出一帧”:
-
RTMP_ReadPacket()每次读一个完整 RTMP packet(含 header + payload),但 payload 不等于一帧——它可能是 FLV tag(含 frame type + timestamp + data),也可能是控制消息(如SetChunkSize)或元数据(onMetaData) - 真正含音视频帧的是 type=8(音频)或 type=9(视频)的 FLV tag;需先跳过 FLV header(9 字节)和 tag header(11 字节),再按
body_size字段截取原始帧数据 - 视频帧(type=9)的
data[0]是 AVCPacketType(0=SPS/PPS,1=IDR,2=non-IDR),音频帧(type=8)的data[0]是 SoundFormat + SoundRate + SoundSize + SoundType 组合位域 - 别直接 memcpy 到解码器——H.264 帧前缀是 0x00000001,但 RTMP 中是 length-prefixed(4 字节大端 size),需手动转换;AAC 同理,ADTS 头需自行补全或喂给解码器 raw AAC mode
用 ffmpeg avformat_open_input() 会绕过协议解析吗?
不会。FFmpeg 的 rtmp:// 协议支持实际就是封装了 librtmp(旧版)或自研 RTMP 实现(新版)。调用 avformat_open_input() 时,它已在后台完成握手、chunk 处理、FLV tag 解包、时间戳归一化。你拿到的是已解复用的 AVPacket,其中 pkt->data 指向的就是 H.264 NALU 或 AAC raw data(无 ADTS)。
- 必须设
AVDictionary *opts = nullptr; av_dict_set(&opts, "timeout", "5000000", 0);,否则默认超时极短,易断连 -
avformat_find_stream_info()会触发多次RTMP_ReadPacket()并缓存若干秒数据,对低延迟场景不利;可设ic->max_analyze_duration2 = 500000;限制分析时长 - 若需访问原始 RTMP 控制消息(如
_checkbw、onStatus),FFmpeg 不暴露这些——此时必须切回librtmp
自己实现 RTMP parser 的三个致命坑
除非做嵌入式极简终端或教学演示,否则不建议手写。踩过的人反馈最痛的点:
立即学习“C++免费学习笔记(深入)”;
- chunk size 动态变更:服务端随时发
SetChunkSize(type=1)消息,后续所有 chunk 必须按新 size 拆分;漏处理会导致后续全部 packet 解析错位 - 时间戳溢出处理:RTMP timestamp 是 32 位,每 49.7 天回绕一次;需结合
previousTimestamp和abs_timestamp字段做单调递增校正,否则音画不同步 - AMF0 解码陷阱:
AMF_STRING长度字段是 16 位大端,但某些推流端(如 OBS 旧版)会误写成小端;AMF_ECMA_ARRAY的 length 字段后紧跟 key-value 对,key 是 string,value 类型需递归识别——没严格按 spec 处理会崩溃
协议深度不在于代码行数,而在于那些“文档里写了但没人告诉你必须检查”的边界条件。真要碰,先拿 Wireshark 抓包对照 librtmp 日志,比对着 spec 写更可靠。



















