RTP详解
一、RTP 头结构总览RFC 3550RTP 固定头为12 字节不含 CSRC 列表和扩展头字段按大端序排列0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -------------------------------- |V2|P|X| CC |M| PT | Sequence Number | -------------------------------- | Timestamp | -------------------------------- | SSRC | -------------------------------- | CSRC (0~15 个每个 4 字节) | -------------------------------- | 扩展头若 X1可选 | -------------------------------- | Payload ... | --------------------------------二、各字段详解VVersion2 bitsRTP 协议版本。固定为 2对应 RFC 3550。发送端必须设为 2接收端可忽略非 2 的包。PPadding1 bit填充标志。若为 1载荷末尾有 1 个或多个填充字节这些字节不属于有效载荷。最后一个填充字节的值表示需要忽略的填充字节总数含自身。应用场景加密算法要求块对齐或底层协议需要固定长度。音视频 RTSP 推流中通常设为 0。XExtension1 bit扩展头标志。若为 1固定头之后紧跟一个扩展头格式见 RFC 3550 §5.3.1。应用场景携带传输层无关的扩展信息如绝对发送时间。常规 H264/AAC 推流中设为 0。CCCSRC Count4 bitsCSRC 计数器。表示固定头之后跟随的 CSRC 标识符数量0~15。应用场景多方混音/混流场景标识参与混流的多个源。单源推流中设为 0。MMarker1 bit标记位。含义由 Payload Type 定义用于标记帧边界等重要事件。场景M 位含义H264 视频一帧的最后一个 RTP 包AAC 音频一帧的最后一个 RTP 包通常一个包一帧视频帧分片第一个分片 M0最后一个分片 M1接收端用法收到 M1 表示一帧完整接收可交付解码器。PTPayload Type7 bits载荷类型标识载荷的编码格式决定接收端如何解释载荷。PT 值编码时钟频率说明96H264动态90000SDP 中artpmap:96 H264/9000097AAC动态采样率SDP 中artpmap:97 mpeg4-generic/44100/20~95静态映射固定RFC 3551 定义如 0PCMU推流端在 SDP 中协商 PT通常视频用 96音频用 97。接收端根据 SDP 的rtpmap行确定 PT 对应的编码。Sequence Number16 bits序列号每发送一个 RTP 包递增 1用于接收端检测丢包和恢复包序。初始值应随机防明文攻击递增到 65535 后回绕到 0。接收端用法期望值 上一个包的序列号 1若收到的序列号 期望值 →丢包若收到的序列号 期望值 →乱序或重复Timestamp32 bits时间戳反映 RTP 数据包中第一个字节的采样时刻用于同步和抖动计算。时钟频率由编码格式决定编码时钟频率时间戳增量H26490000 Hz每帧增量 90000 / fpsAAC 44100Hz44100 Hz每帧增量 1024每 AAC 帧 1024 采样AAC 48000Hz48000 Hz每帧增量 1024初始值应随机但同一帧的所有分片 RTP 包必须使用相同的时间戳。示例H264 30fps第 1 帧timestamp T0第 2 帧timestamp T0 90000/30 T0 3000第 3 帧timestamp T0 6000Frame::dts是 90kHz 时基H264 可直接作为 RTP timestampAAC 需从 90kHz 转换为采样率时基rtpTs (dts90k * sampleRate) / 90000SSRCSynchronization Source32 bits同步源标识符唯一标识 RTP 流的发送者随机生成。同一 SSRC 的包属于同一媒体流传输地址变更时 SSRC 应更新同一会话中不同媒体流视频/音频使用不同 SSRC生成建议QRandomGenerator::global()-generate()CSRCContributing Source0~15 × 4 bytes贡献源标识符列表仅在 CC 0 时存在。每个 CSRC 是 4 字节标识参与混流的源。单源推流中 CC0此字段不存在。接收端解析时不能硬编码偏移 12必须根据 CC 和 X 动态计算int headerLen 12 cc * 4; if (x) { uint16_t extLen load_be16(p headerLen 2); headerLen 4 extLen * 4; }三、H264 RTP 封包详解RFC 6184H264 的 RTP 载荷有三种模式模式 1单 NALU 包1 ≤ NAL type ≤ 23一个 RTP 包承载一个完整的 NALURTP 头之后直接跟 NALU不含起始码。--------------------------------- | RTP Header | NALU (无起始码) | ---------------------------------适用条件NALU 大小 ≤ MTU通常 1400 字节。模式 2STAP-ANAL type 24单时间聚合包把多个小 NALU 聚合成一个 RTP 包发送。-------------------------------------------- | RTP Header |STAP-A|NALU1 |NALU2 | ... |NALU N| | | Hdr |LenData|LenData| | | --------------------------------------------STAP-A 头1 字节nal_type 24。之后每个 NALU 前有2 字节长度大端。适用条件SPS/PPS 一起发送或多个小 slice 聚合。模式 3FU-ANAL type 28分片单元把一个大 NALU 拆成多个 RTP 包。FU-A 结构每个分片包-------------------------------------------------- | RTP Header| FU Indicator| FU Header | FU Payload | | (12 字节) | (1 字节) | (1 字节) | (NALU 分片数据) | --------------------------------------------------FU Indicator1 字节0 1 2 3 4 5 6 7 -------- |F|NRI| Type28 | --------F禁止位与原始 NALU 的 F 位一致NRI与原始 NALU 的 NRI 一致Type固定为 28FU-AFU Header1 字节0 1 2 3 4 5 6 7 -------- |S|E|R| Type | --------SStart1 表示分片起始第一个包EEnd1 表示分片结束最后一个包R保留设为 0Type原始 NALU 的 NAL type如 5 表示 IDR分片规则第一个分片S1E0FU Payload 原始 NALU 去掉第 1 字节中间分片S0E0最后一个分片S0E1M 位设为 1完整示例一个 IDR 帧2048 字节 NALUMTU 1400包SEMFU Payload1100NALU[1..1399]2011NALU[1400..2047]四、AAC RTP 封包详解RFC 3640AAC 使用mpeg4-generic载荷格式RTP 载荷由AU Header Section AU Data Section组成。完整结构------------------------------------------------------------- | AU-headers-length | AU-header(s) | AAC raw data | | (2 字节, 大端) | (每个 2 字节) | (无 ADTS 头) | -------------------------------------------------------------AU-headers-length2 字节表示后面AU-header 部分的总比特数。对于单个 AAC 帧固定为 16即 2 字节。AU-header2 字节0 1 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 ---------------- | AU-size (13 bits) |AU-Idx (3)| ----------------AU-size13 bitsAAC 帧的原始字节数不含 AU-headerAU-Index3 bits固定为 0非交错模式完整 AAC RTP 包示例假设 raw AAC 帧为 380 字节------------------------------------------------ | RTP Header| 00 10 | AU-size | AAC raw data | | (12 字节) | (16 bits) | (133 bits)| (380 字节) | ------------------------------------------------AU-header 的13 位 AU-size 3800x17C3 位 Index 0第 1 字节0x01 (高 5 位 of 13-bit size) 第 2 字节0x7C (低 8 位 of 13-bit size) 3 位 Index0 填充接收端解析cppuint16_t auHeadersLen load_be16(payload); // 应为 16 uint16_t auHeader load_be16(payload 2); int auSize (auHeader 3) 0x1FFF; // 13 bits const uint8_t* aacData payload 4;五、代码实现5.1 RtpPacketizer 接口// protocol/rtsp/rtp/RtpPacketizer.h class RtpPacketizer { public: using PacketCB std::functionvoid(const QByteArray rtpPacket); void setOnPacket(PacketCB cb) { _onPacket std::move(cb); } void setSsrc(uint32_t ssrc) { _ssrc ssrc; } void setPayloadType(uint8_t pt) { _pt pt; } // H264: 输入 FrameAnnex-B输出 0~N 个 RTP 包 void packetizeH264(const Frame::Ptr frame); // AAC: 输入 Frameraw AAC输出 1 个 RTP 包 void packetizeAAC(const Frame::Ptr frame); private: uint16_t _seq 0; // 初始随机 uint32_t _ssrc 0; uint8_t _pt 96; PacketCB _onPacket; QByteArray buildRtpHeader(bool marker, uint32_t timestamp); };5.2 H264 封包核心实现void RtpPacketizer::packetizeH264(const Frame::Ptr frame) { // 1. 拆分 Annex-B 为 NALU 列表 auto nalus H264Utils::splitNalus(frame-data); // 2. 时间戳Frame::dts 已是 90kHz直接使用 uint32_t ts static_castuint32_t(frame-dts); // 3. 过滤 SPS/PPS单独发送或聚合发送 QListQByteArray mediaNalus; for (auto nalu : nalus) { int type uint8_t(nalu[0]) 0x1F; if (type 7 || type 8) continue; // SPS/PPS 单独处理 mediaNalus.append(nalu); } // 4. 逐 NALU 封包 for (int i 0; i mediaNalus.size(); i) { const auto nalu mediaNalus[i]; bool isLastNalu (i mediaNalus.size() - 1); if (nalu.size() 12 kMtu) { // 单 NALU 包 QByteArray pkt buildRtpHeader(isLastNalu frame-keyFrame, ts); pkt.append(nalu); if (_onPacket) _onPacket(pkt); } else { // FU-A 分片 packetizeFuA(nalu, ts, isLastNalu frame-keyFrame); } } } void RtpPacketizer::packetizeFuA(const QByteArray nalu, uint32_t ts, bool lastNaluMarker) { uint8_t nalHeader uint8_t(nalu[0]); uint8_t nri (nalHeader 0x60) 5; uint8_t nalType nalHeader 0x1F; uint8_t fuIndicator (nri 5) | 28; // Type 28 const int maxPayload kMtu - 14; // 12 RTP 2 FU int offset 1; // 跳过原始 NALU 头 bool first true; while (offset nalu.size()) { int chunkSize qMin(maxPayload, nalu.size() - offset); bool last (offset chunkSize nalu.size()); uint8_t fuHeader nalType; if (first) fuHeader | 0x80; // S1 if (last) fuHeader | 0x40; // E1 bool marker (last lastNaluMarker); QByteArray pkt buildRtpHeader(marker, ts); pkt.append(char(fuIndicator)); pkt.append(char(fuHeader)); pkt.append(nalu.constData() offset, chunkSize); if (_onPacket) _onPacket(pkt); offset chunkSize; first false; } }5.3 AAC 封包核心实现void RtpPacketizer::packetizeAAC(const Frame::Ptr frame) { // frame-data 是 raw AAC无 ADTS 头 // 需要知道采样率从 Track 获取 int sampleRate _audioTrack-sampleRate; // 时间戳转换dts 是 90kHz → 采样率时基 uint32_t ts static_castuint32_t(frame-dts * sampleRate / 90000); // 构建 AU Header Section QByteArray auHeader; uint16_t auHeadersLen 16; // 单个 AU-header固定 16 bits auHeader.append(char(auHeadersLen 8)); auHeader.append(char(auHeadersLen 0xFF)); // AU-header: 13 bits size 3 bits index(0) uint16_t sizeAndIndex (frame-data.size() 3) 0xFFF8; auHeader.append(char(sizeAndIndex 8)); auHeader.append(char(sizeAndIndex 0xFF)); // 构建完整 RTP 包 bool marker true; // AAC 一帧一包M1 QByteArray pkt buildRtpHeader(marker, ts); pkt.append(auHeader); pkt.append(frame-data); // raw AAC if (_onPacket) _onPacket(pkt); }5.4 RTP 头构建QByteArray RtpPacketizer::buildRtpHeader(bool marker, uint32_t timestamp) { QByteArray hdr(12, \0); uint8_t* p reinterpret_castuint8_t*(hdr.data()); // 第 1 字节: V2, P0, X0, CC0 p[0] 0x80; // 10 000000 // 第 2 字节: Mmarker, PTpayload type p[1] (marker ? 0x80 : 0x00) | (_pt 0x7F); // 序列号大端 p[2] uint8_t(_seq 8); p[3] uint8_t(_seq 0xFF); _seq; // 时间戳大端 p[4] uint8_t(timestamp 24); p[5] uint8_t(timestamp 16); p[6] uint8_t(timestamp 8); p[7] uint8_t(timestamp 0xFF); // SSRC大端 p[8] uint8_t(_ssrc 24); p[9] uint8_t(_ssrc 16); p[10] uint8_t(_ssrc 8); p[11] uint8_t(_ssrc 0xFF); return hdr; }六、与 RTSP 会话的集成推流端 SETUP 时SDP 中声明 PT 和编码mvideo 0 RTP/AVP 96 artpmap:96 H264/90000 afmtp:96 packetization-mode1;profile-level-id42a01e; sprop-parameter-setsZ0KAHpY0Cg,aM48gA maudio 0 RTP/AVP 97 artpmap:97 mpeg4-generic/44100/2 afmtp:97 profile-level-id15;modeAAC-hbr;SizeLength13; IndexLength3;IndexDeltaLength3;config1210推流端从Track::extradata提取 SPS/PPS 填充sprop-parameter-sets音频从AudioSpecificConfig转 Base64 填充config。接收端解析 SDP 后用 PT 和编码类型初始化RtpDepacketizerFU-A 重组、AU-header 解析。

相关新闻

JavaSE加强:File文件操作

JavaSE加强:File文件操作

java.io.File:代表文件或者文件夹的路径对象。⚠️重点:File 对象仅仅代表路径,不代表文件一定真实存在!new File 不会创建文件。包:import java.io.File;一、File 构造方法//1.传入字符串路径(相对路径 / …

2026/10/9 2:53:47 阅读更多 →
亲测有效:用 Codex 的 20 个精选 Prompt 降重,查重率仅10%!

亲测有效:用 Codex 的 20 个精选 Prompt 降重,查重率仅10%!

各位同仁好,我是七哥。一个在高校里从事人工智能 相关领域研究,钻研用大模型AI实操的学术人。可以和七哥交流学术写作或Gemini、GPT、Claude 等大模型 学术实操相关问题,多多交流,相互成就,共同进步。论文写作进入修…

2026/10/9 2:53:47 阅读更多 →
更适合科研学术党体质的神仙论文写作 SKILL 来了!

更适合科研学术党体质的神仙论文写作 SKILL 来了!

各位同仁好,我是七哥。一个在高校里从事人工智能 相关领域研究,钻研用大模型AI实操的学术人。可以和七哥交流学术写作或Gemini、GPT、Claude 等大模型 学术实操相关问题,多多交流,相互成就,共同进步。 科研过程中,很少有人能一路顺风。很多令人头疼的情况几乎每位研究…

2026/10/9 2:53:47 阅读更多 →

最新新闻

JavaWeb学生成绩管理系统课设包:JSP+Servlet+JDBC+MySQL完整实现与避坑指南

JavaWeb学生成绩管理系统课设包:JSP+Servlet+JDBC+MySQL完整实现与避坑指南

简介:这是一套面向高校课程设计、毕业设计与Java Web入门进阶的完整学生成绩管理系统源码包,基于JSP、Servlet、JDBC与MySQL实现,并引入MD5加密算法,覆盖学生、教师、管理员三类角色。学生端支持考勤管理、请假、选课、成绩查询与…

2026/10/9 3:30:12 阅读更多 →
SSM电影推荐系统:本科毕设落地指南与User-Based CF实现

SSM电影推荐系统:本科毕设落地指南与User-Based CF实现

简介:这是一套面向计算机专业本科生的毕业设计级电影推荐系统实战资源,基于Java语言与SSM(SpringSpringMVCMyBatis)主流框架开发,完整覆盖需求分析、系统实现、数据库设计到论文撰写的全流程,可直接用于毕设…

2026/10/9 3:30:12 阅读更多 →
SSH远程登录Linux实战:从原理到密钥配置和故障排查

SSH远程登录Linux实战:从原理到密钥配置和故障排查

搞Linux的朋友,几乎都绕不开一个问题:怎么把远在机房、云端甚至客厅角落的Linux服务器“抓”到自己电脑面前来操作。SSH(Secure Shell)就是干这件事的标准答案。它把本地终端和Linux服务器之间的网络通信放进一条加密隧道&#xf…

2026/10/9 3:30:12 阅读更多 →
Linux内核内存管理:Slab与Slub分配器原理及排查实战

Linux内核内存管理:Slab与Slub分配器原理及排查实战

1. 为什么Buddy System之外,内核还要再养一个分配器在Linux内核里,内存管理虽然从Buddy System(伙伴系统)开始,但实际工作中你几乎不会直接向它申请小内存。我见过不少刚接触内核的人去看alloc_pages,然后疑…

2026/10/9 3:30:12 阅读更多 →
HTTP错误状态码实战排查手册:4xx/5xx定位与解决方案

HTTP错误状态码实战排查手册:4xx/5xx定位与解决方案

接手线上问题的时候,我最先问的一句话永远是:状态码是多少?先别急着甩日志,也别让用户一遍遍复现,“HTTP错误状态码”就是服务器给我们的第一句人话——它直接告诉你请求死在了哪个环节。这篇文章我把实战里最常遇到的…

2026/10/9 3:30:12 阅读更多 →
ONS15454配置指南:单板识别与电路创建,从时隙到SNCP保护

ONS15454配置指南:单板识别与电路创建,从时隙到SNCP保护

简介:这是面向光网络运维与工程人员的一份思科ONS15454 SDH配置教学课件,聚焦同步数字体系设备的基础配置场景。内容系统覆盖客户端端口定义与SFP模块激活、STM-1/STM-4/STM-16端口类型选择、用户电路创建中的高阶电路(VC_HO_PATH_CIRCUIT&am…

2026/10/9 3:29:11 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 10:10:36 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 13:34:55 阅读更多 →