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);     


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM