RTSP RTP数据流简单解析

6 阅读4分钟

RTP(Real-time Transport Protocol,实时传输协议)数据解析的官方标准完全基于IETF(互联网工程任务组)发布的 RFC 文档
官网:

流程:

接收RTP包
  
解析RTP头部(验证版本、长度等)
  
根据Payload Type分发处理:
├── 96: H.264视频  FU-A/STAP-A重组  完整NAL单元
├── 26: JPEG视频  重建JPEG文件格式  完整JPEG帧
├── 0/8: G.711音频  直接提取音频数据
└── 其他: 不支持的格式  返回错误
  
时间戳处理(音视频同步)
  
帧重组(RTP Marker标志)
  
传递给上层解码/显示

RTSP over TCP 数据是交错发送的,所以需要从TCP流中解析数据流,区分RTSP命令和RTP数据包。
TCP大概会接收到如下交织的数据流:

RTSP/1.0 200 OK\r\nCSeq: 1\r\n...\r\n\r\n    // RTSP命令: 普通文本,以RTSP开头
$<通道><长度><RTP数据>                        // 每个RTP包以'$'开头               
$<通道><长度><RTP数据>                        // RTP包
DESCRIBE rtsp://...\r\nCSeq: 2\r\n\r\n       // RTSP命令: 普通文本

RTP数据

RTP数据 INTERLEAVED_INFO + RTP_HEADER + RTP_HEADER.csrccount * 4 + (RTP_HEADER.extension=1时4+拓展长度) + H264数据/H265数据/音频 H264数据:(NALU_HEADER + FU_HEADER + RTSP_FRAME)

typedef struct {
    tuint8  magic;      // '$' (0x24) - 交织模式标识, RTP数据以'$' (0x24)开头
    tuint8  channel;    // 通道号 (0=视频RTP, 1=视频RTCP, 2=音频RTP...)
    tuint16 datalength; // RTP数据长度(网络字节序)
} INTERLEAVED_INFO;     // 总共4字节
// h264 define
typedef struct _rtp_header_
{
	tuint8 csrccount:4;  // CSRC计数(0-15)
	tuint8 extension:1;  // 扩展标志  `0`=无扩展,`1`=有扩展头,最后一字节表示填充长度
	tuint8 padding:1;    // 填充标志   `0`=无填充,`1`=有填充
	tuint8 version:2;    // 版本(总是2)

	tuint8 payloadtype:7;    // 负载类型(Payload Type),标识编码格式,96=H.264,97=AAC
	tuint8 marker:1;         // 标记位(Mark bit),视频:帧结束标志;音频:静音开始

	tuint16 sequenceNumber;  // 序列号(16位,网络字节序)从随机值开始,递增,每包+1,用于检测丢包 
	tuint32 timeStamp;       // 时间戳(32位,网络字节序) 视频:90kHz时钟;音频:采样率时钟
	tuint32 ssrc;            // 同步源标识(32位,网络字节序)流的唯一ID,随机生成,区分不同流
        // uint32_t csrc[cc];       // 贡献源列表,仅当 CC>0 时存在
        // 仅当 X=1 时存在,第一字节是拓展数据长度+扩展数据
}RTP_HEADER;

// h264结构 ——begin
typedef struct _nalu_header_
{
	tuint8 type:5; //对应NAL_UNIT_TYPE
	tuint8 nri:2;
	tuint8 forbidden_zero_bit:1;
}NALU_HEADER;

typedef struct _fragmentation_unit_header_
{
	tuint8 type:5;			//和原始数据中nalu中的type一样
	tuint8 reserve_bit:1;	        //保留位,必须是0;
	tuint8 end_bit:1;		//指示是不是这一帧的最后一个分片
	tuint8 start_bit:1;		//指示是不是这一帧的第一个分片
}FU_HEADER;

typedef struct {
    unsigned long long ullFrametime;  // 帧时间
    bool bKeyFrame;                   // 是否为关键帧
    int frameType;                    // 帧类型(视频/音频)
    union {
        int videoEnc;                 // 视频编码类型
        int audioEnc;                 // 音频编码类型
    } encodeType;
    unsigned int dwWidth;             // 视频宽度
    unsigned int dwHeight;            // 视频高度
    unsigned int dwFrameLen;          // 帧长度
} RTSP_FRAME;
// h264结构 ——end
负载类型

负载类型可以同RTSP DESCRIBE命令解析SDP协议读出来:

PT值编码格式时钟频率说明
0PCMU (G.711 μ-law)8000音频电话质量
8PCMA (G.711 A-law)8000音频电话质量
9G.7228000音频7kHz带宽
18G.7298000音频低比特率
96H.264/AVC90000视频,最常见
97MPEG4-GENERIC (AAC)44100/48000音频
98H.26390000视频
99MPEG4-ES90000视频
102H.26190000视频
103H.263+90000视频
nal类型
typedef enum _nal_unit_type_
{
	NAL_UNIT_TYPE_ZERO	= 0,					//未规定
	SLICE_LAYER_WITHOUT_PARTITIONING_RBSP	= 1,//非IDR图像中不采用数据划分的片段
	SLICE_DATA_PARTION_A_LAYER_RBSP			= 2,//非IDR图像中A类数据划分片段
	SLICE_DATA_PARTION_B_LAYER_RBSP			= 3,//非IDR图像中B类数据划分片段
	SLICE_DATA_PARTION_C_LAYER_RBSP			= 4,//非IDR图像中C类数据划分片段
	SLICE_LAYER_WITH_PARTITIONING_RBSP		= 5,//IDR图像的片段
	SEI_RBSP					= 6,//补充增强信息 (SEI)
	SEQ_PARAMETER_SET_RBSP				= 7,//序列参数集
	PIC_PARAMETER_SET_RBSP				= 8,//图像参数集
	ACCESS_UNIT_DELIMITER_RBSP			= 9,//分割符
	END_OF_SEQ_RBSP					= 10,//序列结束符
	END_OF_STREAM_RBSP				= 11,//流结束符
	FILLER_DATA_RBSP				= 12,//填充数据
	//13-23保留  24-31未定义
	SINGLE_TIME_AGGREGATION_PACKET_A		= 24,  //单时间聚合包
	SINGLE_TIME_AGGREGATION_PACKET_B		= 25,  //单时间聚合包
	MULTI_TIME_AGGREGATION_PACKET_A			= 26,  //多时间聚合包
	MULTI_TIME_AGGREGATION_PACKET_B			= 27,  //多时间聚合包
	FRAGMENTATION_UNIT_A				= 28,	//分片的包
	FRAGMENTATION_UNIT_B				= 29,	//分片的包
}NAL_UNIT_TYPE;