第1年1月10日 flv格式
1.雷神读flv文件代码,https://blog.csdn.net/leixiaohua1020/article/details/42104945
读flv文件jump over9个字节,再jump over4个字节。再读type,datalength,timestamp,streamid。
还是要对照协议看。
9个字节的文件头
Field Type Comment Signature UI8 Signature byte always 'F' (0x46) Signature UI8 Signature byte always 'L' (0x4C) Signature UI8 Signature byte always 'V' (0x56) Version UI8 File version (for example, 0x01 for FLV version 1) TypeFlagsReserved UB [5] Shall be 0 TypeFlagsAudio UB [1] 1 = Audio tags are present TypeFlagsReserved UB [1] Shall be 0 TypeFlagsVideo UB [1] 1 = Video tags are present DataOffset UI32 The length of this header in bytes
4个字节的PreviousTagSize0,接着是FLVTAG,循环
Field Type Comment PreviousTagSize0 UI32 Always 0 Tag1 FLVTAG First tag PreviousTagSize1 UI32 Size of previous tag, including its header, in bytes. \
For FLV version 1, this value is 11 plus the DataSize of the previous tag. Tag2 FLVTAG Second tag ...
结合前文《librtmp发送h264》 ,SendH264Packet函数,SendVideoSpsPps函数就是这样组包的。
//packet attributes uint32_t type=0; uint32_t datalength=0; uint32_t timestamp=0; uint32_t streamid=0; FILE*fp=NULL; fp=fopen("cuc_ieschool.flv","rb"); if (!fp){ RTMP_LogPrintf("Open File Error.\n"); CleanupSockets(); return -1; } //jump over FLV Header fseek(fp,9,SEEK_SET); //jump over previousTagSizen fseek(fp,4,SEEK_CUR); start_time=RTMP_GetTime(); while(1) { if((((now_time=RTMP_GetTime())-start_time) <(pre_frame_time)) && bNextIsKey){ //wait for 1 sec if the send process is too fast //this mechanism is not very good,need some improvement if(pre_frame_time>lasttime){ RTMP_LogPrintf("TimeStamp:%8lu ms\n",pre_frame_time); lasttime=pre_frame_time; } Sleep(1000); continue; } //not quite the same as FLV spec if(!ReadU8(&type,fp)) break; if(!ReadU24(&datalength,fp)) break; if(!ReadTime(×tamp,fp)) break; if(!ReadU24(&streamid,fp)) break; if (type!=0x08&&type!=0x09){ //jump over non_audio and non_video frame, //jump over next previousTagSizen at the same time fseek(fp,datalength+4,SEEK_CUR); continue; } if(fread(packet->m_body,1,datalength,fp)!=datalength) break; packet->m_headerType = RTMP_PACKET_SIZE_LARGE; packet->m_nTimeStamp = timestamp; packet->m_packetType = type; packet->m_nBodySize = datalength; pre_frame_time=timestamp; if (!RTMP_IsConnected(rtmp)){ RTMP_Log(RTMP_LOGERROR,"rtmp is not connect\n"); break; } if (!RTMP_SendPacket(rtmp,packet,0)){ RTMP_Log(RTMP_LOGERROR,"Send Error\n"); break; } if(!ReadU32(&preTagsize,fp)) break; if(!PeekU8(&type,fp)) break; if(type==0x09){ if(fseek(fp,11,SEEK_CUR)!=0) break; if(!PeekU8(&type,fp)){ break; } if(type==0x17) bNextIsKey=1; else bNextIsKey=0; fseek(fp,-11,SEEK_CUR); } } RTMP_LogPrintf("\nSend Data Over\n"); if(fp) fclose(fp);
https://blog.csdn.net/leixiaohua1020/article/details/42104945
https://depthlove.github.io/2015/11/13/flv-analysis-in-rtmp-live-play/
https://blog.csdn.net/yeyumin89/article/details/7932368
2.avc
AVCDecoderConfigurationRecord
configurtaionVersion: 第一字段,占8位。其值始终为1。
AVCProfileIndication: 这个字段也占8位,填写AVC的profile. 我们一般接触比较多的profile. 有Baseline Profile(66), Main Profile(77), Extended Profile(88), High Profile(100)。这个值就是直接去AVC Sps的 profile_idc字段的值。
profile_compatibility: 这个字段就是Sps 中哪些Constraints_set 的值。所以直接去Sps第二个字节。
AVCLevelIndication: 这个字段就是Sps中的level_idc字段的值。
所以可以看出,就是直接Copy了Sps的前三个字节。
lengthSizeMinusOne:指示AVC视频中NALUnitLength字段的字节长度。 比如NALUintlength字段我们用4字节。那么这里就是4-1 = 3; 一般我们NALUingLenth字段就固定为4了,所以这个字段就是3: ‘11’b。
numOfSequenceParameterSets: 指示在这个AVCDecoderConfigurtaionRecord中包含的Sps的个数。一般就一个咯,所以取值为’00001’b。
sequenceParameterSetLength: 单个Sps的长度。
spsquenceParameterSetNALUint:Sps的实际内容。
numOfPictureParameterSets: pps的个数。一般就是1.
pictureParameterSetLength: pps的长度。注意字节序。大端
pictureParamterSetNALUint: pps的实际内容。
如果此时AVCPacketType==1.且在第一个TAG中 lengthSizeMinusOne被设置为3(4-1)。那么此时的Data字段将在实际的NALUint数据前面加上一个4字节的NALUintLength字段。注意为大端模式。然后才是实际的NALUint的数据
https://blog.csdn.net/y_z_hyangmo/article/details/79208275
3.rtmp消息分块
+--------------+----------------+--------------------+--------------+
| Basic Header | Message Header | Extended Timestamp | Chunk Data |
+--------------+----------------+--------------------+--------------+
| |
|<------------------- Chunk Header ----------------->|
Chunk Format
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 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | timestamp | message length| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | message length (cont) |message type id| msg stream id | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | message stream id (cont) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Chunk Message Header - Type 0
https://blog.csdn.net/tanningzhong/article/details/109787281