视音频数据处理入门:H.264视频码流解析

=====================================================

视音频数据处理入门系列文章:

视音频数据处理入门:RGB、YUV像素数据处理

视音频数据处理入门:PCM音频采样数据处理

视音频数据处理入门:H.264视频码流解析

视音频数据处理入门:AAC音频码流解析

视音频数据处理入门:FLV封装格式解析

视音频数据处理入门:UDP-RTP协议解析

=====================================================


前两篇文章介绍的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
 

在CODE上查看代码片

派生到我的代码片

  1. /** 
  2.  * 最简单的视音频数据处理示例 
  3.  * Simplest MediaData Test 
  4.  * 
  5.  * 雷霄骅 Lei Xiaohua 
  6.  * [email protected] 
  7.  * 中国传媒大学/数字电视技术 
  8.  * Communication University of China / Digital TV Technology 
  9.  * http://blog.csdn.net/leixiaohua1020 
  10.  * 本项目包含如下几种视音频测试示例: 
  11.  *  (1)像素数据处理程序。包含RGB和YUV像素格式处理的函数。 
  12.  *  (2)音频采样数据处理程序。包含PCM音频采样格式处理的函数。 
  13.  *  (3)H.264码流分析程序。可以分离并解析NALU。 
  14.  *  (4)AAC码流分析程序。可以分离并解析ADTS帧。 
  15.  *  (5)FLV封装格式分析程序。可以将FLV中的MP3音频码流分离出来。 
  16.  *  (6)UDP-RTP协议分析程序。可以将分析UDP/RTP/MPEG-TS数据包。 
  17.  * This project contains following samples to handling multimedia data: 
  18.  *  (1) Video pixel data handling program. It contains several examples to handle RGB and YUV data. 
  19.  *  (2) Audio sample data handling program. It contains several examples to handle PCM data. 
  20.  *  (3) H.264 stream analysis program. It can parse H.264 bitstream and analysis NALU of stream. 
  21.  *  (4) AAC stream analysis program. It can parse AAC bitstream and analysis ADTS frame of stream. 
  22.  *  (5) FLV format analysis program. It can analysis FLV file and extract MP3 audio stream. 
  23.  *  (6) UDP-RTP protocol analysis program. It can analysis UDP/RTP/MPEG-TS Packet. 
  24.  */  
  25. #include <stdio.h>  
  26. #include <stdlib.h>  
  27. #include <string.h>  
  28.   
  29. typedef enum {  
  30.     NALU_TYPE_SLICE    = 1,  
  31.     NALU_TYPE_DPA      = 2,  
  32.     NALU_TYPE_DPB      = 3,248)">     NALU_TYPE_DPC      = 4,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important">     NALU_TYPE_IDR      = 5,248)">     NALU_TYPE_SEI      = 6,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important">     NALU_TYPE_SPS      = 7,248)">     NALU_TYPE_PPS      = 8,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important">     NALU_TYPE_AUD      = 9,248)">     NALU_TYPE_EOSEQ    = 10,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important">     NALU_TYPE_EOSTREAM = 11,248)">     NALU_TYPE_FILL     = 12,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> } NaluType;  
  33.   
  34. enum {  
  35.     NALU_PRIORITY_DISPOSABLE = 0,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important">     NALU_PRIRITY_LOW         = 1,248)">     NALU_PRIORITY_HIGH       = 2,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important">     NALU_PRIORITY_HIGHEST    = 3  
  36. } NaluPriority;  
  37. struct  
  38. {  
  39.     int startcodeprefix_len;      //! 4 for parameter sets and first slice in picture, 3 for everything else (suggested)  
  40.     unsigned len;                 //! Length of the NAL unit (Excluding the start code, which does not belong to the NALU)  
  41.     unsigned max_size;            //! Nal Unit Buffer size  
  42.     int forbidden_bit;            //! should be always FALSE  
  43. int nal_reference_idc;        //! NALU_PRIORITY_xxxx  
  44. int nal_unit_type;            //! NALU_TYPE_xxxx      
  45. char *buf;                    //! contains the first byte followed by the EBSP  
  46. } NALU_t;  
  47. FILE *h264bitstream = NULL;                //!< the bit stream file  
  48. int info2=0, info3=0;  
  49. static int FindStartCode2 (unsigned char *Buf){  
  50.     if(Buf[0]!=0 || Buf[1]!=0 || Buf[2] !=1) return 0; //0x000001?  
  51.     else return 1;  
  52. }  
  53. int FindStartCode3 (unsigned char *Buf){  
  54. if(Buf[0]!=0 || Buf[1]!=0 || Buf[2] !=0 || Buf[3] !=1) return 0;//0x00000001?  
  55. return 1;  
  56. }  
  57. int GetAnnexbNALU (NALU_t *nalu){  
  58. int pos = 0;  
  59. int StartCodeFound, rewind;  
  60.     unsigned char *Buf;  
  61. if ((Buf = (unsigned char*)calloc (nalu->max_size , sizeof(char))) == NULL)   
  62.         printf ("GetAnnexbNALU: Could not allocate Buf memory\n");  
  63.     nalu->startcodeprefix_len=3;  
  64. if (3 != fread (Buf, 1, 3, h264bitstream)){  
  65.         free(Buf);  
  66.         return 0;  
  67.     }  
  68.     info2 = FindStartCode2 (Buf);  
  69. if(info2 != 1) {  
  70. if(1 != fread(Buf+3,248)">             free(Buf);  
  71.                      }  
  72.         info3 = FindStartCode3 (Buf);  
  73.         if (info3 != 1){   
  74.             free(Buf);  
  75.             return -1;  
  76.         }  
  77. else {  
  78.             pos = 4;  
  79.             nalu->startcodeprefix_len = 4;  
  80. else{  
  81.         nalu->startcodeprefix_len = 3;  
  82.         pos = 3;  
  83.     StartCodeFound = 0;  
  84.     info2 = 0;  
  85.     info3 = 0;  
  86. while (!StartCodeFound){  
  87. if (feof (h264bitstream)){  
  88.             nalu->len = (pos-1)-nalu->startcodeprefix_len;  
  89.             memcpy (nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len);       
  90.             nalu->forbidden_bit = nalu->buf[0] & 0x80; //1 bit  
  91.             nalu->nal_reference_idc = nalu->buf[0] & 0x60; // 2 bit  
  92.             nalu->nal_unit_type = (nalu->buf[0]) & 0x1f;// 5 bit  
  93. return pos-1;  
  94.         Buf[pos++] = fgetc (h264bitstream);  
  95.         info3 = FindStartCode3(&Buf[pos-4]);  
  96. if(info3 != 1)  
  97.             info2 = FindStartCode2(&Buf[pos-3]);  
  98.         StartCodeFound = (info2 == 1 || info3 == 1);  
  99.     // Here, we have found another start code (and read length of startcode bytes more than we should  
  100.     // have.  Hence, go back in the file  
  101.     rewind = (info3 == 1)? -4 : -3;  
  102. if (0 != fseek (h264bitstream, rewind, SEEK_CUR)){  
  103.         free(Buf);  
  104.         printf("GetAnnexbNALU: Cannot fseek in the bit stream file");  
  105.     }  
  106. // Here the Start code, the complete NALU, and the next start code is in the Buf.    
  107. // The size of Buf is pos, pos+rewind are the number of bytes excluding the next  
  108. // start code, and (pos+rewind)-startcodeprefix_len is the size of the NALU excluding the start code  
  109.     nalu->len = (pos+rewind)-nalu->startcodeprefix_len;  
  110.     memcpy (nalu->buf, nalu->len);//  
  111.     nalu->forbidden_bit = nalu->buf[0] & 0x80;      nalu->nal_reference_idc = nalu->buf[0] & 0x60;      nalu->nal_unit_type = (nalu->buf[0]) & 0x1f;     free(Buf);  
  112. return (pos+rewind);  
  113.  * Analysis H.264 Bitstream 
  114.  * @param url    Location of input H.264 bitstream file. 
  115.  */  
  116. int simplest_h264_parser(char *url){  
  117.     NALU_t *n;  
  118. int buffersize=100000;  
  119. //FILE *myout=fopen("output_log.txt","wb+");  
  120. FILE *myout=stdout;  
  121.     h264bitstream=fopen(url, "rb+");  
  122. if (h264bitstream==NULL){  
  123.         printf("Open file error\n");  
  124. return 0;  
  125.     n = (NALU_t*)calloc (1, sizeof (NALU_t));  
  126. if (n == NULL){  
  127.         printf("Alloc NALU Error\n");  
  128.     n->max_size=buffersize;  
  129.     n->buf = (char*)calloc (buffersize,153); font-weight:bold; background-color:inherit">sizeof (char));  
  130. if (n->buf == NULL){  
  131.         free (n);  
  132.         printf ("AllocNALU: n->buf");  
  133. int data_offset=0;  
  134. int nal_num=0;  
  135.     printf("-----+-------- NALU Table ------+---------+\n");  
  136.     printf(" NUM |    POS  |    IDC |  TYPE |   LEN   |\n");  
  137.     printf("-----+---------+--------+-------+---------+\n");  
  138. while(!feof(h264bitstream))   
  139.     {  
  140.         int data_lenth;  
  141.         data_lenth=GetAnnexbNALU(n);  
  142.         char type_str[20]={0};  
  143. switch(n->nal_unit_type){  
  144. case NALU_TYPE_SLICE:sprintf(type_str,"SLICE");break;  
  145. case NALU_TYPE_DPA:sprintf(type_str,"DPA");break;  
  146. case NALU_TYPE_DPB:sprintf(type_str,"DPB");case NALU_TYPE_DPC:sprintf(type_str,"DPC");case NALU_TYPE_IDR:sprintf(type_str,"IDR");case NALU_TYPE_SEI:sprintf(type_str,"SEI");case NALU_TYPE_SPS:sprintf(type_str,"SPS");case NALU_TYPE_PPS:sprintf(type_str,"PPS");case NALU_TYPE_AUD:sprintf(type_str,"AUD");case NALU_TYPE_EOSEQ:sprintf(type_str,"EOSEQ");case NALU_TYPE_EOSTREAM:sprintf(type_str,"EOSTREAM");case NALU_TYPE_FILL:sprintf(type_str,"FILL");char idc_str[20]={0};  
  147. switch(n->nal_reference_idc>>5){  
  148. case NALU_PRIORITY_DISPOSABLE:sprintf(idc_str,"DISPOS");case NALU_PRIRITY_LOW:sprintf(idc_str,"LOW");case NALU_PRIORITY_HIGH:sprintf(idc_str,"HIGH");case NALU_PRIORITY_HIGHEST:sprintf(idc_str,"HIGHEST");         fprintf(myout,"%5d| %8d| %7s| %6s| %8d|\n",nal_num,data_offset,idc_str,type_str,n->len);  
  149.         data_offset=data_offset+data_lenth;  
  150.         nal_num++;  
  151. //Free  
  152. if (n){  
  153. if (n->buf){  
  154.             free(n->buf);  
  155.             n->buf=NULL;  
  156. }  

上文中的函数调用方法如下所示。
copy
  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) [email protected] http://blog.csdn.net/leixiaohua1020

相关文章

自1998年我国取消了福利分房的政策后,房地产市场迅速开展蓬...
文章目录获取数据查看数据结构获取数据下载数据可以直接通过...
网上商城系统MySql数据库设计
26个来源的气象数据获取代码
在进入21世纪以来,中国电信业告别了20世纪最后阶段的高速发...