zoukankan      html  css  js  c++  java
  • h264 aac 封装 flv

    Part 1flvtag组成

    FLV 文件结构由 FLVheader和FLVBody组成。(注意flv文件是大端格式的)
    FLV头组成(以c为例子,一字节对齐):
    FLVBody是由若干个Tag组成的;
         Tag=Tag头(11字节)+数据

    [cpp] view plain copy
     
    1. typedef struct _FLV_HEADER  
    2. {  
    3.     char FLV[3];//={0x46,0x4c,0x56};  
    4.     char Ver;   //版本号  
    5.     char StreamInfo;// 有视频又有音频就是0x01 | 0x04(0x05)  
    6.     int HeaderLen; /*****头的长度*****/  
    7. } FLV_HEADER;  

    Part2 h264

    H264是一个个NALU单元组成的,每个单元以00 00 01 或者 00 00 00 01分隔开来,每2个00 00 00 01之间就是一个NALU单元。我们实际上就是将一个个NALU单元封装进FLV文件。
    每个NALU单元开头第一个byte的低5bits表示着该单元的类型,即NAL nal_unit_type:

    [cpp] view plain copy
     
    1. #define NALU_TYPE_SLICE 1  
    2. #define NALU_TYPE_DPA 2  
    3. #define NALU_TYPE_DPB 3  
    4. #define NALU_TYPE_DPC 4  
    5. #define NALU_TYPE_IDR 5    /**关键帧***/  
    6. #define NALU_TYPE_SEI 6    /*****曾强帧******/       
    7. #define NALU_TYPE_SPS 7  
    8. #define NALU_TYPE_PPS 8  
    9. #define NALU_TYPE_AUD 9  
    10. #define NALU_TYPE_EOSEQ 10  
    11. #define NALU_TYPE_EOSTREAM 11  
    12. #define NALU_TYPE_FILL 12  



    每个NALU第一个byte & 0x1f 就可以得出它的类型,比如上图第一个NALU:67 & 0x1f = 7,则此单元是SPS,第三个:68 & 0x1f = 8,则此单元是PPS。

    Part3 h264 封装flv

     我们现在开始把H264,AAC封装为FLV文件。
     首先定义一个函数(功能反向拷贝):

    [cpp] view plain copy
     
    1. void ReverseMemcpy(void* dest,size_t destLen, const void* src, size_t n)  
    2. {  
    3.         char*     d= (char*) dest;  
    4.         const char*  s= (const char*)src;  
    5.         s=s+n-1;  
    6.         while(n--&&destLen--)  
    7.         {  
    8.             *d++=*s--;  
    9.         }  
    10.         return dest;  
    11. }  



                  1.写入FLV头。
                  2.写入FLV脚本Tag;
                  3.由于分装的是H264,AAC所以所写入一个视频配置信息,和一个音频配置信息
                  3.写入视频Tag.由于是H264,Tag的数据就需要按照AVC格式封装。Tag数据区有两种,一种是视频(0x17),一种是音频(0x27)。
                     AVC格式:AVCPacketType(1字节)+CompositionTime(3字节)
                                   如果AVCPacketType=0x00,这格式为AVCPacketType(1字节)+CompositionTime(3字节)+AVCDecoderConfigurationRecord。
                                   如果AVCPacketType=0x01,这格式为AVCPacketType(1字节)+CompositionTime(3字节)+ 4个bytes的NALU单元长度 + N个bytes的NALU数据。
    AVCDecoderConfigurationRecord结构信息    

    [cpp] view plain copy
     
    1. typedef struct _AVC_DEC_CON_REC  
    2. {  
    3.     char cfgVersion;//configurationVersion  //0x01  
    4.     char avcProfile;//AVCProfileIndication  //sps[1]  
    5.     char profileCompatibility;//profile_Compatibility //sps[2]  
    6.     char avcLevel;//AVCLevelIndication //sps[3]  
    7.     //lengthSizeMinusOne:indicates the length in bytes of the NALUnitLength field in an AVC video  
    8.     char reserved6_lengthSizeMinusOne2;//  
    9.     char reserved3_numOfSPS5;//个数  
    10.     long spsLength;//sequenceParameterSetLength  
    11.     void *sps;  
    12.     char numOfPPS;//个数  
    13.     long ppsLength;  
    14.     void *pps;  
    15. }AVC_DEC_CON_REC;  
    16.    
    17. char *pH264Data=....;//h264数据。  
    18. int H264DataLen=....;//h264数据长度  
    19. FLV_TAG_HEADER tagHeader;  
    20. char AVCPacket[4]={0x00,0x00,0x00,0x00}  
    21. memset(tagHeader,0,sizeof(FLV_TAG_HEADER));  
    22. int Index=0;//分隔符长度  
    23. if(*pH264Data==0x00&&(*pH264Data+1)==0x00&&(*pH264Data+2)==0x01)  
    24. {  
    25.      Index=3;  
    26. }else if(*pH264Data==0x00&&(*pH264Data+1)==0x00&&(*pH264Data+2)==0x00&&(*pH264Data+4)==0x01)  
    27. {  
    28.      Index=4;  
    29. }else{  
    30.     Err//错误不是h264数据  
    31. }  
    32. if(*(pH264Data+Index)&0x1f==0x07)//sps帧,此h264数据还有一帧,pps。  
    33. {  
    34.      int PreTagLen=.....//前一个Tag长度  
    35.      ReverseMemcpy(&tagHeader.PreTagLen,4,&PreTagLen,4);//大端字节序;  
    36.      tagHeader.TagType=0x09;//视频类型  
    37.      //AVCPacket应全为0x00.  
    38.        
    39. }  

    part 4.音频AAC封装flv。

        AAC音频格式有ADIF和ADTS:ADIF:Audio Data Interchange Format 音频数据交换格式。这种格式的特征是可以确定的找到这个音频数据的开始,不需进行在音频数据流中间开始的解码,即它的解码必须在明确定义的开始处进行。故这种格式常用在磁盘文件中。ADTS:Audio Data Transport Stream 音频数据传输流。这种格式的特征是它是一个有同步字的比特流,解码可以在这个流中任何位置开始。它的特征类似于mp3数据流格式。简单说,ADTS可以在任意帧解码,也就是说它每一帧都有头信息。ADIF只有一个统一的头,所以必须得到所有的数据后解码。且这两种的header的格式也是不同的,目前一般编码后的和抽取出的都是ADTS格式的音频流。语音系统对实时性要求较高,基本是这样一个流程,采集音频数据,本地编码,数据上传,服务器处理,数据下发,本地解码ADTS是帧序列,本身具备流特征,在音频流的传输与处理方面更加合适。
    因此我们在音频编码时常选择ADTS的,以下是我们常用的配置
      

    [cpp] view plain copy
     
    1.  m_hAacEncoder= faacEncOpen(capability.nSamplesPerSec,capability.nChannels,  
    2. &m_nAacInputSamples, &m_nAacMaxOutputBytes);  
    3.    m_nAacnMaxInputBytes=m_nAacInputSamples*capability.wBitsPerSample/8;  
    4.    m_pAacConfig = faacEncGetCurrentConfiguration(m_hAacEncoder);//获取配置结构指针  
    5.    m_pAacConfig->inputFormat = FAAC_INPUT_16BIT;//16精度  
    6. m_pAacConfig->outputFormat=1; //   设置为 ADTS     
    7. m_pAacConfig->useTns=true;  
    8. m_pAacConfig->useLfe=false;  
    9. m_pAacConfig->aacObjectType=LOW;  
    10. m_pAacConfig->shortctl=SHORTCTL_NORMAL;  
    11. m_pAacConfig->quantqual=100;  
    12. m_pAacConfig->bandWidth=0;  
    13. m_pAacConfig->bitRate=capability.nAvgBytesPerSec;  



    对于flv的aac音频和视频一样需要在第一帧写入配置信息。
     

    [cpp] view plain copy
     
    1. flv_packet flvpacket=GetErrPacket();          
    2.  int TagDataLen=1000;  
    3.  char *pTagBuffer=(char *)::malloc(TagDataLen);  
    4.  memset(pTagBuffer,0,TagDataLen);  
    5.  KKMEDIA::FLV_TAG_HEADER Tag_Head;  
    6.  memset(&Tag_Head,0,sizeof(Tag_Head));  
    7.  FlvMemcpy(&Tag_Head.PreTagLen,4,&m_nPreTagLen,4);  
    8.  memset(&Tag_Head.Timestamp,0,3);  
    9.  Tag_Head.TagType=0x08;///音频  
    10.  int datalen=0;  
    11.  memcpy(pTagBuffer,&Tag_Head,sizeof(KKMEDIA::FLV_TAG_HEADER));  
    12.  datalen+=sizeof(KKMEDIA::FLV_TAG_HEADER);  
    13.  //前4bits表示音频格式(全部格式请看官方文档):  
    14.  //1 -- ADPCM  
    15.  //2 -- MP3  
    16.  //4 -- Nellymoser 16-kHz mono  
    17.  //5 -- Nellymoser 8-kHz mono  
    18.  //10 -- AAC  
    19.  //面两个bits表示samplerate:  
    20.  //·0 -- 5.5KHz  
    21.  //·1 -- 11kHz  
    22.  //·2 -- 22kHz  
    23.  //·3 -- 44kHz 1100=0x0C  
    24.  //下面1bit表示采样长度:  
    25.  //·0 -- snd8Bit  
    26.  //·1 -- snd16Bit  
    27.  //下面1bit表示类型:  
    28.  //·0 -- sndMomo  
    29.  //·1 -- sndStereo    
    30.  char TagAudio=0xAF; //1010,11,1,1  
    31.  //TagAudio &=0x0C;//3  
    32.  //TagAudio &=0x02;//1  
    33.  //TagAudio &=0x01;//sndStereo  
    34.  memcpy(pTagBuffer+datalen,&TagAudio,1);  
    35.  datalen++;  
    36.  char AACPacketType=0x00;//012->  
    37.  memcpy(pTagBuffer+datalen,&AACPacketType,1);  
    38.  datalen++;  
    39.  ///两个字节  
    40.  char AudioSpecificConfig[2]={0x12,0x90};///32000hz  
    41.  memcpy(pTagBuffer+datalen,&AudioSpecificConfig,2);  
    42.  datalen+=2;  
    43.  m_nPreTagLen=datalen-4;///(tag长度值)  
    44.  TagDataLen=datalen-15;//(11+4(tag长度值+tag的头)  
    45.  //Tag 数据区长度  
    46.  FlvMemcpy(pTagBuffer+5,3,&TagDataLen,3);  
    47.  flvpacket.buf =(unsigned char*)pTagBuffer;            
    48.  flvpacket.bufLen=datalen;  
    49.  flvpacket.taglen=m_nPreTagLen;  
    50.  return flvpacket;  



    关于上面的代码中AudioSpecificConfig的值是怎样计算来的,可以直接中aac编码库中获取,或者采用公式计算出来,请看一下代码。

    [cpp] view plain copy
     
    1. ///索引表  
    2. static unsigned const samplingFrequencyTable[16] = {  
    3.   96000, 88200, 64000, 48000,  
    4.   44100, 32000, 24000, 22050,  
    5.   16000, 12000, 11025, 8000,  
    6.   7350,  0,     0,      0  
    7. };  
    8. int profile=1;  
    9. int samplingFrequencyIndex=0;  
    10. for(int i=0;i<16;i++)  
    11. {  
    12.         if(samplingFrequencyTable[i]==32000)  
    13.         {  
    14.            samplingFrequencyIndex =i;  
    15.            break;  
    16.         }  
    17. }  
    18. char channelConfiguration =0x02;//(立体声)  
    19. UINT8 audioConfig[2] = {0};    
    20. UINT8 const audioObjectType = profile + 1;  ///其中profile=1;  
    21. audioConfig[0] = (audioObjectType<<3) | (samplingFrequencyIndex>>1);    
    22. audioConfig[1] = (samplingFrequencyIndex<<7) | (channelConfiguration<<3);    
    23. printf("%02x%02x", audioConfig[0], audioConfig[1]);  



       最后就写入aac帧数据了,请看以下代码:
                   

    [cpp] view plain copy
     
    1. flv_packet flvpacket=GetErrPacket();  
    2.             int TagDataLen=1000+srcLen;  
    3.             char *pTagBuffer=(char *)::malloc(TagDataLen);  
    4.             memset(pTagBuffer,0,TagDataLen);  
    5.             KKMEDIA::FLV_TAG_HEADER Tag_Head;  
    6.             memset(&Tag_Head,0,sizeof(Tag_Head));  
    7.             //FlvMemcpy等同于ReverseMemcpy  
    8.             FlvMemcpy(&Tag_Head.PreTagLen,4,&m_nPreTagLen,4);  
    9.             FlvMemcpy(&Tag_Head.Timestamp,3,&pts,3);  
    10.   
    11.   
    12.             Tag_Head.TagType=0x08;///音频  
    13.             int datalen=0;  
    14.             memcpy(pTagBuffer,&Tag_Head,sizeof(KKMEDIA::FLV_TAG_HEADER));  
    15.             datalen+=sizeof(KKMEDIA::FLV_TAG_HEADER);  
    16.             char TagAudio=0xAF;   
    17.             memcpy(pTagBuffer+datalen,&TagAudio,1);  
    18.             datalen++;  
    19.   
    20.   
    21.             char AACPacketType=0x01;  
    22.             memcpy(pTagBuffer+datalen,&AACPacketType,1);  
    23.             datalen++;  
    24.               
    25.                //src aac数据指针(不包含ADTS头长度),srcLenAAC数据长度  
    26.             memcpy(pTagBuffer+datalen,src,srcLen);  
    27.             datalen+=srcLen;  
    28.   
    29.   
    30.             m_nPreTagLen=datalen-4;///(tag长度值)  
    31.             TagDataLen=datalen-15;//(11+4(tag长度值+tag的头)  
    32.   
    33.   
    34.             //Tag 数据区长度  
    35.             FlvMemcpy(pTagBuffer+5,3,&TagDataLen,3);  
    36.             flvpacket.buf =(unsigned char*)pTagBuffer;             
    37.             flvpacket.bufLen=datalen;  
    38.             flvpacket.taglen=m_nPreTagLen;  
    39.             return flvpacket;  



    注意在使用ADTS头输出的AAC编码数据,在打包flv格式时,需要跳过其adts头长度(7字节)。
    例如:

    [cpp] view plain copy
     
      1. AudioPacket((const unsigned  char *)(pDataNALU+7),PktSize-7,Pts);     
  • 相关阅读:
    Code Forces Gym 100886J Sockets(二分)
    CSU 1092 Barricade
    CodeChef Mahesh and his lost array
    CodeChef Gcd Queries
    CodeChef GCD2
    CodeChef Sereja and LCM(矩阵快速幂)
    CodeChef Sereja and GCD
    CodeChef Little Elephant and Balance
    CodeChef Count Substrings
    hdu 4001 To Miss Our Children Time( sort + DP )
  • 原文地址:https://www.cnblogs.com/lidabo/p/9020423.html
Copyright © 2011-2022 走看看