ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

网络摄像机首次播放音视频延时出图像踩坑记录

2026/9/12 18:58:40 拓冰建站 浏览量
网络摄像机首次播放音视频延时出图像踩坑记录 ZLMediaKit / RTSP / H.264 播放音视频踩坑记录 - SDP、SPS、PPS 完整详解适用场景RTSP 信令交互、addStreamProxy 拉流、WebRTC、H264 裸流、花屏、首帧黑屏、无法解码绝大多数问题根源就是 SDP / SPS/PPS。1、SDP 是什么SDPSession Description Protocol会话描述协议文本格式。RTSP 中客户端发送DESCRIBE请求服务端返回 SDPSDP 告诉播放器这个流有什么轨道视频 / 音频、编码格式、分辨率、采样率、RTP 端口、传输协议、SPS/PPS 等信息。SDP 只是描述元数据不携带音视频帧数据纯文本。RTSP 交互片段VLC/ZLM(PlayerProxy) →DESCRIBE rtsp://xxx/vs0 RTSP/1.0设备 RTSP 服务端回复Body 就是SDP 文本。SDP 典型片段 (H.264 视频)v0 o- 1600000001 1600000001 IN IP4 127.0.0.1 sMediaServer t0 0 mvideo 0 RTP/AVP 96 artpmap:96 H264/90000 afmtp:96 packetization-mode1;profile-level-id42001E;sprop‑parameter‑setsZ0IAKeKQFAe2AtwEBAaQeJEA8AAIBBA1KGA,g48IAA重点字段mvideo视频媒体轨道rtpmap:96 H264/90000PT96H264 编码时间戳频率 90000fmtp:96H264 私有参数packetization-mode1模式 1FU‑A 分片模式RTSP/RTP 最常用profile‑level‑idH264 profile levelsprop‑parameter‑setsbase64(SPS),base64(PPS)→SPS、PPS 做 base64 编码写在这里关键点sprop‑parameter‑sets就是把 SPS、PPS 放到 SDP 里面播放器拿到 SDP 就提前拿到解码参数不需要等 I 帧。2、SPS / PPS 是什么H.264H.264 码流由 NALUNAL 单元组成。SPS、PPS 属于参数集 NALU不是图像帧是解码器初始化必须的元数据。名称全称作用NALU 类型SPSSequence Parameter Set 序列参数集序列全局信息分辨率、profile、level、帧率、参考帧数量NALU type7PPSPicture Parameter Set 图像参数集图像相关熵编码模式、片组参数依附 SPSNALU type8⚠️解码器没有 SPS/PPS绝对不能解码 I 帧 (IDR 帧)。现象收到 I 帧但是画面黑屏、花屏、绿屏没有报错但是出不来图像。NALU 类型简单记忆7SPS8PPS5IDR‑I 帧关键帧1普通 P 帧、非 I 帧两种携带 SPS/PPS 的方式RTSP/RTP 场景方式 1SDP 内携带 sprop‑parameter‑sets最理想RTSP 标准SDP 的fmtp字段把 SPS、PPS 做 base64 编码写入。播放器收到 DESCRIBE 应答解析 SDP 就拿到 SPS/PPS解码器直接初始化。ZLMediaKit addStreamProxy 拉流RTSP 服务端输出 SDP 带 sprop体验最好首帧秒出。方式 2SDP 不带 sprop‑parameter‑setsSPS/PPS 随码流在 IDR 前面发送SDP 里没有 sprop‑parameter‑setsSPSPPS NALU 放在每一个 IDR (I 帧) 前面作为码流一部分随 RTP 包传输。播放器必须等到第一个 IDR 帧到来解析前面 SPS/PPS才能开始解码。现象VLC 打开流要等 1~2 秒才出画面seek、断流重连容易黑屏。很多嵌入式设备 RTSP 服务端容易踩坑SDP 不填 sprop‑parameter‑sets只在码流里面输出 SPS/PPS。裸 H264 AnnexB 格式文件 / 本地码流起始码0x00 00 01分割 NALU00 00 01 67 ... (SPS NALU) 00 00 01 68 ... (PPS NALU) 00 00 01 65 ... (IDR I帧)RTP 传输 RTSP 时AnnexB 起始码会被去掉用 RTP 封装SPS/PPS 可以放到 SDP (sprop) 或者穿插在 RTP 负载中。3、sprop‑parameter‑sets 解析示例sprop‑parameter‑setsZ0IAKeKQFAe2AtwEBAaQeJEA8AAIBBA1KGA,g48IAA逗号分隔逗号前 base64 串 →SPS 原始二进制逗号后 base64 串 →PPS 原始二进制解码流程base64 解码 → 得到 SPS NALU 原始字节带 NALU 头 0x67、PPS NALU 字节0x68。注意sprop 里面的 SPS/PPS不带 AnnexB 起始码 00 00 01只是纯 NALU 负载。常见故障现象与 SPS/PPS 关系现象根因VLC 打开 RTSP 流音频正常视频黑屏过 1‑2 秒出图SDP 没有 sprop‑parameter‑sets要等码流中 SPS/PPSIDR 到来首帧花屏、马赛克后面正常拿到 SPS/PPS 之前收到 I 帧解码器参数错误ZLMediaKit WebRTC 播放失败报错 no sps/ppsWebRTC 强制要求 SDP 携带 sprop‑parameter‑sets仅码流携带 SPS/PPS 不行网络抖动断流恢复后黑屏断流丢失 SPS/PPS后续 I 帧没有配套参数集WebRTC 硬性要求SDP 必须携带 sprop‑parameter‑sets只在 RTP 流里送 SPS/PPS 是无法播放 WebRTC。这是 ZLM 非常高频坑。4、RTSP 完整交互看 SDP/SPS/PPS 时序摄像机设备 A - ZLMediaKit addStreamProxyZLM PlayerProxyRTSP 客户端发送 DESCRIBEA 设备 RTSP 服务端回复 SDP。✅理想SDP fmtp 携带sprop‑parameter‑setsZLM 直接解析 SPS/PPS。❌缺陷SDP 无 spropZLM 只能等待 RTP 码流中出现 SPS/PPS NALU。ZLM 发送 SETUP建立 RTP 通道rtsp_type0 TCP interleavedZLM 发送 PLAYA 设备输出 RTP 包SPS/PPS IDR‑I P 帧如果 SDP 不带 spropZLM 内部会缓存等待码流中 SPS/PPS 到来才生成完整 MediaSource 对外分发。此时 VLC 连接 ZLM也要等待 IDR 帧。5、A 端设备 RTSP 服务端输出建议方法A 端设备端RTSP 服务端SDP 的 fmtp 必须填充sprop‑parameter‑setsSPS,PPS base64不要只放在码流里。packetization‑mode1FU‑A 分片大 I 帧分片传输RTSP over TCP 标准。IDR 帧前面码流中依然重复输出 SPSPPS双重保险网络丢包容错。ZLMediaKit addStreamProxy 相关参数影响 SDP 处理directProxy1默认透传源 SDP源 SDP 不带 sprop则 ZLM 对外 SDP 也不带 spropWebRTC 无法播放。directProxy0ZLM 解封装从 RTP 码流提取 SPS/PPS重新生成 SDP补全 sprop‑parameter‑sets兼容性提升消耗少量 CPU。curl 可以追加参数directProxy0curlhttp://127.0.0.1:80/index/api/addStreamProxy?secret$SECRETvhost__defaultVhost__applivestreamak3918urlrtsp%3A%2F%2F127.0.0.1%3A8554%2Fvs0rtsp_type0directProxy06、快速排错手段tcpdump/wireshark 抓 RTSP 报文查看 DESCRIBE 应答 SDP看sprop‑parameter‑sets是否存在。ZLMediaKit 日志搜索sps、pps、h264观察是否成功解析到参数集。如果 SDP 没有 sprop打开 directProxy0ZLM 会从 RTP 码流自动提取 SPS/PPS 重建 SDP。7、C 代码原始 SPS/PPS NALU 生成 sprop‑parameter‑sets 字符串说明输入去掉 AnnexB 起始码 (0x000001) 的纯 NALU 二进制SPS NAL、PPS NAL包含 NALU 头字节 0x67 / 0x68输出sprop‑parameter‑setsxxxx,yyyy字符串可直接填入 SDP 的 fmtp 字段依赖需要实现 base64 编码函数嵌入式可使用开源 base64 实现。注意不要带入 00 00 01 起始码sprop 标准只接受 NALU 净荷。#includestdio.h#includestring.h#includestdlib.h/** * brief base64编码函数输入二进制输出base64字符串(外部释放内存) */char*base64_encode(constunsignedchar*data,size_tlen);/** * brief 生成SDP fmtp中的 sprop‑parameter‑sets 字段字符串 * param sps_buf SPS NALU(无000001起始码含nal头0x67) * param sps_len SPS字节长度 * param pps_buf PPS NALU(无000001起始码含nal头0x68) * param pps_len PPS字节长度 * return 动态分配字符串示例sprop‑parameter‑setsZ0IA...,g48IAA; 返回NULL失败调用者free释放 */char*h264_gen_sprop_parameter_sets(constuint8_t*sps_buf,size_tsps_len,constuint8_t*pps_buf,size_tpps_len){if(!sps_buf||sps_len0||!pps_buf||pps_len0){returnNULL;}// SPS base64char*sps_b64base64_encode(sps_buf,sps_len);if(!sps_b64)returnNULL;// PPS base64char*pps_b64base64_encode(pps_buf,pps_len);if(!pps_b64){free(sps_b64);returnNULL;}// sprop‑parameter‑setssps_b64,pps_b64size_tout_lenstrlen(sprop‑parameter‑sets)strlen(sps_b64)1strlen(pps_b64)1;char*out_str(char*)malloc(out_len);if(!out_str){free(sps_b64);free(pps_b64);returnNULL;}snprintf(out_str,out_len,sprop‑parameter‑sets%s,%s,sps_b64,pps_b64);free(sps_b64);free(pps_b64);returnout_str;}/* --------------使用示例-------------- */voiddemo_usage(void){// 示例sps_nal、pps_nal 为去除00 00 01起始码的NALU原始数据uint8_tsps_nal[]{0x67,0x42,0x00,0x1E,0x8D,0x8D,0x40,0xA0};uint8_tpps_nal[]{0x68,0xCE,0x3C,0x80};char*sprop_strh264_gen_sprop_parameter_sets(sps_nal,sizeof(sps_nal),pps_nal,sizeof(pps_nal));if(sprop_str){printf(%sn,sprop_str);/* * 拼接进SDP fmtp行 * afmtp:96 packetization-mode1;profile-level-id42001E;%srn */free(sprop_str);}}配套从 AnnexB 码流提取 SPS/PPS NALU去除起始码从 H264 AnnexB 流 (带00 00 01) 中剥离起始码拿到纯 NALU作为上面函数输入。重点传给h264_gen_sprop_parameter_sets的不能包含 0x000001 起始码。/** * brief 简单查找AnnexB起始码 0x00 00 01 */intfind_annexb_startcode(constuint8_t*buf,size_tlen,size_t*pos){for(size_ti0;i3len;i){if(buf[i]0buf[i1]0buf[i2]1){*posi3;// 跳过3字节起始码返回NALU有效载荷起始偏移return1;}}return0;}profile‑level‑id 生成补充SDP fmtp 还需要profile‑level‑idXXYYZZ取自 SPS NALU 的第 1、2、3 字节byte0profile_idcbyte1constraint_set_flagsbyte2level_idc拼接为十六进制 6 位字符串例如42001E。集成进 RTSP SDP 输出示例片段afmtp:96 packetization-mode1;profile-level-id42001E;sprop‑parameter‑setsZ0IA...,g48IAA重要注意事项sprop‑parameter‑sets 格式sps_base64,pps_base64逗号分隔无空格。输入必须是 NALU 本体剔除 AnnexB 起始码 00 00 01否则 base64 之后参数集错误解码器无法初始化。编码输出的字符串直接填入 SDP不要额外 urlencodeSDP 本身不需要 URL 编码URL 编码只用于 HTTP API。H.265 HEVC需要同时处理 VPSSPSPPS对应字段sprop‑vps‑sets;sprop‑sps‑sets;sprop‑pps‑sets。补充 H.265 (HEVC)H265 对应SPS (type33), PPS (type34), VPS (type32)SDP 字段为sprop‑vps‑sets,sprop‑sps‑sets,sprop‑pps‑sets。wireshark 常用过滤表达式# 过滤RTSP DESCRIBE应答查看SDP rtsp rtsp.method DESCRIBE # 过滤H264 SPS NALU h264.nal_unit_type 7 # 过滤H264 PPS NALU h264.nal_unit_type 8