新四季網

RTMP協議封裝H264格式詳解(RTMP協議封裝H264格式詳解)

2023-05-05 16:45:11 2

首先我們獲得h264的流,在監聽裡,我們通過參數可以獲得RTMP包 IStreamPacket,調用getData方法直接獲得包數據 放入IOBuffer。以下是提取並修改數據存成h264文件的步驟

1. 添加監聽 IStreamListener

2. 通過IOBuffer的put函數將每次獲得的包數據放入新的IObuffer

3. 在流結束時將IOBuffer存成文件

4. 用工具,如UltraEdit打開文件,查看裡面的數據並分析

5. 根據分析結果修改程序,提取h264視頻文件所需的數據並存儲

1.RTMP協議

RTMP協議封包由一個包頭和一個包體組成,包頭可以是4種長度的任意一種:12, 8, 4, 1 byte(s).完整的RTMP包頭應該是12bytes,包含了時間戳,AMFSize,AMFType,StreamID信息, 8位元組的包頭只紀錄了時間戳,AMFSize,AMFType,其他字節的包頭紀錄信息依次類推 。包體最大長度默認為128位元組,通過chunkSize可改變包體最大長度,通常當一段AFM數據超過128位元組後,超過128的部分就放到了其他的RTMP封包中,包頭為一個字節. 完整的12位元組RTMP包頭每個字節的含義:

1.1 Head_Type

第一個字節Head_Type的前兩個Bit決定了包頭的長度.它可以用掩碼0xC0進行"與"計算:

Head_Type的前兩個Bit和長度對應關係:

Head_Type的後面6個Bit和StreamID決定了ChannelID。 StreamID和ChannelID對應關係:StreamID=(ChannelID-4)/5 1 參考red5

例如在rtmp包裡面經常看到的0xC2,就表示一字節的包頭,channel=2.

1.2 TiMMER

TiMMER佔3個字節紀錄的是時間戳,音視頻流的時間戳是統一排的。可分為絕對時間戳和相對時間戳。 fms對於同一個流,發布的時間戳接受的時間戳是有區別的

publish時間戳,採用相對時間戳,時間戳值等於當前媒體包的絕對時間戳與上個媒體包的絕對時間戳之間的差距,也就是說音視頻時間戳在一個時間軸上面.單位毫秒。

play時間戳,相對時間戳,時間戳值等於當前媒體包的絕對時間戳與上個同類型媒體包的絕對時間戳之間的差距,也就是說音視頻時間戳分別為單獨的時間軸,單位毫秒。

FLV格式文件時間戳,絕對時間戳,時間戳長度3個字節。超過0xFFFFFF後時間戳值等於TimeStamp &0xFFFFFF。 flv格式文件影片總時間長度保存在onMetaData的duration屬性裡面,長度為8個字節,是一個翻轉的double類型。 1.3 AMFSize

AMFSize佔三個字節,這個長度是AMF長度,可超過RTMP包的最大長度128位元組。如果超過了128位元組,那麼由多個後續RTMP封包組合,每個後續RTMP封包的頭只佔一個字節。一般就是以0xC?開頭。 1.4 AMFType

AMFSize佔三個字節,這個長度是AMF長度,可超過RTMP包的最大長度128位元組。 AMFType是包的類型

1.6 StreamID

StreamID是音視頻流的ID,如果AMFType!=0x08或!=0x09那麼 StreamID為0。 ChannelID 和StreamID之間的計算公式:StreamID=(ChannelID-4)/5 1 參考red5 例如當ChannelID為2、3、4時StreamID都為1當ChannelID為9的時候StreamID為2

2.RTMP包的數據部分分析

如果 AMFType = 0×09, 數據就是 VIDEO Data

Video Data由多個video tag組成

一個video tag,包含的信息:SPS,PPS,訪問單元分隔符,SEI,I幀包

首先我們來看下vedio tag

如果TAG包中的TagType==9時,就表示這個TAG是video.

StreamID之後的數據就表示是VideoTagHeader,VideoTagHeader結構如下:

VideoTagHeader的頭1個字節,也就是接跟著StreamID的1個字節包含著視頻幀類型及視頻CodecID最基本信息.表裡列的十分清楚.

VideoTagHeader之後跟著的就是VIDEODATA數據了,也就是videopayload.當然就像音頻AAC一樣,這裡也有特例就是如果視頻的格式是AVC(H.264)的話,VideoTagHeader會多出4個字節的信息.

相關視頻推薦

音視頻開發進階-圖文並茂分析H264編碼原理

【音視頻開發】播放器-錄屏-音視頻同步問題剖析

學習地址:【免費】FFmpeg/WebRTC/RTMP/NDK/Android音視頻流媒體高級開發-學習視頻教程-騰訊課堂

需要更多ffmpeg/webrtc..音視頻流媒體開發學習資料加群812855908領取

AVCPacketType 和CompositionTime。AVCPacketType表示接下來 VIDEODATA(AVCVIDEOPACKET)的內容:

IF AVCPacketType ==0 AVCDecoderConfigurationRecord(AVC sequence header) IF AVCPacketType == 1 One or more NALUs (Full frames are required)

AVCDecoderConfigurationRecord.包含著是H.264解碼相關比較重要的sps和pps信息,再給AVC解碼器送數據流之前一定要把sps和pps信息送出,否則的話解碼器不能正常解碼。而且在解碼器stop之後再次start之前,如seek、快進快退狀態切換等,都需要重新送一遍sps和pps的信息.AVCDecoderConfigurationRecord在FLV文件中一般情況也是出現1次,也就是第一個 video tag.

2.1 AVC sequence header分析

§ 17:1-keyframe 7-avc

§ 00:AVC sequence header -- AVC packet type

§ 00 00 00:composition time,AVC時,全0,無意義

因為AVC packet type=AVCsequence header,接下來就是AVCDecoderConfigurationRecord的內容

§ configurationVersion= 01

§ AVCProfileIndication= 42

§ profile_compatibility=00

§ AVCLevelIndication =1E

§ lengthSizeMinusOne =FF -- FLV中NALU包長數據所使用的字節數,(lengthSizeMinusOne & 3) 1,實際測試時發現總為ff,計算結果為4,下文還會提到這個數據

§ numOfSequenceParameterSets= E1 -- SPS的個數,numOfSequenceParameterSets & 0x1F,實際測試時發現總為E1,計算結果為1

§ sequenceParameterSetLength= 0x2E-- SPS的長度,2個字節,計算結果46

§ sequenceParameterSetNALUnits=6742 80 1E 96 54 0A 0F D8 0A 84 00 00 03 00 04 00 00 03 00 7B 80 00 08 00 00 0400 1F c6 38 C0 00 04 00 0 03 02 00 0F E3 1C 3B 42 44 D4-- SPS,為剛才計算的46個字節,SPS中包含了視頻長、寬的信息

§ numOfPictureParameterSets= 01 -- PPS的個數,實際測試時發現總為E1,計算結果為1

§ pictureParameterSetLength= 0004-- PPS的長度

§ pictureParameterSetNALUnits=68ce 35 20 -- PPS

2.1 AVCNALU分析

接下來又是新的一包videotag數據了

§ 17:1-keyframe 7-avc

§ 01:AVC NALU

§ 00 00 00:composition time,AVC時,全0,無意義

因為AVCPacket type = AVCNALU,接下來就是一個或多個NALU

每個NALU包前面都有(lengthSizeMinusOne & 3) 1個字節的NAL包長度描述(前文提到的,還記得嗎),前面計算結果為4個字節

§ 00 00 00 02:2 -- NALU length

§ 09 10:NAL包

這裡插入一點NALU的小知識,每個NALU第一個字節的前5位標明的是該NAL包的類型,即NAL nal_unit_type

#define NALU_TYPE_SLICE 1

#define NALU_TYPE_DPA 2

#define NALU_TYPE_DPB 3

#define NALU_TYPE_DPC 4

#define NALU_TYPE_IDR 5

#define NALU_TYPE_SEI 6

#define NALU_TYPE_SPS 7

#define NALU_TYPE_PPS 8

#define NALU_TYPE_AUD 9//訪問分隔符

#define NALU_TYPE_EOSEQ 10

#define NALU_TYPE_EOSTREAM 11

#define NALU_TYPE_FILL 12

§ 09&0x1f=9,訪問單元分隔符

前面我們解析的sps頭字節為67,67&0x1f = 7,pps頭字節為68,68&0x1f=8,正好能對應上。

§ 00 00 00 29:說明接下來的NAL包長度為41

06 00 11 80 00 af c8 00 00 03 00 00 03 00 00 af c8 00 00 03 00 00 40 010c 00 00 03 00 00 03 00 90 80 08 00 00 03 00 0880:06&0x1f=6 -- SEI

§ 00 00 0F 9F:接下來的NAL包長度

65 88 80……:65&0x1f=5 -- I幀數據

這包video tag分析到此結束了,下面會緊接著來一些該I幀對應的P幀數據,

前面說的I幀數據從65 88 80,到下圖第一行的 5F 7E B0都是上一個video tag的內容,即前面說的65 88 80那個I幀的數據拉,27開始是新的一個video tag

§ 27:2-inter frame即P幀,7-codecid=AVC

§ 01:AVCPacket type = AVC NALU

§ 00 00 00:composition time,AVC時,全0,無意義

§ 00 00 00 02 09 30:跟上面分析的一樣拉,2個字節的nal包,訪問單元分隔符

§ 00 00 00 11:17位元組的NAL包

§ 06 01 0c 00 00 80 0000 90 80 18 00 00 03 00 08 80:06&0x1f=6 --SEI

§ 00 00 0C 45: NAL包數據長度

§ 41 9A 02……: 41&0x1f=1 --P幀數據

3.H264視頻文件格式

h264的NALU和NALU之間是由00 00 01(也可以是00 00 00 01)分隔開的,我們組成h264之後的格式為

1、00 00 00 01 SPS 0000 00 01 PPS 00 00 00 01訪問單元分隔符 00 00 00 01 SEI 0000 00 01 I幀 00 00 00 01 P幀 00 00 00 01 P幀……(P幀數量不定)

其中的訪問單元分隔符和SEI不是必須的

4.將獲得的包數據存儲成H264文件

通過以上我們清楚了H264文件的格式,也分析了現在獲得的數據格式,我們需要對這些數據進行處理,得到H264視頻要求的數據格式

1.當數據是AVC squence header(只有一次)的時候,提取sps,pps數據並加入 0000 01(也可以是00 00 00 01)隔開。

2. 當數據是AVC NALU時,四個字節存儲幀數據長度,後面緊跟著數據,根據長度計算幀數據長,提取數據,加上00 00 00 01,將每個幀數據隔開。

5.red5 數據處理代碼

@Override

public void streamPublishStart(IBroadcastStreamstream) {undefined

super.streamPublishStart(stream);

stream.addStreamListener(newIStreamListener {undefined

protected boolean bFirst = true;

@Override

public void packetReceived(IBroadcastStreamarg0, IStreamPacket arg1) {undefined

IoBufferin = arg1.getData;

if(arg1.getDataType == 0x09){

System.out.println("11111");

byte[] data = new byte[in.limit];

in.get(data);

byte[] foredata = { 0, 0, 0, 1 };

ioBuffer3.put(data);

// buflimit3 = in.limit;

if( bFirst) {undefined

//AVCsequence header

ioBuffer.put(foredata);

//獲取sps

intspsnum = data[10]&0x1f;

intnumber_sps = 11;

intcount_sps = 1;

while (count_sps<=spsnum){undefined

int spslen =(data[number_sps]&0x000000FF)<<8 |(data[number_sps 1]&0x000000FF);

number_sps = 2;

ioBuffer.put(data,number_sps, spslen);

ioBuffer.put(foredata);

number_sps = spslen;

count_sps ;

}

//獲取pps

intppsnum = data[number_sps]&0x1f;

intnumber_pps = number_sps 1;

intcount_pps = 1;

while (count_pps<=ppsnum){undefined

int ppslen =(data[number_pps]&0x000000FF)<<8|data[number_pps 1]&0x000000FF;

number_pps = 2;

ioBuffer.put(data,number_pps,ppslen);

ioBuffer.put(foredata);

number_pps = ppslen;

count_pps ;

}

bFirst =false;

} else {undefined

//AVCNALU

int len =0;

int num =5;

ioBuffer.put(foredata);

while(num<data.length) {undefined

len =(data[num]&0x000000FF)<<24|(data[num 1]&0x000000FF)<<16|(data[num 2]&0x000000FF)<<8|data[num 3]&0x000000FF;

num = num 4;

ioBuffer.put(data,num,len);

ioBuffer.put(foredata);

num = num len;

}

}

System.out.println("22222");

}else if (arg1.getDataType == 0x08) {undefined

// 這存儲處理音頻數據 Audio data

}

}

});

}

,
同类文章
葬禮的夢想

葬禮的夢想

夢見葬禮,我得到了這個夢想,五個要素的五個要素,水火只好,主要名字在外面,職業生涯良好,一切都應該對待他人治療誠意,由於小,吉利的冬天夢想,秋天的夢是不吉利的
找到手機是什麼意思?

找到手機是什麼意思?

找到手機是什麼意思?五次選舉的五個要素是兩名士兵的跡象。與他溝通很好。這是非常財富,它擅長運作,職業是仙人的標誌。單身男人有這個夢想,主要生活可以有人幫忙
我不怎麼想?

我不怎麼想?

我做了什麼意味著看到米飯烹飪?我得到了這個夢想,五線的主要土壤,但是Tu Ke水是錢的跡象,職業生涯更加真誠。他真誠地誠實。這是豐富的,這是夏瑞的巨星
夢想你的意思是什麼?

夢想你的意思是什麼?

你是什​​麼意思夢想的夢想?夢想,主要木材的五個要素,水的跡象,主營業務,主營業務,案子應該抓住魅力,不能疏忽,春天夢想的吉利夢想夏天的夢想不幸。詢問學者夢想
拯救夢想

拯救夢想

拯救夢想什麼意思?你夢想著拯救人嗎?拯救人們的夢想有一個現實,也有夢想的主觀想像力,請參閱週宮官方網站拯救人民夢想的詳細解釋。夢想著敵人被拯救出來
2022愛方向和生日是在[質量個性]中

2022愛方向和生日是在[質量個性]中

[救生員]有人說,在出生88天之前,胎兒已經知道哪天的出生,如何有優質的個性,將走在什麼樣的愛情之旅,將與生活生活有什么生活。今天
夢想切割剪裁

夢想切割剪裁

夢想切割剪裁什麼意思?你夢想切你的手是好的嗎?夢想切割手工切割手有一個真正的影響和反應,也有夢想的主觀想像力。請參閱官方網站夢想的細節,以削減手
夢想著親人死了

夢想著親人死了

夢想著親人死了什麼意思?你夢想夢想你的親人死嗎?夢想有一個現實的影響和反應,還有夢想的主觀想像力,請參閱夢想世界夢想死亡的親屬的詳細解釋
夢想搶劫

夢想搶劫

夢想搶劫什麼意思?你夢想搶劫嗎?夢想著搶劫有一個現實的影響和反應,也有夢想的主觀想像力,請參閱週恭吉夢官方網站的詳細解釋。夢想搶劫
夢想缺乏缺乏紊亂

夢想缺乏缺乏紊亂

夢想缺乏缺乏紊亂什麼意思?你夢想缺乏異常藥物嗎?夢想缺乏現實世界的影響和現實,還有夢想的主觀想像,請看官方網站的夢想組織缺乏異常藥物。我覺得有些東西缺失了