buf_in 一般是由 live555 client 获得的数据,buf_out是解包成 H264编码格式的数据,该数据直接发给解码器 就完成了解码的流程。
rtp解包代码流程
#define MEDIA_RTP_HEADER_LEN 12
typedef struct nalu_tag {
unsigned char forbidden_bit; //Should always be FALSE
unsigned char nal_reference_idc; //NALU_PRIORITY_xxxx
unsigned char nal_unit_type; //NALU_TYPE_xxxx
unsigned int startcodeprefix_len; //前缀字节数
unsigned int len; //包含nal 头的nal 长度,从第一个00000001到下一个000000001的长度
unsigned int max_size; //做多一个nal 的长度
unsigned char * buf; //包含nal 头的nal 数据
unsigned int lost_packets; //预留
} nalu_t;
/*
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
*/
typedef struct nalu_header_tag {
unsigned char Type : 5;
unsigned char NRI : 2;
unsigned char F : 1;
}nalu_header_t;
/*
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
*/
typedef struct fu_indicator_tag {
unsigned char Type : 5;
unsigned char NRI : 2;
unsigned char F : 1;
}fu_indicator_t;
/*
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|S|E|R| Type |
+---------------+
*/
typedef struct fu_header_tag {
unsigned char Type : 5;
unsigned char R : 1;
unsigned char E : 1;
unsigned char S : 1;
}fu_header_t;
typedef struct rtp_header_tag {
unsigned char cc : 4; /**< CSRC count */
unsigned char x : 1; /**< header extension flag */
unsigned char p : 1; /**< padding flag */
unsigned char v : 2; /**< packet type/version */
unsigned char pt : 7; /**< payload type */
unsigned char m : 1; /**< marker bit */
unsigned short seq; /**< sequence number */
unsigned int ts; /**< timestamp */
unsigned int ssrc; /**< synchronization source */
}rtp_header_t; //12bytes
//rtp解包出原始数据
int rtsp_unpackage(unsigned char *buf_in, int len, unsigned char *buf_out)
{
nalu_header_t *nalu_hdr = NULL;
fu_indicator_t *fu_ind = NULL;
fu_header_t *fu_hdr = NULL;
int i = 0;
nalu_hdr = (nalu_header_t*)(buf_in + MEDIA_RTP_HEADER_LEN);
if (nalu_hdr->Type > 0 && nalu_hdr->Type < 24)
{
//NALU单包
//写起始字节
buf_out[i++] = 0x00;
buf_out[i++] = 0x00;
buf_out[i++] = 0x01;
//写NAL_HEADER
memcpy(&buf_out[i++], nalu_hdr, 1);
memcpy(&buf_out[i], buf_in + MEDIA_RTP_HEADER_LEN + 1, len - MEDIA_RTP_HEADER_LEN - 1);
i += len - MEDIA_RTP_HEADER_LEN - 1;
}
else if (nalu_hdr->Type == 28 || nalu_hdr->Type == 29)
{
//FU-A或FU-B分片包,解码顺序和传输顺序相同
fu_ind = (fu_indicator_t*)(buf_in + MEDIA_RTP_HEADER_LEN);
fu_hdr = (fu_header_t*)(buf_in + MEDIA_RTP_HEADER_LEN + 1);
if (fu_hdr->E == 1) {
//分片包最后一个包
memcpy(&buf_out[i], buf_in + MEDIA_RTP_HEADER_LEN + 2, len - MEDIA_RTP_HEADER_LEN - 2);
i += len - MEDIA_RTP_HEADER_LEN - 2;
}
else {
//分片包,但不是最后一个包
if (fu_hdr->S == 1) {
//分片的第一个包
//写起始字节
buf_out[i++] = 0x00;
buf_out[i++] = 0x00;
buf_out[i++] = 0x01;
unsigned char F;
unsigned char NRI;
unsigned char TYPE;
unsigned char nh;
F = fu_ind->F << 7;
NRI = fu_ind->NRI << 5;
TYPE = fu_hdr->Type;
nh = F | NRI | TYPE;
//写NAL_HEADER
buf_out[i++] = nh;
memcpy(&buf_out[i], buf_in + MEDIA_RTP_HEADER_LEN + 2, len - MEDIA_RTP_HEADER_LEN - 2);
i += len - MEDIA_RTP_HEADER_LEN - 2;
}
else {
//如果不是第一个包
memcpy(&buf_out[i], buf_in + MEDIA_RTP_HEADER_LEN + 2, len - MEDIA_RTP_HEADER_LEN - 2);
i += len - MEDIA_RTP_HEADER_LEN - 2;
}
}
}
return i;
}
//此代码忽略了组合包,一般没有组合包
https://zhuanlan.zhihu.com/p/25685166
https://blog.csdn.net/machh/article/details/52165292
二, h264 rtp 封包详解
H.264 视频 RTP 负载格式
1. 网络抽象层单元类型 (NALU)
NALU 头由一个字节组成, 它的语法如下:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
F: 1 个比特.
forbidden_zero_bit. 在 H.264 规范中规定了这一位必须为 0.
NRI: 2 个比特.
nal_ref_idc. 取 00 ~ 11, 似乎指示这个 NALU 的重要性, 如 00 的 NALU 解码器可以丢弃它而不影响图像的回放. 不过一般情况下不太关心
这个属性.
Type: 5 个比特.
nal_unit_type. 这个 NALU 单元的类型. 简述如下:
0 没有定义
1-23 NAL单元 单个 NAL 单元包.
24 STAP-A 单一时间的组合包
25 STAP-B 单一时间的组合包
26 MTAP16 多个时间的组合包
27 MTAP24 多个时间的组合包
28 FU-A 分片的单元
29 FU-B 分片的单元
30-31 没有定义
2. 打包模式
下面是 RFC 3550 中规定的 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 |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
负载类型 Payload type (PT): 7 bits
序列号 Sequence number (SN): 16 bits
时间戳 Timestamp: 32 bits
H.264 Payload 格式定义了三种不同的基本的负载(Payload)结构. 接收端可能通过 RTP Payload
的第一个字节来识别它们. 这一个字节类似 NALU 头的格式, 而这个头结构的 NAL 单元类型字段
则指出了代表的是哪一种结构,
这个字节的结构如下, 可以看出它和 H.264 的 NALU 头结构是一样的.
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
字段 Type: 这个 RTP payload 中 NAL 单元的类型. 这个字段和 H.264 中类型字段的区别是, 当 type
的值为 24 ~ 31 表示这是一个特别格式的 NAL 单元, 而 H.264 中, 只取 1~23 是有效的值.
24 STAP-A 单一时间的组合包
25 STAP-B 单一时间的组合包
26 MTAP16 多个时间的组合包
27 MTAP24 多个时间的组合包
28 FU-A 分片的单元
29 FU-B 分片的单元
30-31 没有定义
可能的结构类型分别有:
1. 单一 NAL 单元模式
即一个 RTP 包仅由一个完整的 NALU 组成. 这种情况下 RTP NAL 头类型字段和原始的 H.264的
NALU 头类型字段是一样的.
2. 组合封包模式
即可能是由多个 NAL 单元组成一个 RTP 包. 分别有4种组合方式: STAP-A, STAP-B, MTAP16, MTAP24.
那么这里的类型值分别是 24, 25, 26 以及 27.
3. 分片封包模式
用于把一个 NALU 单元封装成多个 RTP 包. 存在两种类型 FU-A 和 FU-B. 类型值分别是 28 和 29.
分片封包
h264包在传输的时候,如果包太大,会被分成多个片。NALU头会被如下的2个自己代替。
Type=28 FU-A
+---------------+---------------+---------------
|0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+
|F|NRI| Type:28 |S|E|R| Type |
+---------------+---------------+-----------------
例:
0x7C85=01111100 10000101 (开始包)
0x7C05=01111100 00000101 (中间包)
0x7C45=01111100 01000101 (结束包)
组合封包
https://blog.csdn.net/jwybobo2007/article/details/7054140
Type=24 STAP-A
RTP 封包 解包 协议理解
https://blog.csdn.net/jwybobo2007/article/details/7054140
https://blog.csdn.net/jwybobo2007/article/details/7054140
https://blog.csdn.net/jwybobo2007/article/details/7054140