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;
}
//此代碼忽略了組合包,一般沒有組合包
RTP 數據封包分析
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