【音视频】RTP

看见了

RTP

数据传输协议RTP,用于实时传输数据。

RTP数据发向偶数的UDP端口,而对应的控制信号RTCP数据发向相邻的奇数UDP端口(偶数的UDP端口+1),这样就构成一个UDP端口对。

RTP实际应用中的细节

  • 发送端,上层应用程序以分组形式将编码后的媒体数据传给RTP通信模块,作为RTP报文的有效载荷,RTP通信模块将根据上层应用提供的参数在有效载荷前添加RTP报头,形成 RTP报文,通过Socket接口选择UDP协议发送出去。
  • 接收端,RTP通信模块通过Socket接口接收到RTP报文后,将RTP报头分离出来作相应处理,再将RTP报文的有效载荷作为数据分组传递给上层应用。

    RTP结构解析

RTP报文由两部分组成:报头和有效载荷


    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
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |V=2|P|X|  CC   |M|     PT      |       sequence number         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           timestamp                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           synchronization source (SSRC) identifier            |
   +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
   |            contributing source (CSRC) identifiers             |
   |                             ....                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

根据RTP负载设置的RTP头信息
格式设置如下:

  • Marker bit (M): 1 bit 如果是1,代表是访问单元的最后一个数据包

NTP

根据RTP规范,不同的RTP媒体流是分开传输的,且使用各自独立的时间戳进行同步。假设在一次视频点播中,传输两路RTP媒体流,一路视频,一路音频。根据视频帧时间戳,可以实现视频流内同步,这很好理解,通过视频帧时间戳可以计算出相邻视频帧的时间间隔,也就是视频帧之间的相对时间关系很容易通过时间戳来确定,按照这个间隔去呈现视频,就可以获得较好的效果。同理,音频流也可以实现自身的同步。

Session Description Protocol -- SDP

在会话前需要确定网络相关和媒体相关信息。sdp是文本型松散格式结构。偏向介绍WebRTC的SDP Offer/Answer 模型。

sdp对于发送方描述了想要什么类型接收者;接收者回应了想让发送者以什么格式发送。

RTP载荷H.264码流

H264的RTP中有三种不同的基本负载:

  • Single NAL(单个NALU包模式),荷载中只包含一个NAL单元。NAL头类型域等于原始 NAL单元类型,即在范围1到23之间
  • Aggregation Packet(组合包模式),本类型用于聚合多个NAL单元到单个RTP荷载中。本包有四种版本,单时间聚合包类型A (STAP-A),单时间聚合包类型B (STAP-B),多时间聚合包类型(MTAP)16位位移(MTAP16), 多时间聚合包类型(MTAP)24位位移(MTAP24)。赋予STAP-A, STAP-B, MTAP16, MTAP24的NAL单元类型号分别是 24,25, 26, 27
  • Fragmentation Unit(分包模式),用于分片单个NAL单元到多个RTP包。现存两个版本FU-A,FU-B,用NAL单元类型 28,29标识。

常用的打包时的分包规则是:如果小于MTU采用单个NAL单元包,如果大于MTU就采用FUs分片方式。
因为常用的打包方式就是单个NAL包和FU-A方式,所以我们只解析这两种。

NAL

NAL单元由NAL头和NAL单元载荷组成。NAL单元分为两类:一类为参数集SPS,PPS NAL单元以及补充增强信息SEI NAL单元等。另一类为slice NAL单元。NAL单元载荷按8字节对齐。

+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI|  Type   |
+---------------+

F: 1 bit, forbidden_zero_bit 在 H.264 规范中规定了这一位必须为 0.
NRI: 2 bit, nal_ref_idc, H.264规范要求nal_unit_type等于6、9、10、11或12的所有NAL单元的NRI值应为0。
Type: 4 bit, nal type

Payload Packet    Single NAL    Non-Interleaved    Interleaved
Type    Type      Unit Mode           Mode             Mode
-------------------------------------------------------------
0      reserved      ig               ig               ig
1-23   NAL unit     yes              yes               no
24     STAP-A        no              yes               no
25     STAP-B        no               no              yes
26     MTAP16        no               no              yes
27     MTAP24        no               no              yes
28     FU-A          no              yes              yes
29     FU-B          no               no              yes
30-31  reserved      ig               ig               ig

到这里应该都可以生成h.264的NAL单元。

JS实现RTP解析

var FIXED_HEADER_LENGTH = 12;

class RtpPacket {
  constructor(buf) {
    // Typed check
    if (!buf instanceof Uint8Array) {
      throw new Error("buf is not Uint8Array type");
    }

    if (buf.length < FIXED_HEADER_LENGTH) {
      throw new Error("can not parse buffer smaller than fixed header");
    }

    let firstByte = buf.getUint8(0);
    let secondByte = buf.getUint8(1);
    // RTP协议的版本号
    this.version = firstByte >>> 6;
    // P(padbit):填充标志
    this.padding = (firstByte >>> 5) & 1;
    // X(extbit):扩展标志
    this.has_extension = (firstByte >>> 4) & 1;
    // CC:CSRC计数器
    this.csrcCount = firstByte & 0x0f;
    // M(markbit): 标记
    this.marker = secondByte >>> 7;
    // PT(paytype): 有效荷载类型
    this.payloadType = secondByte & 0x7f;
    // 序列号(seq_number):占16位
    this.sequenceNumber = buf.getUint16(2);
    // 时戳(timestamp)
    this.timestamp = buf.getUint32(4);
    // 同步信源(SSRC)标识符
    this.ssrc = buf.getUint32(8);
    // 特约信源(CSRC)标识符
    this.csrc = [];
    let byteIndex = FIXED_HEADER_LENGTH

    this.payload = buf.slice(FIXED_HEADER_LENGTH + 4 * this.csrcCount);
  }
}

参考文章

阅读 570

solfKwolf的前端杂记
涉及个人学习心得与经验

i'm look for a good job :)

568 声望
10 粉丝
0 条评论

i'm look for a good job :)

568 声望
10 粉丝
文章目录
宣传栏