RTP 扩展字段

 基础知识

RTSP RTP RTCP SDP基础知识_rtp rtsp-优快云博客

扩展类型

基本扩展字段

mid (Media Identification)

  • 作用:媒体流标识符,用于标识 RTP 包属于哪个媒体流

  • URI:urn:ietf:params:rtp-hdrext:sdes:mid

  • 用途:在 RTP Bundle 多路复用中,帮助接收端区分不同的媒体流(音频、视频等)

  • 重要性:WebRTC 中实现多媒体流复用的关键机制

rid (RTP Stream ID)

  • 作用:RTP 流标识符,用于区分同一媒体源的不同编码流(如联播)

  • URI:urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id

  • 用途:支持同一视频源的多个分辨率/码率版本的联播传输

  • 应用场景:视频会议中根据网络条件选择不同质量的视频流

rrid (Repaired RTP Stream ID)

  • 作用:修复流标识符,标识重传包对应的原始流

  • URI:urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id

  • 用途:用于 RTX(重传)机制,指示重传包修复的是哪个原始流

  • 重要性:提高传输可靠性,减少丢包影响

时间相关扩展

absSendTime (Absolute Send Time)

  • 作用:绝对发送时间戳,记录包离开发送方的时间

  • URI:http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time

  • 格式:24 位固定点数,6.18 格式,3.8μs 精度

  • 用途:服务端带宽估算、拥塞控制、网络延迟测量

absCaptureTime (Absolute Capture Time)

  • 作用:绝对捕获时间戳,记录媒体帧的原始捕获时间

  • URI:http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time

  • 用途:音视频同步、端到端延迟测量,特别适用于有中间处理节点的场景

toffset (Transmission Time Offset)

  • 作用:传输时间偏移,记录实际发送时间与标称发送时间的差值

  • URI:urn:ietf:params:rtp-hdrext:toffset

  • 格式:24 位有符号整数

  • 用途:更精确的抖动计算,适用于非实时传输场景

拥塞控制扩展

transportWideCc01 (Transport Wide Congestion Control)

  • 作用:传输层拥塞控制,提供跨所有媒体流的统一拥塞控制

  • URI:http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01

  • 机制:每个包分配唯一序号,接收端反馈到达时间信息

  • 优势:相比单流拥塞控制更准确、高效

视频相关扩展

frameMarking07 / frameMarking (Frame Marking)

  • 作用:视频帧标记,提供帧的关键信息用于转发决策

  • URI:urn:ietf:params:rtp-hdrext:framemarking

  • 信息:帧开始/结束、独立帧、可丢弃帧、时域/空域层信息

  • 用途:SFU 选择性转发,不需要解码即可进行帧级处理

  • 注意:frameMarking07 是旧版本,将被标准版本替代

videoOrientation (Video Orientation)

  • 作用:视频方向信息,指示视频需要的旋转角度

  • URI:urn:3gpp:video-orientation

  • 用途:移动设备旋转时避免重新编码,通过头部扩展传递旋转信息

  • 限制:Firefox 和某些服务端不支持,需要移除此扩展强制客户端编码旋转视频

音频扩展

ssrcAudioLevel (Audio Level)

  • 作用:音频电平指示,包含音频包的音量信息

  • URI:urn:ietf:params:rtp-hdrext:ssrc-audio-level

  • 格式:包含音量值和语音活动检测位

  • 用途:服务端无需解码即可识别活跃说话人、实现音频混音

/**
 * RTP头部扩展ID集合。
 * 
 * 用于存储RTP头部扩展的ID值,部分ID由使用相同Transport的所有Producers共享,
 * 其他ID则每个Producer各不相同。值为0表示未设置该扩展。
 */
struct RtpHeaderExtensionIds {
    // 0 means no id.
    uint8_t mid{0u};
    uint8_t rid{0u};
    uint8_t rrid{0u};
    uint8_t absSendTime{0u};
    uint8_t transportWideCc01{0u};
    uint8_t frameMarking07{0u};  // NOTE: Remove once RFC.
    uint8_t frameMarking{0u};
    uint8_t ssrcAudioLevel{0u};
    uint8_t videoOrientation{0u};
    uint8_t toffset{0u};
    uint8_t absCaptureTime{0u};
};

  扩展字段封包封包   

依然是RTP固定头: 12字节

扩展字段:

起始固定头部 4 字节(0xBEDE + 长度) 单字节模式 or twoByte模式则是0x1000
该扩展的条目头 1 字节(ID+len)+ 1 字节数据(level)
对齐填充到 32-bit 边界
合计通常为 8 字节

举例:
RTP固定头: 12字节:其中常见90是包含扩展 80是不包含扩展

BE DE 00 03 42 00 00 00 51 00 00 A0 CE 00 00 00
RTP固定头: 12字节
扩展头+扩展数据: 16字节
0xBE 0xDE 0x00 0x03 → RFC5285 one-byte扩展头,长度=3个32-bit字=12字节扩展数据
扩展数据逐项:
0x42 00 00 00 → 0100 0010 ID=4,len=2 数据全0(多为 AbsoluteSendTime 3字节占位)
0x51 00 00 → 0101 0001 ID=5,len=1字节,数据全0(多为 TransportWideCc 2字节占位)
0xA0 CE → 1010 0000 ID=10,len=0字节,0xCE=1100 1110 → V=1,level=0x4E=78(AudioLevel)
0x00 0x00 0x00 → 填充到32-bit对齐
合计: 12 + (4 + 12) = 28字节

剥离扩展字段示例代码

把包含扩展字段的rtp转换成标准12字节头部rtp。

bool StripRtpExtensionBlock(uint8_t* rtp, size_t& rtp_len, bool stripKnownProfilesOnly = true) {
    if (!rtp || rtp_len < 12) return false;

    const uint8_t vpxcc = rtp[0];
    const uint8_t cc = (vpxcc & 0x0F);
    const bool xbit_set = (vpxcc & 0x10) != 0;

    // 扩展起始偏移:固定头(12) + CSRC(4*CC)
    size_t ext_off = 12u + (static_cast<size_t>(cc) * 4u);
    if (!xbit_set) return true;                 // 没有扩展
    if (ext_off + 4u > rtp_len) return false;   // 不完整

    // 读取扩展头:profile(16) + length(16, 单位32-bit word)
    const uint16_t profile = (static_cast<uint16_t>(rtp[ext_off + 0]) << 8) |
                              static_cast<uint16_t>(rtp[ext_off + 1]);
    const uint16_t length_words = (static_cast<uint16_t>(rtp[ext_off + 2]) << 8) |
                                   static_cast<uint16_t>(rtp[ext_off + 3]);
    const size_t ext_bytes = 4u + static_cast<size_t>(length_words) * 4u; // 整块扩展字节数

    if (ext_off + ext_bytes > rtp_len) return false; // 越界保护

    if (stripKnownProfilesOnly) {
        // 仅剥离 RFC5285 的两种规范扩展
        if (profile != 0xBEDE && profile != 0x1000) return true;
    }
    // 清除固定头 X 位
    rtp[0] = static_cast<uint8_t>(rtp[0] & ~0x10);

    // 剩余数据整体前移覆盖扩展区
    const size_t tail_off = ext_off + ext_bytes;
    const size_t tail_len = rtp_len - tail_off;
    if (tail_len > 0) {
        std::memmove(rtp + ext_off, rtp + tail_off, tail_len);
    }
    rtp_len -= ext_bytes;
    return true;
}

/ - src - 谷歌的 Git --- / - src - Git at Google

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值