flv的格式

flv封装单元是以tag来表示的,一个tag可以是音频tag或者视频tag,或者脚本tag及其其他类型。

一、flv的格式

  • flvheader
  • 脚本tag(metadata)
  • 第一个视频tag(h264_spspps)
  • 第一个音频tag(aac_header)
  • 第二个视频tag(h264第一个关键帧)
  • 后面就是音频和视频tag交互存在

二、tag的格式:

  • [TYPE] (1byte)
  • [body size] (3byte)
  • [timestamp] (4byte)
  • [stream ID] (3byte)
  • [body data]
  • [previousTagSize] (4byte)

三、flv header

文件头由 9 bytes 组成

  • [1-3] 前3个 bytes 是文件类型,总是“FLV”,也就是(0x46 0x4C 0x56)。
  • [4] 第4 btye 是版本号,目前一般是 0x01。
  • [5] 第5 byte 是流的信息:倒数第一 bit 是1表示有视频(0x01),倒数第三 bit 是1表示有音频(0x4),有视频又有音频就是 0x01 | 0x04(0x05),其他都应该是0。
  • [6-9] 最后 4 bytes 表示 FLV 头的长度,3+1+1+4 = 9。

四、flv body

由若干个 tag (tag header+tag data)组成

[4 bytes 记录着上一个 tag 的长度]+[11 bytes的tag header]+[tag data]

4.1 tag header

  • [1] 第1个 byte 为记录着 tag 的类型,音频(0x8),视频(0x9),脚本(0x12);
  • [2-4] 第2到4 bytes 是数据区的长度,也就是 tag data 的长度;
  • [5-7] 再后面3个 bytes 是时间戳,单位是毫秒,类型为0x12则时间戳为0;
  • [8] 时间戳后面一个 byte 是扩展时间戳,时间戳不够长的时候用;
  • [9-11] 最后3 bytes 是 streamID,但是总为0

第一个tag的前面没有tag,所以第一个tag前面的previousTagSize就是 00 00 00 00

4.2 tag data

4.2.1 脚本tag data

该类型 Tag 又通常被称为Metadata(元数据) Tag,会放一些关于 FLV 视频和音频的参数信息,如duration、width、height等。通常该类型 Tag 会跟在 File Header 后面作为第一个 Tag 出现,而且只有一个。

包含两个 AMF 包。AMF(Action Message Format)是 Adobe 设计的一种通用数据封装格式,在Adobe 的很多产品中应用,简单来说,AMF 将不同类型的数据用统一的格式来描述。

第一个 AMF 包封装字符串类型数据,用来装入一个“onMetaData”标志

第二个 AMF 包封装一个数组类型,这个数组中包含了音视频信息项的名称和值

第一个 AMF 包:

  • [1] 第1个字节表示 AMF 包类型,一般总是0x02,表示字符串,其他值表示意义请查阅文档。
  • [2-3] 第2-3个字节为 UI16 类型值,表示字符串的长度,一般总是 0x000A(“onMetaData”长度)。
  • [4-…] 后面字节为字符串数据,一般总为“onMetaData”。

第二个AMF包:

  • [1] 第1个字节表示 AMF 包类型,一般总是 0x08,表示数组。
  • [2-5] 第2-5个字节为 UI32 类型值,表示数组元素的个数。
  • [6-…] 后面即为各数组元素的封装,

数组元素为元素名称和值组成的对。表示方法如下:

  • [1-2] 第1-2个字节表示元素名称的长度,假设为L。
  • [3- L+2] 后面跟着为长度为L的字符串。
  • [L+3] 第L+3个字节表示元素值的类型。
  • [L+4-…] 后面跟着为对应值,占用字节数取决于值的类型。
4.2.2 音频tag data

tag data 如果是音频数据,第一个 byte 记录 audio 信息:
前 4 bits 表示音频格式(全部格式请看官方文档):

  • 0 – 未压缩
  • 1 – ADPCM
  • 2 – MP3
  • 4 – Nellymoser 16-kHz mono
  • 5 – Nellymoser 8-kHz mono
  • 10 – AAC

下面两个 bits 表示 samplerate:

  • 0 – 5.5KHz
  • 1 – 11kHz
  • 2 – 22kHz
  • 3 – 44kHz

下面1 bit 表示采样长度:

  • 0 – snd8Bit
  • 1 – snd16Bit

下面1 bit 表示类型:

  • 0 – sndMomo
  • 1 – sndStereo

之后是数据。

4.2.3 视频tag data

如果是视频数据,第一个 byte 记录 video 信息:
前4 bits 表示类型:

  • 1 – keyframe
  • 2 – inner frame
  • 3 – disposable inner frame (h.263 only)
  • 4 – generated keyframe

后4 bits 表示解码器 ID:

  • 2 – seronson h.263
  • 3 – screen video
  • 4 – On2 VP6
  • 5 – On2 VP6 with alpha channel
  • 6 – Screen video version 2
  • 7 – AVC (h.264)

之后是数据。

五、实例代码

char body[1024] = {0}; 
char * p = (char *)body;  
 p = put_byte(p, AMF_STRING );
 p = put_amf_string(p , "@setDataFrame" );

 p = put_byte( p, AMF_STRING );
 p = put_amf_string( p, "onMetaData" );

 p = put_byte(p, AMF_OBJECT );  
 p = put_amf_string( p, "title" );  
 p = put_byte(p, AMF_STRING );  
 p = put_amf_string( p, "ipc" );  

 p =put_amf_string( p, "width");
 p =put_amf_double( p, lpMetaData->nWidth);

 p =put_amf_string( p, "height");
 p =put_amf_double( p, lpMetaData->nHeight);

 p =put_amf_string( p, "framerate" );
 p =put_amf_double( p, lpMetaData->nFrameRate); 

 p =put_amf_string( p, "videocodecid" );
 p =put_amf_double( p, FLV_CODECID_H264 );

 p =put_amf_string( p, "" );
 p =put_byte( p, AMF_OBJECT_END  );

// int index = p-body;

 SendPacket(RTMP_PACKET_TYPE_INFO,(unsigned char*)body,p-body,0);

 int i = 0;
 body[i++] = 0x17; // 1:keyframe  7:AVC
 body[i++] = 0x00; // AVC sequence header

 body[i++] = 0x00;
 body[i++] = 0x00;
 body[i++] = 0x00; // fill in 0;

 // AVCDecoderConfigurationRecord.
 body[i++] = 0x01; // configurationVersion
 body[i++] = lpMetaData->Sps[1]; // AVCProfileIndication
 body[i++] = lpMetaData->Sps[2]; // profile_compatibility
 body[i++] = lpMetaData->Sps[3]; // AVCLevelIndication 
    body[i++] = 0xff; // lengthSizeMinusOne  

    // sps nums
 body[i++] = 0xE1; //&0x1f
 // sps data length
 body[i++] = lpMetaData->nSpsLen>>8;
 body[i++] = lpMetaData->nSpsLen&0xff;
 // sps data
 memcpy(&body[i],lpMetaData->Sps,lpMetaData->nSpsLen);
 i= i+lpMetaData->nSpsLen;

 // pps nums
 body[i++] = 0x01; //&0x1f
 // pps data length 
 body[i++] = lpMetaData->nPpsLen>>8;
 body[i++] = lpMetaData->nPpsLen&0xff;
 // sps data
 memcpy(&body[i],lpMetaData->Pps,lpMetaData->nPpsLen);
 i= i+lpMetaData->nPpsLen;

 return SendPacket(RTMP_PACKET_TYPE_VIDEO,(unsigned char*)body,i,0);