首页 > 代码库 > H264解码深度解析——DM8168 OMX从H264文件读取一帧数据(do chunking of h264)

H264解码深度解析——DM8168 OMX从H264文件读取一帧数据(do chunking of h264)

 

源码来源:TI - DM8168 - EZSDK - OMX - examples - decode_display

 

基本执行流程如下:

 技术分享


Decode_GetNextFrameSize(H264_ParsingCtx *pc)函数源码(加注释)如下:

 

/******************************************************************************     Decode_GetNextFrameSize Function Declaration
\*****************************************************************************/
/**
*
* @brief    Gets the size of thenext frame, It is doing chunking of h264
*           elementary bitstream and providing frames to OMX component.
*
* @param in:
*           pc: Pointer to H264_ParsingCtx structure
*
* @param Out:
*           None
*
* @return   uint32_t - Frame Size
*
*/
 
unsigned int Decode_GetNextFrameSize(H264_ParsingCtx *pc)
{
 FILE *fp = pc->fp; // pc->fp指向的是H264文件
 unsigned char *readBuf = pc->readBuf;
 H264_ChunkingCtx *ctx = &pc->ctx;
 AVChunk_Buf *inBuf = &pc->inBuf;
 AVChunk_Buf *outBuf = &pc->outBuf;
 
 unsigned char termCond = 1;
  if(pc->firstParse == 1) // pc->firstParse的初始化值就是1
   termCond = 0;
 
 while ((!feof (fp)) ||
        ((((pc->firstParse == 0) && (pc->bytes != 0))
          && (pc->bytes <= READSIZE) && (pc->tmp <=pc->bytes))))
  {
   if (pc->firstParse == 1)
    {
     pc->bytes = fread (readBuf, 1, READSIZE, fp);//将H264比特流读取到readBuf
     if (!pc->bytes)
     {
       return 0;
     }
     inBuf->ptr = readBuf;  // inBuf->ptr也指向了H264比特流数据
     pc->tmp = 0;
     pc->firstParse = 0;
    }
   else
    {
     if (pc->bytes <= pc->tmp)
     {
       pc->bytes = fread (readBuf, 1, READSIZE, fp);
       if (!pc->bytes)
       {
         return 0;
       }
       inBuf->ptr = readBuf;
       pc->tmp = 0;
     }
    }
 
   while (pc->bytes > pc->tmp)
    {
     inBuf->bufsize =
       ((pc->bytes - pc->tmp) > 184) ? 184 : (pc->bytes -pc->tmp);
//并不是必须184,测试了多个其他数值都没有问题,我也不知道184意义何在?
     inBuf->bufused = 0;
 
     while (inBuf->bufsize != inBuf->bufused)
     {
              //调用Decode_DoChunking()从码流中分出一帧
       if (AVC_SUCCESS == Decode_DoChunking (ctx, outBuf, 1, inBuf, NULL))
       {
          pc->frameNo = pc->chunkCnt++;
         pc->frameSize = outBuf->bufused;
         pc->tmp += inBuf->bufused;
         inBuf->ptr += inBuf->bufused;
         return pc->frameSize;
       }
     } /* while (inBuf->bufSize */
     pc->tmp += inBuf->bufused;
     inBuf->ptr += inBuf->bufused;
    }/* while (pc->bytes) */
  }/* while ((!feof (fp)...)) */
 
 return 0;
}

 

 

Decode_DoChunking()函数源码(加注释)如下:

 

/******************************************************************************     Decode_DoChunking Function Declaration
\*****************************************************************************/
/**
*
* @brief    Does H.264 FrameChunking.
*
* @param in:
*           c: Pointer to H264_ChunkingCtx structure
*           opBufs: Pointer to AVChunk_Buf Structure
*           numOutBufs: Number of output buffers
*           inBuf: Pointer to AVChunk_Buf Structure
*           attrs: Additional attributes
*
* @param Out:
*           None
*
* @return   AVC_Err - AVC Error Code
*
*/
 
AVC_Err Decode_DoChunking (H264_ChunkingCtx*c, AVChunk_Buf *opBufs,
                           unsigned intnumOutBufs, AVChunk_Buf *inBuf,
                          void *attrs)
{
  inti = 0, j, frame_end, sc_found, tmp, bytes;
 unsigned int w, z;
 unsigned char *inp;
 
  inp= &inBuf->ptr[inBuf->bufused];
 bytes = inBuf->bufsize - inBuf->bufused;
 
/*TI 在这里构建了一个状态机,通过改变和判定c->state 变量来跳转,共有如下5个状态:
H264_ST_LOOKING_FOR_SPS,     // Initial state at start, look forSPS 
//查找序列参数集,初始状态
H264_ST_LOOKING_FOR_ZERO_SLICE, //Looking for slice header with zero MB num
//查找带0MB num的slice头
H264_ST_INSIDE_PICTURE,      //Inside a picture, looking for nextpicure start
//处于一个图片内部,查找下一幅图的开始
H264_ST_STREAM_ERR,    //When some discontinuity wasdetected in the stream
//错误处理
H264_ST_HOLDING_SC,       //Intermediate state, when a newframe is detected
//过渡状态
*/
BACK:
  if(H264_ST_INSIDE_PICTURE == c->state)  //处于一个图像内部
  {
   tmp = i;
   sc_found = frame_end = 0;
   while (i < bytes)
    {
     z = c->workingWord << 8;
     w = c->fifthByte;
     c->workingWord = z | w;
     c->fifthByte = inp[i++];
 
     if (z == 0x100)//查找新的图像
     {
       w &= 0x1f;
       if (w == H264_NAL_ACCESS_UNIT_CODEDSLICE_CODE_FOR_NONIDR ||
           w == H264_NAL_ACCESS_UNIT_CODEDSLICE_CODE_FOR_IDR)
       {
         /* check for MB number in slice header */
         if (c->fifthByte & 0x80)
         {
           sc_found = frame_end = 1;
           break;
         }
       }                       /* if (w)*/
       if (w == H264_PPS_START_CODE || w == H264_SPS_START_CODE)
       {
         sc_found = frame_end = 1;
         break;
       }                       /* if (w)*/
     }                         /* if(z) */
   }                           /*while (i) */
    j= i - tmp;
 
   /* minus 5 to remove next header that is already copied */
   if (frame_end)
    {
     j -= 5;
    }
 
   if (j > (int32_t) (opBufs->bufsize - opBufs->bufused))
    {
     /* output buffer is full, end the frame right here */
     AVCLOG (AVC_MOD_MAIN, AVC_LVL_TRACE_ALL,("memcpy(%p,%d,%p,%d,%d)",
                                               opBufs->ptr,
                                               opBufs->bufused, inp, tmp,
                                               opBufs->bufsize -
                                               opBufs->bufused));
     memcpy (&opBufs->ptr[opBufs->bufused], &inp[tmp],
              opBufs->bufsize -opBufs->bufused);
     opBufs->bufused = opBufs->bufsize;
     c->state = H264_ST_LOOKING_FOR_ZERO_SLICE;
     frame_end = 1;
    }
   else if (j > 0)
    {
     AVCLOG (AVC_MOD_MAIN, AVC_LVL_TRACE_ALL, ("memcpy(%p,%d,%p,%d,%d)",
                                               opBufs->ptr,
                                               opBufs->bufused, inp, tmp, j));
     memcpy (&opBufs->ptr[opBufs->bufused], &inp[tmp], j);
     opBufs->bufused += j;
    }
   else
    {
     opBufs->bufused += j;
    }
 
   if (frame_end)
    {
     if (sc_found)
     {
       c->state = H264_ST_HOLDING_SC;
//转换状态到Holding,以便下次调用该dochunking函数时状态判定合理
       // printf("FrameSize = %d\n", i);
     }
     inBuf->bufused += i;
     return AVC_SUCCESS;//返回成功读取一帧的标志值
    }
  }
 
  if(H264_ST_LOOKING_FOR_ZERO_SLICE == c->state)
  {
   tmp = i;
   sc_found = 0;
   while (i < bytes)
    {
     z = c->workingWord << 8;
     w = c->fifthByte;
     c->workingWord = z | w;
     c->fifthByte = inp[i++];
 
     if (z == 0x100)
     {
       w &= 0x1f;
       if (w == H264_NAL_ACCESS_UNIT_CODEDSLICE_CODE_FOR_NONIDR ||
           w == H264_NAL_ACCESS_UNIT_CODEDSLICE_CODE_FOR_IDR)
       {
         /* check for MB number in slice header */
         if (c->fifthByte & 0x80)//判定nal头后面一个字节的最高位是否为1
         {
           sc_found = 1;
           break;
         } /* if (c) */
       } /* if (w) */
     } /* if (z) */
    }/* while (i) */
    j= i - tmp;
 
   if (j > (opBufs->bufsize - opBufs->bufused))
    {
     /* output buffer is full, discard this data, go back to looking for seq
        start code */
      opBufs->bufused = 0;
     c->state = H264_ST_LOOKING_FOR_SPS;
    }
   else if (j > 0)
    {
     AVCLOG (AVC_MOD_MAIN, AVC_LVL_TRACE_ALL,("memcpy(%p,%d,%p,%d,%d)",
                                               opBufs->ptr,
                                                opBufs->bufused,inp, tmp, j));
     memcpy (&opBufs->ptr[opBufs->bufused], &inp[tmp], j);
//将参数集比特数据copy到输出缓存区
     opBufs->bufused += j;
    }
 
   if (sc_found)
    {
     /* Set the attribute at rioWptr */
     c->state = H264_ST_INSIDE_PICTURE;//转换状态
    }
  }/* if (c->state...) */
 
  if(H264_ST_STREAM_ERR == c->state)
  {
   while (i < bytes)
    {
     z = c->workingWord << 8;
     w = c->fifthByte;
     c->workingWord = z | w;
     c->fifthByte = inp[i++];
 
      if (z == 0x100)
     {
       w &= 0x1f;
       if (w == H264_NAL_ACCESS_UNIT_CODEDSLICE_CODE_FOR_NONIDR ||
           w == H264_NAL_ACCESS_UNIT_CODEDSLICE_CODE_FOR_IDR)
       {
         /* chueck for MB number in slice header */
         if (c->fifthByte & 0x80)
         {
           c->state = H264_ST_HOLDING_SC;
           break;
         }
       }
       if (w == H264_PPS_START_CODE || w == H264_SPS_START_CODE)
       {
         c->state = H264_ST_HOLDING_SC;
         break;
       }
     } /* if (z) */
    }/* while (i) */
  }
 
  if(H264_ST_LOOKING_FOR_SPS == c->state) //初始状态,查找SPS
  {
   while (i < bytes)
    {
     z = c->workingWord << 8;
// c->workingWord初始值为0xFFFFFFFF,左移8位, z=0xFFFFFF00
     w = c->fifthByte; // c->fifthByte初始值为0xFF
     c->workingWord = z | w;
     c->fifthByte = inp[i++];//从H264码流读取一个byte
/*H264码流每一帧NALU起始码都是0x00000001
整体看以上四步,
当循环5次时,c->workingWord=0x00000001
当循环6次时,z=0x00000100, w此时为nalu头(如不了解nalu头请网上搜索)
*/
     if (z == 0x100)
     {
       // printf("%d: %08x @ %d\n",chunkCnt, c->workingWord,
       // inBuf->bufused+i-6);
 
       w &= 0x1f;
       if (w == H264_SPS_START_CODE)//判定NAL类型是否为SPS
       {
         c->state = H264_ST_HOLDING_SC;//将状态改到HOLDING_SC
         break;
       } /* if (w) */
     } /* if (z) */
    }/* while (i) */
  }
 
  if(H264_ST_HOLDING_SC == c->state)
  {
    w= c->workingWord;
   opBufs->bufused = 0;
   if (opBufs->bufsize < 5)
    {
     /* Means output buffer does not have space for 4 bytes, bad error */
      AVCLOG (AVC_MOD_MAIN,AVC_LVL_CRITICAL_ERROR, ("Bad error"));
    }
   // Copy these 5 bytes into output  将nalu起始码和nal头copy到输出缓存区
   for (j = 0; j < 4; j++, w <<= 8)
    {
     opBufs->ptr[opBufs->bufused + j] = ((w >> 24) & 0xFF);
    }
   opBufs->ptr[opBufs->bufused + j] = c->fifthByte;
   opBufs->bufused += 5;
   // Copying frame start code done, now proceed to next state 状态转换
    w= c->workingWord & 0x1f;
   if (w == H264_PPS_START_CODE || w == H264_SPS_START_CODE)
    {
     c->state = H264_ST_LOOKING_FOR_ZERO_SLICE;
    }
   else
    {
     c->state = H264_ST_INSIDE_PICTURE;
    }
 
   c->workingWord = H264_WORKING_WORD_INIT;
   c->fifthByte = 0xFF;
  }
 
  if(i < bytes)
  {
   goto BACK;
  }
 
 inBuf->bufused += i;
 return AVC_ERR_INSUFFICIENT_INPUT;
}

 

H264解码深度解析——DM8168 OMX从H264文件读取一帧数据(do chunking of h264)