RTMP服務(wù)器(一)
一:chunk_header/rtmp_header塊由頭和數(shù)據(jù)組成。塊頭由三部分組成:
塊基本頭:1到3 字節(jié) 本字段包含塊流ID和塊類型。塊類型決定編碼的消息頭的格式。長(zhǎng)度取決于塊流ID。塊流ID是可變長(zhǎng)字段。 塊消息頭:0,3,7或11字節(jié)。本字段編碼要發(fā)送的消息的信息。本字段的長(zhǎng)度,取決于塊頭中指定的塊類型。 擴(kuò)展時(shí)間戳:0個(gè)或4字節(jié) 本字段必須在發(fā)送普通時(shí)間戳(普通時(shí)間戳是指塊消息頭中的時(shí)間戳)設(shè)置為0xffffff時(shí)發(fā)送,正常時(shí)間戳為其他值時(shí)都不應(yīng)發(fā)送本值。當(dāng)普通時(shí)間戳的值小于0xffffff時(shí),本字段不用出現(xiàn),而應(yīng)當(dāng)使用正常時(shí)間戳字段。 //rtmp packet typedef struct RtmpPacket { ChunkHeader m_ChunkHeader; //chunkheader 1-3字節(jié) //chunk msg header unsigned int m_TimeStamp; //時(shí)間戳3字節(jié) 大端模式 unsigned int m_MessageLenth; //數(shù)據(jù)大小3個(gè)字節(jié)//amfsize 大端模式 unsigned char m_MessageTypeID; //數(shù)據(jù)類型1個(gè)字節(jié) //MessageTypeID unsigned int m_MessgageStreamID; //流ID 4字節(jié) “小”端模式 unsigned int m_ExtendTimeStamp; //擴(kuò)展時(shí)間戳4字節(jié) //則m_MessageLenth== 100,m_BytesRead == 50; unsigned int m_hasAbsTimestamp; //11字節(jié)的完整ChunkMsgHeader的TimeStamp是絕對(duì)值 char * m_PacketData; //包數(shù)據(jù)內(nèi)容 }RtmpPacket;
//chunk 頭 typedef struct ChunkHeader { unsigned char m_F0mt; //塊類型2位 unsigned int m_CsID/m_ChunkStreamId; //編碼塊流ID 6位或字節(jié)位或字節(jié)位 大端模式 }ChunkHeader;
Rtmp可以衍生出21種packetheader,即ChunkHeader一個(gè)字節(jié),沒有擴(kuò)展時(shí)間戳,常用的4種分別為: 類型0 0類型的塊長(zhǎng)度為11字節(jié)。在一個(gè)塊流的開始和時(shí)間戳返回的時(shí)候必須有這種塊。 時(shí)間戳:3字節(jié) 對(duì)于0類型的塊。消息的絕對(duì)時(shí)間戳在這里發(fā)送。如果時(shí)間戳大于或等于16777215(16進(jìn)制0x00ffffff),該值必須為16777215,并且擴(kuò)展時(shí)間戳必須出現(xiàn)。否則該值就是整個(gè)的時(shí)間戳。 類型1 類型1的塊占7個(gè)字節(jié)長(zhǎng)。消息流 ID不包含在本塊中。塊的消息流ID與先前的塊相同。具有可變大小消息的流,在第一個(gè)消息之后的每個(gè)消息的第一個(gè)塊應(yīng)該使用這個(gè)格式。
類型2 類型2的塊占3個(gè)字節(jié)。既不包含流ID也不包含消息長(zhǎng)度。本塊使用的流ID和消息長(zhǎng)度與先前的塊相同。具有固定大小消息的流,在第一個(gè)消息之后的每個(gè)消息的第一個(gè)塊應(yīng)該使用這個(gè)格式。
類型3 類型3的塊沒有頭。流ID,消息長(zhǎng)度,時(shí)間戳都不出現(xiàn)。這種類型的塊使用與先前塊相同的數(shù)據(jù)。當(dāng)一個(gè)消息被分成多個(gè)塊,除了第一塊以外,所有的塊都應(yīng)使用這種類型。由相同大小,流ID,和時(shí)間間隔的流在類型2的塊之后應(yīng)使用這種塊。 如果第一個(gè)消息和第二個(gè)消息的時(shí)間增量與第一個(gè)消息的時(shí)間戳相同,那么0類型的塊之后必須是3類型的塊而,不需要類型2的塊來注冊(cè)時(shí)間增量。如果類型3的塊在類型0的塊之后,那么類型3的時(shí)間戳增量與0類型的塊的時(shí)間戳相同。 時(shí)間戳增量:3字節(jié) 對(duì)于類型1的塊和類型2的塊,本字段表示先前塊的時(shí)間戳與當(dāng)前塊的時(shí)間戳的差值。如果增量大于等于1677215(16進(jìn)制0x00ffffff),這個(gè)值必須是16777215 ,并且擴(kuò)展時(shí)間戳必須出現(xiàn)。否則這個(gè)值就是整個(gè)的增量。 消息長(zhǎng)度:3字節(jié) 對(duì)于類型0或類型1的塊本字段表示消息的長(zhǎng)度。 注意,這個(gè)值通常與負(fù)載長(zhǎng)度是不相同的。The chunk payload length is themaximum chunk size for all but the last chunk, and the remainder (which maybe the entire length, for small messages) for the last chunk. 消息類型ID:1字節(jié) 對(duì)于0類型和1類型的塊,本字段發(fā)送消息類型。 消息流ID:4 字節(jié) 對(duì)于0類型的塊,本字段存儲(chǔ)消息流ID。通常,在一個(gè)塊流中的消息來自于同一個(gè)消息流。雖然,由于不同的消息可能復(fù)用到一個(gè)塊流中而使頭壓縮無法有效實(shí)施。但是,如果一個(gè)消息流關(guān)閉而另一個(gè)消息流才打開,那么通過發(fā)送一個(gè)新的0類型的塊重復(fù)使用一個(gè)存在的塊流也不是不可以。
/* MessageTypeID -數(shù)據(jù)類型 0× Chunk Size changes the chunk size for packets 0× Unknown 0× Bytes Read send every x bytes read by both sides 0× Ping ping is a stream control message, hassubtypes 0× Server BW the servers downstream bw 0× Client BW the clients upstream bw 0× Unknown 0× Audio Data packet containing audio 0× Video Data packet containing video data 0x0A-0x0E Unknown 0x0F FLEX_STREAM_SENDTYPE_FLEX_STREAM_SEND 0x10 FLEX_SHARED_OBJECT TYPE_FLEX_SHARED_OBJECT 0x11 FLEX_MESSAGE TYPE_FLEX_MESSAGE 0× Notify an invoke which does not expect a reply 0× Shared Object has subtypes 0× Invoke like remoting call, used for stream actionstoo. 0× StreamData 這是FMS3出來后新增的數(shù)據(jù)類型,這種類型數(shù)據(jù)中包含AudioData和VideoData */ /* RTMP_PACKET_TYPE_... 0x00 */ #define RTMP_PACKET_TYPE_CHUNK_SIZE 0x01 /* RTMP_PACKET_TYPE_... 0x02 */ #define RTMP_PACKET_TYPE_BYTES_READ_REPORT 0x03 #define RTMP_PACKET_TYPE_CONTROL 0x04 #define RTMP_PACKET_TYPE_SERVER_BW 0x05 #define RTMP_PACKET_TYPE_CLIENT_BW 0x06 /* RTMP_PACKET_TYPE_... 0x07 */ #define RTMP_PACKET_TYPE_AUDIO 0x08 #define RTMP_PACKET_TYPE_VIDEO 0x09 /* RTMP_PACKET_TYPE_... 0x0A */ /* RTMP_PACKET_TYPE_... 0x0B */ /* RTMP_PACKET_TYPE_... 0x0C */ /* RTMP_PACKET_TYPE_... 0x0D */ /* RTMP_PACKET_TYPE_... 0x0E */ #define RTMP_PACKET_TYPE_FLEX_STREAM_SEND 0x0F #define RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT 0x10 #define RTMP_PACKET_TYPE_FLEX_MESSAGE 0x11 #define RTMP_PACKET_TYPE_INFO 0x12 #define RTMP_PACKET_TYPE_SHARED_OBJECT 0x13 #define RTMP_PACKET_TYPE_INVOKE 0x14 /* RTMP_PACKET_TYPE_... 0x15 */ #define RTMP_PACKET_TYPE_FLASH_VIDEO 0x16
二:amf結(jié)構(gòu)/* AMF數(shù)據(jù)由部分組成:ObjType加上ObjValue。ObjType的大小為一個(gè)字節(jié)。ObjValue的大小不固定,和ObjType相關(guān)。常用的ObjType類型和對(duì)應(yīng)的ObjValue大小整理如下,詳細(xì)的ObjType的數(shù)據(jù)在本文的最下面列出: 類型說明(ObjType) 數(shù)據(jù) dataSize CORE_String 0x02 2字節(jié)(2字節(jié)的數(shù)據(jù)紀(jì)錄了String的實(shí)際長(zhǎng)度) CORE_Object 0x03 0字節(jié)(開始嵌套x00000009表示嵌套結(jié)束) NULL 0x05 0字節(jié)空字節(jié)無意義 CORE_NUMBER 0x00 8字節(jié) CORE_Map 0x08 4字節(jié)(開始嵌套) CORE_BOOLEAN 0x01 1字節(jié)
ObjValue不一定是一個(gè)固定的大小,他可以包含另外一個(gè)AMF數(shù)據(jù),這另外一個(gè)AMF數(shù)據(jù)里面又有ObjType 加上ObjValue,也就是AMF數(shù)據(jù)的嵌套關(guān)系 AMF0數(shù)據(jù)的嵌套關(guān)系如下: Object={ObjType + ObjValue} CORE_BOOLEAN={Value(1 Byte)} CORE_NUMBER={Value(8 Byte)} CORE_String={StringLen(2Byte) + StringValue(StringLen Byte)} CORE_DATE={value(10 Byte)} CORE_Array={ArrayLen(4 Byte)+ Object} CORE_Map={MapNum(4 Byte) +CORE_Object} CORE_Object={CORE_String +Object} */ typedef enum { AMF_NUMBER = 0, AMF_BOOLEAN,AMF_STRING, AMF_OBJECT, AMF_MOVIECLIP, /* reserved, not used */ AMF_NULL, AMF_UNDEFINED,AMF_REFERENCE, AMF_ECMA_ARRAY,AMF_OBJECT_END, AMF_STRICT_ARRAY, AMF_DATE, AMF_LONG_STRING,AMF_UNSUPPORTED, AMF_RECORDSET, /* reserved, not used */ AMF_XML_DOC, AMF_TYPED_OBJECT, AMF_AVMPLUS, /* switch to AMF3 */ AMF_INVALID = 0xff } AMFDataType;
三:rtmp拆包組包分塊使高層協(xié)議的大消息分割成小的消息,保證大的低優(yōu)先級(jí)消息不阻塞小的高優(yōu)先級(jí)消息。分塊把原本應(yīng)該消息中包含的信息壓縮在塊頭中減少了小塊消息發(fā)送的開銷。 塊大小是可配置的。最大塊是65535字節(jié),最小塊是128字節(jié)。塊越大CPU使用率越低,但是也導(dǎo)致大的寫入,在低帶寬下產(chǎn)生其他內(nèi)容的延遲。塊大小對(duì)每個(gè)方向都保持獨(dú)立。 1:拆包:即當(dāng)組成一個(gè)完整的rtmppacket有header和body,將此包分成小塊,一般發(fā)送第一個(gè)包時(shí)候需要完整的12字節(jié)頭或更多,剩下發(fā)送的包根據(jù)ChunkStreamId:編碼塊流ID匹配。 2:組包:即當(dāng)接收到rtmp分片包,根據(jù)ChunkStreamId:編碼塊流ID匹配組成一個(gè)完整的rtmp包,無論是命令或數(shù)據(jù)。
四:flv發(fā)包處理這里需要解析flv流。 1:發(fā)包:首先:去掉9個(gè)字節(jié)的flv文件頭(如果是流不用去掉), 然后分析各個(gè)tag類型,flv有三種tag分別是Script,Video,Audio. Script:里面存放的是flv流或文件的信息,寬高,文件長(zhǎng)度等等。 Video:存放的視頻數(shù)據(jù)(其中包含sps,pps ,用AvcC結(jié)構(gòu)表示),結(jié)構(gòu)如下 typedef struct Tag_Video_AvcC { unsigned char configurationVersion; unsigned char AVCProfileIndication; unsigned char profile_compatibility; unsigned char AVCLevelIndication; unsigned char reserved_1; unsigned char lengthSizeMinusOne; unsigned char reserved_2; unsigned char numOfSequenceParameterSets; //一般都是一個(gè) unsigned int sequenceParameterSetLength; //sps長(zhǎng)度 unsigned char * sequenceParameterSetNALUnit; //sps unsigned char numOfPictureParameterSets; //一般都是一個(gè) unsigned int pictureParameterSetLength; //pps長(zhǎng)度 unsigned char * pictureParameterSetNALUnit; //pps unsigned char reserved_3; unsigned char chroma_format; unsigned char reserved_4; unsigned char bit_depth_luma_minus8; unsigned char reserved_5; unsigned char bit_depth_chroma_minus8; unsigned char numOfSequenceParameterSetExt; unsigned int sequenceParameterSetExtLength; unsigned char * sequenceParameterSetExtNALUnit; }Video_AvcC; Audio: 存放的視頻數(shù)據(jù)(其中包含聲道,采樣率樣本等信息 ,用ASC結(jié)構(gòu)表示):結(jié)構(gòu)如下: typedef struct Tag_Audio_ASC { unsigned char audioObjectType; //編解碼類型:AAC-LC = 0x02 unsigned char samplingFrequencyIndex; //采樣率44100 = 0x04 unsigned char channelConfiguration; //聲道= 2 unsigned char framelengthFlag; //標(biāo)志位,位于表明IMDCT窗口長(zhǎng)度= 0 unsigned char dependsOnCoreCoder; //標(biāo)志位,表明是否依賴于corecoder = 0 unsigned char extensionFlag; //選擇了AAC-LC = 0
}Audio_ASC; 發(fā)包的時(shí)候,根據(jù)獲取到的流信息://音頻(x08)、視頻(x09)和script data(x12),其它保留 填寫rtmppacket頭然后拆包發(fā)出去。 2:收包:收包的時(shí)候,首先將分片的包組成完整的包,然后根據(jù)上面結(jié)構(gòu)解析出視頻:sps,pps。音頻采樣率,樣本,聲道等,如果是aac填寫7字節(jié)adts頭,然后一幀一幀的接收數(shù)據(jù)存入內(nèi)存或者渲染。 五:命令流程以s代表服務(wù)器,c代表客戶端(publish或play)。 1:publish
Accept之后 c->s:c0c1 s->c:s0s1s2 c->s:c2 c->s:connect命令 s->c:windowacknowledgement size set peer bandwidth __result c->s:windowacknowledgement size s->c:onbwdone c->s:checkbw c->s:releasestream fcpublish createstream s->c:__result c->s:publish s->c:onstatus; c->s:開始向服務(wù)器發(fā)送數(shù)據(jù) 2:playerAccept之后 c->s:c0c1 s->c:s0s1s2 c->s:c2 c->s:connect命令 s->c:windowacknowledgement size set peerbandwidth __result c->s:windowacknowledgement size s->c:onbwdone c->s:checkbw c->s:createstream s->c:__result c->s:play s->c:onstatus; s->c:服務(wù)器開始向player發(fā)送數(shù)據(jù)
當(dāng)c向服務(wù)器發(fā)送命令,服務(wù)器給個(gè)響應(yīng),回復(fù)的命令對(duì)應(yīng)是用rtmp body中的mumber做對(duì)應(yīng)的。再有當(dāng)MessageTypeID /type id == 0x14或0x11時(shí)候說明服務(wù)器給客戶端發(fā)送的命令完成。
即MessageTypeID /type id == #define RTMP_PACKET_TYPE_INVOKE 0x14或 MessageTypeID/typeid == #define RTMP_PACKET_TYPE_FLEX_MESSAGE 0x11。 解釋下三個(gè)ID; 1: MessageTypeID:MessageTypeID中的不同類型,如: #define RTMP_PACKET_TYPE_AUDIO 0x08 #define RTMP_PACKET_TYPE_VIDEO 0x09 2:chunkstream id :分片區(qū)分,如有個(gè)包大于128分包,當(dāng)傳輸?shù)诙€(gè)分片的時(shí)候要和第一個(gè)分片的chunk stream id相同才能判斷是一個(gè)包。 3:streamid :connectstream之前填寫0,之后為了區(qū)分flv/f4v流中多路流由server傳給client。
//注: AMF3 比AMF0 的 數(shù)據(jù)body前面多一個(gè)字節(jié)的“00”; 例如: amf0: (HandleInvoke(r,packet->m_body,packet->m_nBodySize) Amf3: (HandleInvoke(r,packet->m_body + 1,packet->m_nBodySize - 1)
上面用以區(qū)分 amf0 和amf3。 |
|