網(wǎng)絡(luò)抽象層單元類型 (NALU):
NALU頭由一個(gè)字節(jié)組成,它的語法如下:

F: 1個(gè)比特. forbidden_zero_bit. 在 H.264 規(guī)范中規(guī)定了這一位必須為 0.
NRI: 2個(gè)比特. nal_ref_idc. 取00~11,似乎指示這個(gè)NALU的重要性,如00的NALU解碼器可以丟棄它而不影響圖像的回放.
Type: 5個(gè)比特. nal_unit_type. 這個(gè)NALU單元的類型.簡述如下:

h264僅用1-23,24以后的用在RTP H264負(fù)載類型頭中
不同類型的NALU的重要性指示如下表所示:

RTP 頭的結(jié)構(gòu):

V: RTP協(xié)議的版本號(hào),占2bits,當(dāng)前協(xié)議版本號(hào)為2
P: 填充標(biāo)志,占1bit,如果P=1,則在該報(bào)文的尾部填充一個(gè)或多個(gè)額外的八位組,它們不是有效載荷的一部分。
X: 擴(kuò)展標(biāo)志,占1bit,如果X=1,則在RTP報(bào)頭后跟有一個(gè)擴(kuò)展報(bào)頭
CC: CSRC計(jì)數(shù)器,占4位,指示CSRC 標(biāo)識(shí)符的個(gè)數(shù)
M: 1bit,標(biāo)記解釋由設(shè)置定義,目的在于允許重要事件在包流中標(biāo)記出來。如不同的有效載荷有不同的含義,對(duì)于視頻,標(biāo)記一幀的結(jié)束;對(duì)于音頻,標(biāo)記會(huì)話的開始。
負(fù)載類型 Payload type(PT): 7bits
注:rfc里面對(duì)一些早期的格式定義了這個(gè)payload type。但是后來的,如h264并沒有分配,那就用96來代替。因此現(xiàn)在96以上都不表示特定的格式,具體表示什么要用sdp或者其他協(xié)議來協(xié)商。
序列號(hào) Sequence number(SN): 16bits,用于標(biāo)識(shí)發(fā)送者所發(fā)送的RTP報(bào)文的序列號(hào),每發(fā)送一個(gè)報(bào)文,序列號(hào)增1,序列號(hào)的初始值是隨機(jī)產(chǎn)生的??梢杂糜跈z查丟包以及進(jìn)行數(shù)據(jù)包排序。
時(shí)間戳 Timestamp: 32bits,必須使用90kHz時(shí)鐘頻率。
同步信源(SSRC)標(biāo)識(shí)符: 32bits,用于標(biāo)識(shí)同步信源。該標(biāo)識(shí)符是隨機(jī)隨機(jī)產(chǎn)生的,參加同一視頻會(huì)議的兩個(gè)同步信源不能有相同的SSRC。
特約信源(CSRC)標(biāo)識(shí)符: 每個(gè)CSRC標(biāo)識(shí)符占32bits,可以有0~15個(gè)。每個(gè)CSRC標(biāo)識(shí)了包含在該RTP報(bào)文有效載荷中的所有特約信源。
上面介紹了NALU和RTP header的基本結(jié)構(gòu),下面介紹的全部都是RTP PayLoad的部分
Rtp負(fù)載第一個(gè)字節(jié)的結(jié)構(gòu)如下,它和H.264的NALU頭結(jié)構(gòu)一致,可以把它認(rèn)為是RTP h264負(fù)載類型字節(jié),完全是多增加的一個(gè)字節(jié),不影響后面的NALU結(jié)構(gòu)

封包介紹:
單一NAL單元模式
對(duì)于 NALU 的長度小于 MTU 大小的包, 一般采用單一 NAL 單元模式. 對(duì)于一個(gè)原始的 H.264 NALU 單元常由 [Start Code] [NALU Header] [NALU Payload] 三部分組成, 其中 Start Code 用于標(biāo)示這是一個(gè)
NALU 單元的開始, 必須是 "00 00 00 01" 或 "00 00 01", NALU 頭僅一個(gè)字節(jié), 其后都是 NALU 單元內(nèi)容. 打包時(shí)去除 "00 00 01" 或 "00 00 00 01" 的開始碼, 把其他數(shù)據(jù)封包的 RTP 包即可.

例: 如有一個(gè) H.264 的 NALU 是這樣的:
[00 00 00 01 67 42 A0 1E 23 56 0E 2F ... ]
這是一個(gè)序列參數(shù)集 NAL 單元. [00 00 00 01] 是四個(gè)字節(jié)的開始碼, 67 是 NALU 頭, 42 開始的數(shù)據(jù)是 NALU 內(nèi)容.
封裝成 RTP 包將如下:
[ RTP Header ] [ 67 42 A0 1E 23 56 0E 2F ]
即只要去掉 4 個(gè)字節(jié)的開始碼就可以了.
組合封包模式
其次, 當(dāng) NALU 的長度特別小時(shí), 可以把幾個(gè) NALU 單元封在一個(gè) RTP 包中.

這里只介紹STAP-A模式,如果是STAP-B的話會(huì)多加入一個(gè)DON域,另外還有MTAP16、MTAP24,具體不介紹,可以看rfc文檔,文章尾貼一個(gè)鏈接可以去看。
轉(zhuǎn)載的話注明一下作者:jwybobo2007 出處:http://blog.csdn.net/jwybobo2007/article/details/7054140
例:
如有一個(gè) H.264 的 NALU 是這樣的:
[00 00 00 01 67 42 A0 1E 23 56 0E 2F ... ]
[00 00 00 01 68 42 B0 12 58 6A D4 FF ... ]
封裝成 RTP 包將如下:
[ RTP Header ] [78 (STAP-A頭,占用1個(gè)字節(jié))] [第一個(gè)NALU長度 (占用兩個(gè)字節(jié))] [ 67 42 A0 1E 23 56 0E 2F ] [第二個(gè)NALU長度 (占用兩個(gè)字節(jié))] [68 42 B0 12 58 6A D4 FF ... ]
FU-A
當(dāng)NALU的長度超過MTU時(shí),就必須對(duì)NALU單元進(jìn)行分片封包.也稱為Fragmentation Units(FUs)。
本荷載類型允許分片一個(gè)NAL單元到幾個(gè)RTP包中。下圖 表示FU-A的RTP荷載格式。FU-A由1字節(jié)的分片單元指示,1字節(jié)的分片單元頭,和分片單元荷載組成。
FU指示字節(jié)有以下格式:
FU指示字節(jié)的類型域的28,29表示FU-A和FU-B。F的使用在5。3描述。NRI域的值必須根據(jù)分片NAL單元的NRI域的值設(shè)置。
注意:這是第一個(gè)字節(jié)FU indicator,NRI為 幀重要程度 00 可以丟 ,11不能丟。F一般設(shè)置0。這一個(gè)字節(jié)用來表示當(dāng)前包為分片F(xiàn)U-A包。
FU頭的格式如下:
S: 1 bit
當(dāng)設(shè)置成1,開始位指示分片NAL單元的開始。當(dāng)跟隨的FU荷載不是分片NAL單元荷載的開始,開始位設(shè)為0。
E: 1 bit
當(dāng)設(shè)置成1, 結(jié)束位指示分片NAL單元的結(jié)束,即, 荷載的最后字節(jié)也是分片NAL單元的最后一個(gè)字節(jié)。當(dāng)跟隨的
FU荷載不是分片NAL單元的最后分片,結(jié)束位設(shè)置為0。
R: 1 bit
保留位必須設(shè)置為0,接收者必須忽略該位。
Type: 5 bits
NAL單元荷載類型定義在[1]的表7-1.
注意:這是第二個(gè)字節(jié),用來表示開始結(jié)束和NAL幀的類型。
示例代碼:
if (iLen > iSize) { //超過MTU
const unsigned char s_e_r_Start = 0x80;
const unsigned char s_e_r_Mid = 0x00;
const unsigned char s_e_r_End = 0x40;
//獲取幀頭數(shù)據(jù),1byte
unsigned char naluType = *((unsigned char *) pcData) & 0x1f; //獲取NALU的5bit 幀類型
unsigned char nal_ref_idc = *((unsigned char *) pcData) & 0x60; //獲取NALU的2bit 幀重要程度 00 可以丟 11不能丟
//nal_ref_idc = 0x60;
//組裝FU-A幀頭數(shù)據(jù) 2byte
unsigned char f_nri_type = nal_ref_idc + 28;//F為0 1bit,nri上面獲取到2bit,28為FU-A分片類型5bit
unsigned char s_e_r_type = naluType;
bool bFirst = true;
bool mark = false;
int nOffset = 1;
while (!mark) {
if (iLen < nOffset + iSize) { //是否拆分結(jié)束
iSize = iLen - nOffset;
mark = true;
s_e_r_type = s_e_r_End + naluType;
} else {
if (bFirst == true) {
s_e_r_type = s_e_r_Start + naluType;
bFirst = false;
} else {
s_e_r_type = s_e_r_Mid + naluType;
}
}
memcpy(aucSectionBuf, &f_nri_type, 1);
memcpy(aucSectionBuf + 1, &s_e_r_type, 1);
memcpy(aucSectionBuf + 2, (unsigned char *) pcData + nOffset, iSize);
nOffset += iSize;
makeH264Rtp(aucSectionBuf, iSize + 2, mark, uiStamp);
}
} else {
makeH264Rtp(pcData, iLen, true, uiStamp);
}