HEVC/H265 HM10.0 分析(三)TAppDecTop.cpp .

在TAppDecTop.cpp  ,最重要的是decode 函数,下面将对其进行分析,是解码上层的一个重要函数。

代码如下,代码后将进行分析。

  1. Void TAppDecTop::decode()  
  2. {  
  3.   Int                 poc;  
  4.   TComList<TComPic*>* pcListPic = NULL;  
  5.   
  6.   ifstream bitstreamFile(m_pchBitstreamFile, ifstream::in | ifstream::binary);  
  7.   if (!bitstreamFile)  
  8.   {  
  9.     fprintf(stderr, "\nfailed to open bitstream file `%s' for reading\n", m_pchBitstreamFile);  
  10.     exit(EXIT_FAILURE);  
  11.   }  
  12.   
  13.   InputByteStream bytestream(bitstreamFile);  
  14.   
  15.   // create & initialize internal classes   
  16.   xCreateDecLib();  
  17.   xInitDecLib  ();  
  18.   m_iPOCLastDisplay += m_iSkipFrame;      // set the last displayed POC correctly for skip forward.   
  19.   
  20.   // main decoder loop   
  21.   Bool recon_opened = false// reconstruction file not yet opened. (must be performed after SPS is seen)   
  22.   
  23.   while (!!bitstreamFile)  
  24.   {  
  25.     /* location serves to work around a design fault in the decoder, whereby 
  26.      * the process of reading a new slice that is the first slice of a new frame 
  27.      * requires the TDecTop::decode() method to be called again with the same 
  28.      * nal unit. */  
  29.     streampos location = bitstreamFile.tellg();  
  30.     AnnexBStats stats = AnnexBStats();  
  31.     Bool bPreviousPictureDecoded = false;  
  32.   
  33.     vector<uint8_t> nalUnit;  
  34.     InputNALUnit nalu;  
  35.     byteStreamNALUnit(bytestream, nalUnit, stats);  
  36.   
  37.     // call actual decoding function   
  38.     Bool bNewPicture = false;  
  39.     if (nalUnit.empty())  
  40.     {  
  41.       /* this can happen if the following occur: 
  42.        *  - empty input file 
  43.        *  - two back-to-back start_code_prefixes 
  44.        *  - start_code_prefix immediately followed by EOF 
  45.        */  
  46.       fprintf(stderr, "Warning: Attempt to decode an empty NAL unit\n");  
  47.     }  
  48.     else  
  49.     {  
  50.       read(nalu, nalUnit);  
  51.       if( (m_iMaxTemporalLayer >= 0 && nalu.m_temporalId > m_iMaxTemporalLayer) || !isNaluWithinTargetDecLayerIdSet(&nalu)  )  
  52.       {  
  53.         if(bPreviousPictureDecoded)  
  54.         {  
  55.           bNewPicture = true;  
  56.           bPreviousPictureDecoded = false;  
  57.         }  
  58.         else  
  59.         {  
  60.           bNewPicture = false;  
  61.         }  
  62.       }  
  63.       else  
  64.       {  
  65.         bNewPicture = m_cTDecTop.decode(nalu, m_iSkipFrame, m_iPOCLastDisplay);  
  66.           
  67.         if (bNewPicture)  
  68.         {  
  69.           bitstreamFile.clear();  
  70.           /* location points to the current nalunit payload[1] due to the 
  71.            * need for the annexB parser to read three extra bytes. 
  72.            * [1] except for the first NAL unit in the file 
  73.            *     (but bNewPicture doesn't happen then) */  
  74.           bitstreamFile.seekg(location-streamoff(3));  
  75.           bytestream.reset();  
  76.         }  
  77.         bPreviousPictureDecoded = true;   
  78.       }  
  79.     }  
  80.     if (bNewPicture || !bitstreamFile)  
  81.     {  
  82.           
  83.       m_cTDecTop.executeLoopFilters(poc, pcListPic);  
  84.       printf("\npoc =%d\n",poc);  
  85.     }  
  86.   
  87.     if( pcListPic )  
  88.     {  
  89.         printf("\nnaluType =%d\n",nalu.m_nalUnitType);  
  90.       if ( m_pchReconFile && !recon_opened )  
  91.       {  
  92.         if (!m_outputBitDepthY) { m_outputBitDepthY = g_bitDepthY; }  
  93.         if (!m_outputBitDepthC) { m_outputBitDepthC = g_bitDepthC; }  
  94.   
  95.         m_cTVideoIOYuvReconFile.open( m_pchReconFile, true, m_outputBitDepthY, m_outputBitDepthC, g_bitDepthY, g_bitDepthC ); // write mode   
  96.         recon_opened = true;  
  97.       }  
  98.       if ( bNewPicture &&   
  99.            (   nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_IDR  
  100.             || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_IDR_N_LP  
  101.             || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_BLA_N_LP  
  102.             || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_BLANT  
  103.             || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_BLA ) )  
  104.       {  
  105.         xFlushOutput( pcListPic );  
  106.       }  
  107.       // write reconstruction to file   
  108.       if(bNewPicture)  
  109.       {  
  110.         xWriteOutput( pcListPic, nalu.m_temporalId );  
  111.       }  
  112.     }  
  113.   }  
  114.     
  115.   xFlushOutput( pcListPic );  
  116.   // delete buffers   
  117.   m_cTDecTop.deletePicBuffer();  
  118.     
  119.   // destroy internal classes   
  120.   xDestroyDecLib();  
  121. }  
Void TAppDecTop::decode()
{
  Int                 poc;
  TComList<TComPic*>* pcListPic = NULL;

  ifstream bitstreamFile(m_pchBitstreamFile, ifstream::in | ifstream::binary);
  if (!bitstreamFile)
  {
    fprintf(stderr, "\nfailed to open bitstream file `%s' for reading\n", m_pchBitstreamFile);
    exit(EXIT_FAILURE);
  }

  InputByteStream bytestream(bitstreamFile);

  // create & initialize internal classes
  xCreateDecLib();
  xInitDecLib  ();
  m_iPOCLastDisplay += m_iSkipFrame;      // set the last displayed POC correctly for skip forward.

  // main decoder loop
  Bool recon_opened = false; // reconstruction file not yet opened. (must be performed after SPS is seen)

  while (!!bitstreamFile)
  {
    /* location serves to work around a design fault in the decoder, whereby
     * the process of reading a new slice that is the first slice of a new frame
     * requires the TDecTop::decode() method to be called again with the same
     * nal unit. */
    streampos location = bitstreamFile.tellg();
    AnnexBStats stats = AnnexBStats();
    Bool bPreviousPictureDecoded = false;

    vector<uint8_t> nalUnit;
    InputNALUnit nalu;
    byteStreamNALUnit(bytestream, nalUnit, stats);

    // call actual decoding function
    Bool bNewPicture = false;
    if (nalUnit.empty())
    {
      /* this can happen if the following occur:
       *  - empty input file
       *  - two back-to-back start_code_prefixes
       *  - start_code_prefix immediately followed by EOF
       */
      fprintf(stderr, "Warning: Attempt to decode an empty NAL unit\n");
    }
    else
    {
      read(nalu, nalUnit);
      if( (m_iMaxTemporalLayer >= 0 && nalu.m_temporalId > m_iMaxTemporalLayer) || !isNaluWithinTargetDecLayerIdSet(&nalu)  )
      {
        if(bPreviousPictureDecoded)
        {
          bNewPicture = true;
          bPreviousPictureDecoded = false;
        }
        else
        {
          bNewPicture = false;
        }
      }
      else
      {
        bNewPicture = m_cTDecTop.decode(nalu, m_iSkipFrame, m_iPOCLastDisplay);
		
        if (bNewPicture)
        {
          bitstreamFile.clear();
          /* location points to the current nalunit payload[1] due to the
           * need for the annexB parser to read three extra bytes.
           * [1] except for the first NAL unit in the file
           *     (but bNewPicture doesn't happen then) */
          bitstreamFile.seekg(location-streamoff(3));
          bytestream.reset();
        }
        bPreviousPictureDecoded = true; 
      }
    }
    if (bNewPicture || !bitstreamFile)
    {
		
      m_cTDecTop.executeLoopFilters(poc, pcListPic);
	  printf("\npoc =%d\n",poc);
    }

    if( pcListPic )
    {
		printf("\nnaluType =%d\n",nalu.m_nalUnitType);
      if ( m_pchReconFile && !recon_opened )
      {
        if (!m_outputBitDepthY) { m_outputBitDepthY = g_bitDepthY; }
        if (!m_outputBitDepthC) { m_outputBitDepthC = g_bitDepthC; }

        m_cTVideoIOYuvReconFile.open( m_pchReconFile, true, m_outputBitDepthY, m_outputBitDepthC, g_bitDepthY, g_bitDepthC ); // write mode
        recon_opened = true;
      }
      if ( bNewPicture && 
           (   nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_IDR
            || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_IDR_N_LP
            || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_BLA_N_LP
            || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_BLANT
            || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_BLA ) )
      {
        xFlushOutput( pcListPic );
      }
      // write reconstruction to file
      if(bNewPicture)
      {
        xWriteOutput( pcListPic, nalu.m_temporalId );
      }
    }
  }
  
  xFlushOutput( pcListPic );
  // delete buffers
  m_cTDecTop.deletePicBuffer();
  
  // destroy internal classes
  xDestroyDecLib();
}



代码xCreateDecLib 和 xInitDecLib 重要是初始化四叉树和解码需要的全局变量和申请内存。

当提供一个码流文件的文件名后,进行ifstream bitstreamFile(m_pchBitstreamFile, ifstream::in | ifstream::binary); 以二进制方式打开文件名,码流以字节方式InputByteStream bytestream(bitstreamFile);进行读操作。


我们都知道,HEVC/H265 是以NAL方式组织数据的,解析VPS,SPS,PPS,SEI,SEI_SUFFIX 后,其他的是一个个slice的NAL数据,而Deblocking & SAO Filters 等滤波是对整个picuture进行滤波操作,出现从第二帧开始,每帧的第一个slice两次进行解析,这是参考软件的一个bug或不好的地方,其实完全可以知道是否是最后一个slice,不必进行两次打开。

所以出现:

bitstreamFile.clear();

bitstreamFile.seekg(location-streamoff(3));
bytestream.reset();


大家有兴趣,可以先增加一个变量,判断是否是最后一个slice,就不需要执行上面代码了。


如下代码是从来不会执行,因为HEVC/H265没有cavlc,也就不会有slice part A ,slice part B,slice part C ,是现实的编解码告诉了设计者,slice part A ,slice part B,slice part C 没有人使用,就被抛弃了,实际的编解码从来没有实现slice part A ,slice part B,slice part C 等编解码的。

 if( (m_iMaxTemporalLayer >= 0 && nalu.m_temporalId > m_iMaxTemporalLayer) || !isNaluWithinTargetDecLayerIdSet(&nalu)  )
      {
        if(bPreviousPictureDecoded)
        {
          bNewPicture = true;
          bPreviousPictureDecoded = false;
        }
        else
        {
          bNewPicture = false;
        }
      }


解码和滤波后当然就是输出重构数据,xWriteOutput( pcListPic, nalu.m_temporalId ), 如果slice是NAL_UNIT_CODED_SLICE_IDR,NAL_UNIT_CODED_SLICE_IDR_N_LP,NAL_UNIT_CODED_SLICE_BLA_N_LP,NAL_UNIT_CODED_SLICE_BLANT,NAL_UNIT_CODED_SLICE_BLA中的一种,将解码产生的picture全部清空,没有任何参考帧,相当于一个新的sequence进行解码了。


其他的代码很简单,请自己分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值