視音頻數據處理入門:H.264視頻碼流解析 via 雷霄驊

視音頻數據處理入門:H.264視頻碼流解析 git

 

 

=====================================================github

視音頻數據處理入門系列文章:ide

視音頻數據處理入門:RGB、YUV像素數據處理函數

視音頻數據處理入門:PCM音頻採樣數據處理測試

視音頻數據處理入門:H.264視頻碼流解析url

視音頻數據處理入門:AAC音頻碼流解析spa

視音頻數據處理入門:FLV封裝格式解析.net

視音頻數據處理入門:UDP-RTP協議解析code

=====================================================orm

 

前兩篇文章介紹的YUV/RGB處理程序以及PCM處理程序都屬於視音頻原始數據的處理程序。從本文開始介紹視音頻碼流的處理程序。本文介紹的程序是視頻碼流處理程序。視頻碼流在視頻播放器中的位置以下所示。

本文中的程序是一個H.264碼流解析程序。該程序能夠從H.264碼流中分析獲得它的基本單元NALU,而且能夠簡單解析NALU首部的字段。經過修改該程序能夠實現不一樣的H.264碼流處理功能。

 

原理

H.264原始碼流(又稱爲「裸流」)是由一個一個的NALU組成的。他們的結構以下圖所示。

其中每一個NALU之間經過startcode(起始碼)進行分隔,起始碼分紅兩種:0x000001(3Byte)或者0x00000001(4Byte)。若是NALU對應的Slice爲一幀的開始就用0x00000001,不然就用0x000001。

H.264碼流解析的步驟就是首先從碼流中搜索0x000001和0x00000001,分離出NALU;而後再分析NALU的各個字段。本文的程序即實現了上述的兩個步驟。

 

代碼

整個程序位於simplest_h264_parser()函數中,以下所示。

[cpp] view plain copy
 
  1. /** 
  2.  * 最簡單的視音頻數據處理示例 
  3.  * Simplest MediaData Test 
  4.  * 
  5.  * 雷霄驊 Lei Xiaohua 
  6.  * leixiaohua1020@126.com 
  7.  * 中國傳媒大學/數字電視技術 
  8.  * Communication University of China / Digital TV Technology 
  9.  * http://blog.csdn.net/leixiaohua1020 
  10.  * 
  11.  * 本項目包含以下幾種視音頻測試示例: 
  12.  *  (1)像素數據處理程序。包含RGB和YUV像素格式處理的函數。 
  13.  *  (2)音頻採樣數據處理程序。包含PCM音頻採樣格式處理的函數。 
  14.  *  (3)H.264碼流分析程序。能夠分離並解析NALU。 
  15.  *  (4)AAC碼流分析程序。能夠分離並解析ADTS幀。 
  16.  *  (5)FLV封裝格式分析程序。能夠將FLV中的MP3音頻碼流分離出來。 
  17.  *  (6)UDP-RTP協議分析程序。能夠將分析UDP/RTP/MPEG-TS數據包。 
  18.  * 
  19.  * This project contains following samples to handling multimedia data: 
  20.  *  (1) Video pixel data handling program. It contains several examples to handle RGB and YUV data. 
  21.  *  (2) Audio sample data handling program. It contains several examples to handle PCM data. 
  22.  *  (3) H.264 stream analysis program. It can parse H.264 bitstream and analysis NALU of stream. 
  23.  *  (4) AAC stream analysis program. It can parse AAC bitstream and analysis ADTS frame of stream. 
  24.  *  (5) FLV format analysis program. It can analysis FLV file and extract MP3 audio stream. 
  25.  *  (6) UDP-RTP protocol analysis program. It can analysis UDP/RTP/MPEG-TS Packet. 
  26.  * 
  27.  */  
  28. #include <stdio.h>  
  29. #include <stdlib.h>  
  30. #include <string.h>  
  31.   
  32. typedef enum {  
  33.     NALU_TYPE_SLICE    = 1,  
  34.     NALU_TYPE_DPA      = 2,  
  35.     NALU_TYPE_DPB      = 3,  
  36.     NALU_TYPE_DPC      = 4,  
  37.     NALU_TYPE_IDR      = 5,  
  38.     NALU_TYPE_SEI      = 6,  
  39.     NALU_TYPE_SPS      = 7,  
  40.     NALU_TYPE_PPS      = 8,  
  41.     NALU_TYPE_AUD      = 9,  
  42.     NALU_TYPE_EOSEQ    = 10,  
  43.     NALU_TYPE_EOSTREAM = 11,  
  44.     NALU_TYPE_FILL     = 12,  
  45. } NaluType;  
  46.   
  47. typedef enum {  
  48.     NALU_PRIORITY_DISPOSABLE = 0,  
  49.     NALU_PRIRITY_LOW         = 1,  
  50.     NALU_PRIORITY_HIGH       = 2,  
  51.     NALU_PRIORITY_HIGHEST    = 3  
  52. } NaluPriority;  
  53.   
  54.   
  55. typedef struct  
  56. {  
  57.     int startcodeprefix_len;      //! 4 for parameter sets and first slice in picture, 3 for everything else (suggested)  
  58.     unsigned len;                 //! Length of the NAL unit (Excluding the start code, which does not belong to the NALU)  
  59.     unsigned max_size;            //! Nal Unit Buffer size  
  60.     int forbidden_bit;            //! should be always FALSE  
  61.     int nal_reference_idc;        //! NALU_PRIORITY_xxxx  
  62.     int nal_unit_type;            //! NALU_TYPE_xxxx      
  63.     char *buf;                    //! contains the first byte followed by the EBSP  
  64. } NALU_t;  
  65.   
  66. FILE *h264bitstream = NULL;                //!< the bit stream file  
  67.   
  68. int info2=0, info3=0;  
  69.   
  70. static int FindStartCode2 (unsigned char *Buf){  
  71.     if(Buf[0]!=0 || Buf[1]!=0 || Buf[2] !=1) return 0; //0x000001?  
  72.     else return 1;  
  73. }  
  74.   
  75. static int FindStartCode3 (unsigned char *Buf){  
  76.     if(Buf[0]!=0 || Buf[1]!=0 || Buf[2] !=0 || Buf[3] !=1) return 0;//0x00000001?  
  77.     else return 1;  
  78. }  
  79.   
  80.   
  81. int GetAnnexbNALU (NALU_t *nalu){  
  82.     int pos = 0;  
  83.     int StartCodeFound, rewind;  
  84.     unsigned char *Buf;  
  85.   
  86.     if ((Buf = (unsigned char*)calloc (nalu->max_size , sizeof(char))) == NULL)   
  87.         printf ("GetAnnexbNALU: Could not allocate Buf memory\n");  
  88.   
  89.     nalu->startcodeprefix_len=3;  
  90.   
  91.     if (3 != fread (Buf, 1, 3, h264bitstream)){  
  92.         free(Buf);  
  93.         return 0;  
  94.     }  
  95.     info2 = FindStartCode2 (Buf);  
  96.     if(info2 != 1) {  
  97.         if(1 != fread(Buf+3, 1, 1, h264bitstream)){  
  98.             free(Buf);  
  99.             return 0;  
  100.         }  
  101.         info3 = FindStartCode3 (Buf);  
  102.         if (info3 != 1){   
  103.             free(Buf);  
  104.             return -1;  
  105.         }  
  106.         else {  
  107.             pos = 4;  
  108.             nalu->startcodeprefix_len = 4;  
  109.         }  
  110.     }  
  111.     else{  
  112.         nalu->startcodeprefix_len = 3;  
  113.         pos = 3;  
  114.     }  
  115.     StartCodeFound = 0;  
  116.     info2 = 0;  
  117.     info3 = 0;  
  118.   
  119.     while (!StartCodeFound){  
  120.         if (feof (h264bitstream)){  
  121.             nalu->len = (pos-1)-nalu->startcodeprefix_len;  
  122.             memcpy (nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len);       
  123.             nalu->forbidden_bit = nalu->buf[0] & 0x80; //1 bit  
  124.             nalu->nal_reference_idc = nalu->buf[0] & 0x60; // 2 bit  
  125.             nalu->nal_unit_type = (nalu->buf[0]) & 0x1f;// 5 bit  
  126.             free(Buf);  
  127.             return pos-1;  
  128.         }  
  129.         Buf[pos++] = fgetc (h264bitstream);  
  130.         info3 = FindStartCode3(&Buf[pos-4]);  
  131.         if(info3 != 1)  
  132.             info2 = FindStartCode2(&Buf[pos-3]);  
  133.         StartCodeFound = (info2 == 1 || info3 == 1);  
  134.     }  
  135.   
  136.     // Here, we have found another start code (and read length of startcode bytes more than we should  
  137.     // have.  Hence, go back in the file  
  138.     rewind = (info3 == 1)? -4 : -3;  
  139.   
  140.     if (0 != fseek (h264bitstream, rewind, SEEK_CUR)){  
  141.         free(Buf);  
  142.         printf("GetAnnexbNALU: Cannot fseek in the bit stream file");  
  143.     }  
  144.   
  145.     // Here the Start code, the complete NALU, and the next start code is in the Buf.    
  146.     // The size of Buf is pos, pos+rewind are the number of bytes excluding the next  
  147.     // start code, and (pos+rewind)-startcodeprefix_len is the size of the NALU excluding the start code  
  148.   
  149.     nalu->len = (pos+rewind)-nalu->startcodeprefix_len;  
  150.     memcpy (nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len);//  
  151.     nalu->forbidden_bit = nalu->buf[0] & 0x80; //1 bit  
  152.     nalu->nal_reference_idc = nalu->buf[0] & 0x60; // 2 bit  
  153.     nalu->nal_unit_type = (nalu->buf[0]) & 0x1f;// 5 bit  
  154.     free(Buf);  
  155.   
  156.     return (pos+rewind);  
  157. }  
  158.   
  159. /** 
  160.  * Analysis H.264 Bitstream 
  161.  * @param url    Location of input H.264 bitstream file. 
  162.  */  
  163. int simplest_h264_parser(char *url){  
  164.   
  165.     NALU_t *n;  
  166.     int buffersize=100000;  
  167.   
  168.     //FILE *myout=fopen("output_log.txt","wb+");  
  169.     FILE *myout=stdout;  
  170.   
  171.     h264bitstream=fopen(url, "rb+");  
  172.     if (h264bitstream==NULL){  
  173.         printf("Open file error\n");  
  174.         return 0;  
  175.     }  
  176.   
  177.     n = (NALU_t*)calloc (1, sizeof (NALU_t));  
  178.     if (n == NULL){  
  179.         printf("Alloc NALU Error\n");  
  180.         return 0;  
  181.     }  
  182.   
  183.     n->max_size=buffersize;  
  184.     n->buf = (char*)calloc (buffersize, sizeof (char));  
  185.     if (n->buf == NULL){  
  186.         free (n);  
  187.         printf ("AllocNALU: n->buf");  
  188.         return 0;  
  189.     }  
  190.   
  191.     int data_offset=0;  
  192.     int nal_num=0;  
  193.     printf("-----+-------- NALU Table ------+---------+\n");  
  194.     printf(" NUM |    POS  |    IDC |  TYPE |   LEN   |\n");  
  195.     printf("-----+---------+--------+-------+---------+\n");  
  196.   
  197.     while(!feof(h264bitstream))   
  198.     {  
  199.         int data_lenth;  
  200.         data_lenth=GetAnnexbNALU(n);  
  201.   
  202.         char type_str[20]={0};  
  203.         switch(n->nal_unit_type){  
  204.             case NALU_TYPE_SLICE:sprintf(type_str,"SLICE");break;  
  205.             case NALU_TYPE_DPA:sprintf(type_str,"DPA");break;  
  206.             case NALU_TYPE_DPB:sprintf(type_str,"DPB");break;  
  207.             case NALU_TYPE_DPC:sprintf(type_str,"DPC");break;  
  208.             case NALU_TYPE_IDR:sprintf(type_str,"IDR");break;  
  209.             case NALU_TYPE_SEI:sprintf(type_str,"SEI");break;  
  210.             case NALU_TYPE_SPS:sprintf(type_str,"SPS");break;  
  211.             case NALU_TYPE_PPS:sprintf(type_str,"PPS");break;  
  212.             case NALU_TYPE_AUD:sprintf(type_str,"AUD");break;  
  213.             case NALU_TYPE_EOSEQ:sprintf(type_str,"EOSEQ");break;  
  214.             case NALU_TYPE_EOSTREAM:sprintf(type_str,"EOSTREAM");break;  
  215.             case NALU_TYPE_FILL:sprintf(type_str,"FILL");break;  
  216.         }  
  217.         char idc_str[20]={0};  
  218.         switch(n->nal_reference_idc>>5){  
  219.             case NALU_PRIORITY_DISPOSABLE:sprintf(idc_str,"DISPOS");break;  
  220.             case NALU_PRIRITY_LOW:sprintf(idc_str,"LOW");break;  
  221.             case NALU_PRIORITY_HIGH:sprintf(idc_str,"HIGH");break;  
  222.             case NALU_PRIORITY_HIGHEST:sprintf(idc_str,"HIGHEST");break;  
  223.         }  
  224.   
  225.         fprintf(myout,"%5d| %8d| %7s| %6s| %8d|\n",nal_num,data_offset,idc_str,type_str,n->len);  
  226.   
  227.         data_offset=data_offset+data_lenth;  
  228.   
  229.         nal_num++;  
  230.     }  
  231.   
  232.     //Free  
  233.     if (n){  
  234.         if (n->buf){  
  235.             free(n->buf);  
  236.             n->buf=NULL;  
  237.         }  
  238.         free (n);  
  239.     }  
  240.     return 0;  
  241. }  


上文中的函數調用方法以下所示。

[cpp] view plain copy
 
  1. simplest_h264_parser("sintel.h264");  

 

結果

本程序的輸入爲一個H.264原始碼流(裸流)的文件路徑,輸出爲該碼流的NALU統計數據,以下圖所示。

下載

 

Simplest mediadata test

 

項目主頁

SourceForge:https://sourceforge.net/projects/simplest-mediadata-test/

Github:https://github.com/leixiaohua1020/simplest_mediadata_test

開源中國:http://git.oschina.net/leixiaohua1020/simplest_mediadata_test

 

CSDN下載地址:http://download.csdn.net/detail/leixiaohua1020/9422409

 

本項目包含以下幾種視音頻數據解析示例:

 (1)像素數據處理程序。包含RGB和YUV像素格式處理的函數。
 (2)音頻採樣數據處理程序。包含PCM音頻採樣格式處理的函數。
 (3)H.264碼流分析程序。能夠分離並解析NALU。
 (4)AAC碼流分析程序。能夠分離並解析ADTS幀。
 (5)FLV封裝格式分析程序。能夠將FLV中的MP3音頻碼流分離出來。
 (6)UDP-RTP協議分析程序。能夠將分析UDP/RTP/MPEG-TS數據包。

 

雷霄驊 (Lei Xiaohua)leixiaohua1020@126.comhttp://blog.csdn.net/leixiaohua1020

相關文章
相關標籤/搜索