Chinaunix首页 | 论坛 | 博客
  • 博客访问: 167588
  • 博文数量: 13
  • 博客积分: 2123
  • 博客等级: 大尉
  • 技术积分: 420
  • 用 户 组: 普通用户
  • 注册时间: 2005-07-05 20:25
文章分类

全部博文(13)

文章存档

2011年(1)

2010年(4)

2009年(5)

2008年(3)

我的朋友

分类:

2010-12-03 09:19:53

【转】H264 视频文件 帧格式 传输封装等 杂碎

rfc3984
Standards Track [Page 2] RFC 3984 RTP Payload Format for H.264 Video February 2005 1.
按照RFC3984协议实现H264视频流媒体

nalu单元 包起始 0x 00 00 00 01

H.264 NAL格式及分析器
%5Fdavy/b ... c409cc7cd92ace.html
_davy/blo ... 081312c8fc7acc.html

----------------------------------比特流信息----------------------------------------------

①NALU(Network Abstract Layer Unit):两标准中的比特流都是以NAL为单位,每个NAL单元包含一个RBSP,NALU的头信息定义了RBSP所属类型。类型一般包括序列参数集(SPS)、图像参数集(PPS)、增强信息(SEI)、条带(Slice)等,其中,SPS和PPS属于参数集,两标准采用参数集机制是为了将一些主要的序列、图像参数(解码图像尺寸、片组数、参考帧数、量化和滤波参数标记等)与其他参数分离,通过解码器先解码出来。此外,为了增强图像的清晰度,AVS-M添加了图像头(Picture head)信息。读取NALU流程中,每个NALU前有一个起始码0x000001,为防止 内部0x000001序列竞争,H.264编码器在最后一字节前插入一个新的字节——0x03,所以解码器检测到该序列时,需将0x03删掉,而AVS-M只需识别出起始码0x000001。


②读取宏块类型(mb type)和宏块编码模板(cbp):编解码图像以宏块划分,一个宏块由一个16*16亮度块和相应的一个8*8cb和一个8*8cr色度块组成。


(a) 两标准的帧内、帧间预测时宏块的划分是有区别的。H.264中,I_slice亮度块有Intra_4*4和Intra_16*16两种模式,色度块只有8*8模式;P_slice宏块分为16*16、16*8、8*16、8*8、8*4、4*8、4*4共7种模式。而AVS-M中,I_slice亮度块有I_4*4和I_Direct两模式,P_slice时宏块的划分和H.264中的划分一致。


(b) 两标准的宏块cbp值计算也不相同。H.264中,Intra_16*16宏块的亮度(色度)cbp直接通过读mb type得到;非Intra_16*16宏块的亮度cbp=coded_block_pattern%16,色度cbp=coded_block_pattern/16 。其中,亮度cbp最低4位有效,每位决定对应宏块的残差系数能不能为0;色度cbp为0时,对应残差系数为0,cbp为1时,DC残差系数不为0,AC系数为0,cbp为2时,DC、AC残差系数都不为0。AVS-M中,当宏块类型不是P_skip时,直接从码流中得到cbp的索引值,并以此索引值查表得到codenum值,再以codenum查表分别得到帧内/帧间cbp。此cbp为6位,每位代表宏块按8*8划分时能不能包含非零系数,当变换系数不为0时,需进一步读cbp_4*4中每位值来判断一个8*8块中4个4*4块的系数能不能为0。
---------------------------------------------------------------------------------------------
总的来说H264的码流的打包方式有两种,一种为annex-b byte stream format的格式,这个是绝大部分编码器的默认输出格式,就是每个帧的开头的3~4个字节是H264的start_code,0x00000001或者0x000001。
另一种是原始的NAL打包格式,就是开始的若干字节(1,2,4字节)是NAL的长度,而不是start_code,此时必须借助某个全局的数据来获得编码器的profile,level,PPS,SPS等信息才可以解码。
----------------------------------------------------------------------------
AVC vs. H.264
AVC and H.264 are synonymous. The standard is known by the full names "ISO/IEC 14496-10" and "ITU-T Recommendation H.264". In addition, a number of alternate names are used (or have been) in reference to this standard. These include:


  • MPEG-4 part 10
  • MPEG-4 AVC
  • AVC
  • MPEG-4 (in the broadcasting world MPEG4 part 2 is ignored)
  • H.264
  • JVT (Joint Video Team, nowadays rarely used referring to actual spec)
  • H.26L (early drafts went by this name)


All of the above (and those I've missed) include the Annex B byte-stream format. Unlike earlier MPEG1/2/4 and H.26x codecs, the H.264 specification proper does not define a full bit-stream syntax. It describes a number of NAL (Network Abstraction Layer) units, a sequence of which can be decoded into video frames. These NAL units have no boundary markers, and rely on some unspecified format to provide framing.

Annex B of of the document specifies one such format, which wraps NAL units in a format resembling a traditional MPEG video elementary stream, thus making it suitable for use with containers like MPEG PS/TS unable to provide the required framing. Other formats, such as ISO base media based formats, are able to properly separate the NAL units and do not need the Annex B wrapping.

The H.264 spec suffers from a deficiency. It defines several header-type NAL units (SPS and PPS) without specifying how to pack them into the single codec data field available in most containers. Fortunately, most containers seem to have adopted the packing used by the ISO format known as MP4.

 

 

1. H.264起始码
  在网络传输h264数据时,一个UDP包就是一个NALU,解码器可以很方便的检测出NAL分界和解码。但是如果编码数据存储为一个文件,原来的解码器将无法从数据流中分别出每个NAL的起始位置和终止位置,为此h.264用起始码来解决这一问题。

  H.264编码时,在每个NAL前添加起始码 0x000001,解码器在码流中检测到起始码,当前NAL结束。为了防止NAL内部出现0x000001的数据,h.264又提出'防止竞争 emulation prevention"机制,在编码完一个NAL时,如果检测出有连续两个0x00字节,就在后面插入一个0x03。当解码器在NAL内部检测到0x000003的数据,就把0x03抛弃,恢复原始数据。
   0x000000  >>>>>>  0x00000300
   0x000001  >>>>>>  0x00000301
   0x000002  >>>>>>  0x00000302
   0x000003  >>>>>>  0x00000303

附上h.264解码nalu中检测起始码的算法流程  
for(;;)
{
    if next 24 bits are 0x000001
    {
        startCodeFound = true
        break;
    }
    else
    {
        flush 8 bits  
    }
}// for(;;)
if(true == startCodeFound)
{
     //startcode found
     // Flush the start code found
     flush 24 bits  
     //Now navigate up to next start code and put the in between stuff
     // in the nal structure.
     for(;;)
     {
         get next 24 bits & check if it equals to 0x000001
         if(false == (next 24 bits == 000001))
         {
             // search for pattern 0x000000
             check if next 24 bits are 0x000000
             if(false == result)
             {
                 // copy the byte into the buffer
                 copy one byte to the Nal unit              
             }
             else
             {
                 break;
             }
          }
          else
          {
              break;
          }
       }//for(;;)
   }

  2. MPEG4起始码
        MPEG4的特色是VOP,没有NALU的概念,仍使用startcode对每帧进行分界。MPEG4的起始码是0x000001. 另外MPEG4中很多起始码也很有用,比如video_object_sequence_start_code 0x000001B0 表示一个视频对象序列的开始,VO_start_code 0x000001B6 表示一个VOP的开始. 0x000001B6之后的两位,是00表示 I frame, 01 表示 P frame, 10 表示 B frame.

 

 

 

 

H.264 NAL格式及分析器程序源代码
          email:zsw_davy@hotmail.com

NAL分析器程序源代码
/*!
**************************************************************************************
* \file
*   nalchek.c
* \brief
*    NALU handling common to encoder and decoder
l
l Aug.18.2006 ***************************************************************************************
*/

#include "stdio.h"
#include "stdlib.h"

#include "nalucommon.h"

//#####################################
//#本程序检查一个缓冲区里面有几个NALU #
//#输出每个NALU的类型到输出文件    #
//#输入为一个带有标记/NALU+标记/的文件#
//#####################################



void main(void)
{
FILE *pSrc;
FILE *pRep;

  
if((pSrc=fopen("H264.rec","r+b"))==NULL)
  exit(-1);
else printf("open src file succeed\n");
if((pRep=fopen("H264.rep","w+t"))==NULL)
  exit(-1);
else printf("open report file succeed\n");
ReportNalu(pSrc,pRep);
fclose(pSrc);
fclose(pRep);
}


/*! **************************************************************************************
* \file
*    nalucommon.h.h
* \brief
*    NALU handling common to encoder and decoder
*  
*   Aug.18.2006 ***************************************************************************************
*/
#include


#ifndef _NALUCOMMON_H_
#define _NALUCOMMON_H_

typedef struct
{
int startcodeprefix_len;      //! 4 for parameter sets and first slice in picture, 3 for everything else

(suggested)
unsigned len;                 //! Length of the NAL unit (Excluding the start code, which does not belong

to the NALU)
unsigned max_size;            //! Nal Unit Buffer size
int nal_unit_type;            //! NALU_TYPE_xxxx
int nal_reference_idc;        //! NALU_PRIORITY_xxxx
int forbidden_bit;            //! should be always FALSE
unsigned char *buf;        //! conjtains the first byte followed by the EBSP
} NALU_t;

#define MAXRBSPSIZE 64000

#define NALU_TYPE_SLICE    1
#define NALU_TYPE_DPA      2
#define NALU_TYPE_DPB      3
#define NALU_TYPE_DPC      4
#define NALU_TYPE_IDR      5
#define NALU_TYPE_SEI      6
#define NALU_TYPE_SPS      7
#define NALU_TYPE_PPS      8
#define NALU_TYPE_AUD      9
#define NALU_TYPE_EOSEQ    10
#define NALU_TYPE_EOSTREAM 11
#define NALU_TYPE_FILL     12

#define NALU_PRIORITY_HIGHEST     3
#define NALU_PRIORITY_HIGH        2
#define NALU_PRIRITY_LOW          1
#define NALU_PRIORITY_DISPOSABLE  0


#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif

#ifndef BOOL
#define BOOL unsigned int
#endif



void ReportNalu(FILE* src,FILE* rep);
void ParseRecBuf(unsigned char *p,int bufsize,FILE* pReport);
int ShowBits (unsigned char buffer[],int totbitoffset,int bytecount, int numbits);
BOOL next_bits(unsigned char *p,int bufpos,int bs,int id );
static int FindStartCode (unsigned char *Buf, int bufpos,  int bufsize, int next_start_pos, BOOL fIRFD);
BOOL gETNALandPROC(unsigned char *p,int bufpos,int bufsize, BOOL fisrtfd,NALU_t *mNal,int inc_SIZE,FILE

*pReport);
#endif


//############################################################
//#       src 文件格式如下   
//#        #-#-#-#-#-B-INT32LEN-T264DST  - #-#-#-#-B-INT32LEN #
//#    1 1 1 1 1 1     4   INT32LEN                           #
//############################################################

void ReportNalu(FILE* src,FILE* rep)
{
unsigned char tempbuf[1];
unsigned char *pBuf;
int *dstlen;
    int fpos=0;
int fstart=0;
int FileSize=0;
int BufSize;
int dstpacketid=0;
unsigned int  SymState=0;
fseek(src,0,SEEK_END);
FileSize=ftell(src);
printf("the src file size is %d \n",FileSize);
fseek(src,0,SEEK_SET);
do {
  
  while (fread(tempbuf,sizeof(unsigned char),1,src))
  {
   if(tempbuf=="#") SymState++;
   else
   {
    fpos=fstart+1;
    SymState=0;
    fseek(src,fpos,SEEK_SET);
   }
   if(SymState==4)
   {fpos+=4;break;}
  }
  
  fread(tempbuf,sizeof(unsigned char),1,src);
  
  if(tempbuf=="B")
  {
   SymState++;
   fpos+=1;
  }
  else
  {
   fpos=fstart+1;
   fseek(src,fpos,SEEK_SET);
  }
  
  if(SymState==5)   
  {   
   dstpacketid++;
   fprintf(rep,"the %d dstpacket\n",dstpacketid);
   dstlen=(int*)malloc(sizeof(int));
   fread(dstlen,sizeof(unsigned char),4,src);
   BufSize=(int*)dstlen;
   pBuf=(unsigned char*)malloc(sizeof(unsigned char)*BufSize);
   fpos+=4;
   fread(pBuf,sizeof(unsigned char),BufSize,src);
   ParseRecBuf(pBuf,BufSize,rep);
   fstart=fpos;
   free(dstlen);
   free(pBuf);
  }
  
} while(fpos!=FileSize);//

}


//####################################################
//# unsigned char *p  --T264 encode的一个dst长度为len#
//# int bufsize   --buffer的大小     #
//##FILE* pReport     --文件指针,存放NAL的分析结果  #
//####################################################

void ParseRecBuf(unsigned char *p,int bufsize,FILE* pReport)
{


int bufpos=0;
int bUFSTART=0;
    struct NALU_t *mNal;
BOOL isfirst;
BOOL starcode_ex;
BOOL zero_ex;
BOOL trail_ex;
int nALSIZE=0;
int nal_id=0;
BOOL firstfd=FALSE;

while (bufpos{
  if( !next_bits(p,bufpos,bufsize, 24 )  &&
   !next_bits(p,bufpos,bufsize,32 )  &&
   next_bits( p,bufpos,bufsize,8 ))
  {bufpos+=8;isfirst=TRUE;firstfd=TRUE;}// leading_zero_8bits /* equal to 0x00 */ //f(8)
  else{ isfirst=FALSE; }
  
  if(!next_bits( p,bufpos,bufsize,24 )  &&   
   next_bits( p,bufpos,bufsize,8 )
   )  
  {   
   bufpos+=8;//zero_byte /* equal to 0x00 */ f(8)
   zero_ex=TRUE;
   if( bufpos    next_bits(p,bufpos,bufsize, 24 ) )
   {      
    bufpos+=24; // start_code_prefix_one_3bytes /* equal to 0x000001 */ f(24)
    starcode_ex=TRUE;
    mNal=(struct NALU_t* )malloc(sizeof( NALU_t));
   if(gETNALandPROC(p,bufpos,bufsize,firstfd,mNal,nALSIZE,pReport))   

  
   {  
   
    nal_id++;
       fprintf(pReport," id of the nal packet above is %d\n",nal_id);
    bufpos+=(nALSIZE>>3);
    if(( bufpos     (!next_bits(p,bufpos,bufsize, 24 )) &&  
     (!next_bits(p,bufpos,bufsize, 32 ) ) &&
     (next_bits(p,bufpos, bufsize,8 )))
    {
     bufpos+=8; trail_ex=TRUE ;/*  trailing_zero_8bitsequal to 0x00 */
    }
    else trail_ex=FALSE;
    free(mNal);
   }
   else printf("cannot get any nal units\n");
   }
   else{starcode_ex=FALSE;}      
  }
  else
  { zero_ex=FALSE; }
  
  if (!zero_ex || !starcode_ex)
   bufpos=bUFSTART+1;
  else bUFSTART=bufpos;
}
}







//############################################
//#unsigned char *p =--buffer containing NALU#
//#int bufpos--current buffer position   #
//#int bs---buffersize                   #
//#int id                                #
//#  id=24   ----0x000001----FALSE   #
//#     id=32   ----0x00000001---FALSE   #
//#     id=8 ----0x00------TRUE    #
//############################################
BOOL next_bits(unsigned char *p,int bufpos,int bs,int id )
{
BOOL rERULT;
switch(id) {
case 8:   
  if(ShowBits(p,bufpos,bs,8)==0x00) rERULT=TRUE ;
  else rERULT=FALSE;
  break;
case 24:
  if(ShowBits(p,bufpos,bs,24)!=0x000001) rERULT=FALSE;
  else rERULT=TRUE;
  break;
case 32 :
  if(ShowBits(p,bufpos,bs,32)!=0x00000001) rERULT=FALSE;
  else rERULT=TRUE;
  break;
default:
  break;
}
return rERULT;
}



//##############################################
// # \brief         #
// #  Reads bits from the bitstream buffer     #
// # \param buffer        #
// #    buffer containing VLC-coded data bits  #
// # \param totbitoffset      #
// #    bit offset from start of partition     #
// # \param bytecount                          #
// #    total bytes in bitstream               #
// # \param numbits                            #
// #    number of bits to read                 #
// #############################################
int ShowBits (unsigned char buffer[],int totbitoffset,int bytecount, int numbits)
{

register int inf;
long byteoffset;      // byte from start of buffer
int bitoffset;      // bit from start of byte

byteoffset= totbitoffset/8;
bitoffset= 7-(totbitoffset%8);

inf=0;
while (numbits)
{
  inf <<=1;
  inf |= (buffer[byteoffset] & (0x01<>bitoffset;
  numbits--;
  bitoffset--;
  if (bitoffset < 0)
  {
   byteoffset++;
   bitoffset += 8;
   if (byteoffset > bytecount)
   {
    return -1;
   }
  }
}

return inf;   // return absolute offset in bit from start of frame
}     


//#########################################################################
// # \brief              

    #
// #    returns if new start code is found at byte aligned position buf.  #
// #    new-startcode is of form N 0x00 bytes, followed by a 0x01 byte.   #
// #  \return              

    #
// #     1 if start-code is found or                      \n              #
// #     0, indicating that there is no start code                        #
// #                                                                      #
// #  \param Buf                                                          #
// #     pointer to byte-stream                                           #
// #  \param   bufpos                                             #
// #     indicates current bufpos.
//#     \bufsize
//  #   indicates   total buffer size                          #
//  #  \param      next_star_pos                                           #
//  #      indicates the next_start_code pos                               #
//  #  \param      fIRFD
//  #    is firs nal already found
// ########################################################################
static int FindStartCode (unsigned char *Buf, int bufpos,  int bufsize, int next_start_pos, BOOL fIRFD)
{
int info;
int tPOS;
int tSTART=bufpos;
BOOL sTARFOUND=FALSE;
info = 1;
while (!sTARFOUND && tSTART  for (tPOS = 0; tPOS < 3; tPOS++)
   if(Buf[tSTART+tPOS] != 0)
    info = 0;   
   if(Buf[tSTART+tPOS] != 1)
    info = 0;
   if (info==0)    tSTART++;   
   else sTARFOUND=TRUE;
}
if (fIRFD && sTARFOUND)
{  
  if(Buf[tSTART-1]==0 &&
   Buf[tSTART-2]==0)
  { info=2;//trailing zero found
  next_start_pos=tSTART-2;}
  else {info=1;
  next_start_pos=tSTART;}   
}
return info;
}




//#####################################
//# unsigned char *p      #
//# int bufpos                    #
//# NALU_t *mNal          #
//# int n_SIZE       #
//#           #
//#           #
//#####################################
BOOL gETNALandPROC(unsigned char *p,
       int bufpos,
       int bufsize,
       BOOL fisrtfd,
       NALU_t *mNal,
       int inc_SIZE,
       FILE *pReport)
{  
BOOL getNfailed=FALSE;
    int nal_BUFSIZE=0;
int next_start_point=0;
int mNal_size=0;
int b_bufpos=bufpos/8;
unsigned int finresult;
BOOL info=TRUE;
finresult=FindStartCode (p,b_bufpos,bufsize,next_start_point,fisrtfd);
if (finresult==1 || finresult==2) {   
  nal_BUFSIZE=next_start_point-b_bufpos-1;
  inc_SIZE=nal_BUFSIZE;
  mNal->buf=(unsigned char *)malloc(sizeof(unsigned char)*nal_BUFSIZE);
  mNal->len=nal_BUFSIZE;
  memcpy (mNal->buf, &p[b_bufpos+1], mNal->len);
  mNal->forbidden_bit = (mNal->buf[0]>>7) & 1;
  mNal->nal_reference_idc = (mNal->buf[0]>>5) & 3;
  mNal->nal_unit_type = (mNal->buf[0]) & 0x1f;
  fprintf(pReport,"#########################################################################\n");
  fprintf(pReport,"nal len is %d\n",mNal->len);
  fprintf(pReport,"fobidden bit is % d\n",mNal->forbidden_bit);
  fprintf(pReport,"nal_reference_idc is d%\n",mNal->nal_reference_idc);
  switch(mNal->nal_unit_type) {
  case NALU_TYPE_SLICE :
   fprintf(pReport," nal type is  * NALU_TYPE_SLICE\n");
   break;
  case NALU_TYPE_DPA:
   fprintf(pReport,"nal type is *  NALU_TYPE_DPA\n");
   break;
  case NALU_TYPE_DPB :
   fprintf(pReport,"nal type is  * NALU_TYPE_DPB\n");
   break;
  case NALU_TYPE_DPC:
   fprintf(pReport,"nal type is  * NALU_TYPE_DPC\n");
   break;
  case NALU_TYPE_IDR:
   fprintf(pReport,"nal type is  * NALU_TYPE_IDR\n");
   break;
  case NALU_TYPE_SEI:
   fprintf(pReport,"nal type is  * NALU_TYPE_SEI\n");
   break;
  case NALU_TYPE_SPS:
   fprintf(pReport,"nal type is  * NALU_TYPE_SPS\n");
   break;
  case NALU_TYPE_PPS:
   fprintf(pReport,"nal type is  * NALU_TYPE_PPS\n");
   break;
  case NALU_TYPE_AUD:
   fprintf(pReport,"nal type is  * NALU_TYPE_AUD\n");
   break;
  case NALU_TYPE_EOSEQ:
   fprintf(pReport,"nal type is  * NALU_TYPE_EOSEQ\n");
   break;
  case NALU_TYPE_EOSTREAM:
   fprintf(pReport,"nal type is  * NALU_TYPE_EOSTREAM\n");
   break;
  case NALU_TYPE_FILL  :
   fprintf(pReport,"nal type is  * NALU_TYPE_FILL\n");
   break;
  default:
   break;
  }
  fprintf(pReport,"nal start code len is %d\n",mNal->startcodeprefix_len);
  fprintf(pReport,"#########################################################################\n");
  free(mNal->buf);
  mNal->buf=NULL;
  }
else info=FALSE;
return info;
}

阅读(15204) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2011-04-26 17:56:43

很有收获,谢谢!