函数入口:Void TEncSlice::compressSlice的m_pcCuEncoder->compressCtu( pCtu );调用xCompressCU( m_ppcBestCU[0], m_ppcTempCU[0], 0 DEBUG_STRING_PASS_INTO(sDebug) );

从CTU开始以四叉树的结构划分CU,递归的每次尝试各种模式和划分方法,记录一个最佳的方案并保存参数。

  1. Void TEncCu::xCompressCU( TComDataCU*& rpcBestCU, TComDataCU*& rpcTempCU, const UInt uiDepth DEBUG_STRING_FN_DECLARE(sDebug_), PartSize eParentPartSize )
  2. {
  3. TComPic* pcPic = rpcBestCU->getPic(); // 获取当前CU的图像
  4. const TComPPS &pps=*(rpcTempCU->getSlice()->getPPS()); // 获取图像参数集
  5. const TComSPS &sps=*(rpcTempCU->getSlice()->getSPS()); // 获取序列参数集
  6.  
  7. m_ppcOrigYuv[uiDepth]->copyFromPicYuv( pcPic->getPicYuvOrg(), rpcBestCU->getCtuRsAddr(), rpcBestCU->getZorderIdxInCtu() ); // 从图像中获取原始YUV数据
  8.  
  9. Bool doNotBlockPu = true; // 快速cbf标识(cbf模式见术语表)
  10. Bool earlyDetectionSkipMode = false; //early skip早期跳出标识(early skip模式见术语表)
  11.  
  12. const UInt uiLPelX = rpcBestCU->getCUPelX(); // 最左端点x坐标
  13. const UInt uiRPelX = uiLPelX + rpcBestCU->getWidth() - ; // 最右端点x坐标
  14. const UInt uiTPelY = rpcBestCU->getCUPelY(); // 最上端点y坐标
  15. const UInt uiBPelY = uiTPelY + rpcBestCU->getHeight() - ; // 最下端点y坐标
  16. const UInt uiWidth = rpcBestCU->getWidth(); // 当前CU块宽度
  17.  
  18. Int iBaseQP = xComputeQP( rpcBestCU, uiDepth );
  19. // 传入当前CU和深度,计算对当前CU的QP;如果不是对每个CU自适应的改变QP,则直接用之前slice算出的QP
  20.  
  21. const UInt numberValidComponents = rpcBestCU->getPic()->getNumberValidComponents();
  22. // 获取成分数量,如果色度格式是CHROMA_400,数量为1,反之为3(最大)
  23.  
  24. /* 【省略代码】根据当前深度、是否使用码率控制、是否使用TQB(TransquantBypass模式,见术语表)调整QP最大和最小的范围(iMinQP-iMaxQP) */
  25.  
  26. TComSlice * pcSlice = rpcTempCU->getPic()->getSlice(rpcTempCU->getPic()->getCurrSliceIdx()); // 获取当前所在slice
  27.  
  28. const Bool bBoundary = !( uiRPelX < sps.getPicWidthInLumaSamples() && uiBPelY < sps.getPicHeightInLumaSamples() ); // 当前CU块的右边界在整个图像的最右边 或者 下边界在整个图像最下边 则为TRUE(即在边界)
  29.  
  30. if ( !bBoundary ) // 如果不在边界
  31. {
  32. for (Int iQP=iMinQP; iQP<=iMaxQP; iQP++) // 在之前确定的QP范围中枚举QP
  33. {
  34. /* 【省略代码】如果是TransquantBypass模式(这里用bIsLosslessMode布尔型标识)且如果当前枚举到最小QP,将其改为lowestQP */
  35. /* 【省略代码】如果是自适应改变QP,设置相关的对最小编码块大小取Log的值、色度QP偏移量索引*/
  36.  
  37. rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
  38. // 使用CTU四叉树子层的deltaQP初始化预测数据,根据深度设置CU的宽度和高度,对QP赋值
  39.  
  40. /* {做帧间预测, SKIP和2Nx2N} */
  41. if( rpcBestCU->getSlice()->getSliceType() != I_SLICE )
  42. {
  43. /* {2Nx2N} */
  44. if(m_pcEncCfg->getUseEarlySkipDetection()) // 使用early skip早期跳出模式
  45. {
  46. xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2Nx2N DEBUG_STRING_PASS_INTO(sDebug) ); // 尝试用普通模式进行预测
  47. rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode ); // rpcBestCU保存性能最优的预测方式下的参数,rpcTempCU是每次用于尝试划分预测的CU,每次做完后重新恢复初始化
  48. }
  49. /* {SKIP} */
  50. xCheckRDCostMerge2Nx2N( rpcBestCU, rpcTempCU DEBUG_STRING_PASS_INTO(sDebug), &earlyDetectionSkipMode ); // 尝试用Merge模式进行预测,传入早期跳出标识,如果模式为skip则修改该布尔值
  51. rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
  52.  
  53. if(!m_pcEncCfg->getUseEarlySkipDetection())
  54. {
  55. /* {2Nx2N, NxN(?讲道理,真没找到这个NxN哪里做了)} */
  56. xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2Nx2N DEBUG_STRING_PASS_INTO(sDebug) );
  57. rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
  58. if(m_pcEncCfg->getUseCbfFastMode()) // 使用快速cbf模式
  59. {
  60. doNotBlockPu = rpcBestCU->getQtRootCbf( ) != ; // 判断四叉树根节点的CBFlag如果为true,则不需要后续继续划分
  61. }
  62. }
  63. }
  64. }
  65.  
  66. if(!earlyDetectionSkipMode) // 如果之前没有设置提前跳出,继续尝试所有的划分方式
  67. {
  68. for (Int iQP=iMinQP; iQP<=iMaxQP; iQP++) // 枚举QP
  69. {
  70. /* 【省略代码】如果是TransquantBypass模式同上处理 */
  71. rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode ); // CU恢复初始化
  72.  
  73. /* {帧间预测, NxN, 2NxN, Nx2N} */
  74. if( rpcBestCU->getSlice()->getSliceType() != I_SLICE )
  75. {
  76. /* {2Nx2N, NxN} */
  77. if(!( (rpcBestCU->getWidth()==) && (rpcBestCU->getHeight()==) )) // 当前CU划分到最小(8*8)
  78. {
  79. if( uiDepth == sps.getLog2DiffMaxMinCodingBlockSize() && doNotBlockPu) // 如果当前块的深度为当前的四叉树底层 且不满足跳出快速cbf条件
  80. {
  81. xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_NxN DEBUG_STRING_PASS_INTO(sDebug) ); // 做NxN的普通预测
  82. rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
  83. }
  84. }
  85. /* {Nx2N} */
  86. if(doNotBlockPu) // 不满足跳出快速cbf条件
  87. {
  88. xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_Nx2N DEBUG_STRING_PASS_INTO(sDebug) );
  89. rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
  90. if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize() == SIZE_Nx2N ) // 如果使用快速CBF策略 且(刚刚尝试的)Nx2N是最佳的划分
  91. {
  92. doNotBlockPu = rpcBestCU->getQtRootCbf( ) != ; // 判断四叉树根节点的CBFlag如果为true,则不需要后续继续划分
  93. }
  94. }
  95. /* {2NxN} */
  96. if(doNotBlockPu) // 和上面一样,就不写了
  97. {
  98. xCheckRDCostInter ( rpcBestCU, rpcTempCU, SIZE_2NxN DEBUG_STRING_PASS_INTO(sDebug) );
  99. rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
  100. if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize() == SIZE_2NxN)
  101. {
  102. doNotBlockPu = rpcBestCU->getQtRootCbf( ) != ;
  103. }
  104. }
  105.  
  106. /* {尝试非对称分割 (SIZE_2NxnU, SIZE_2NxnD, SIZE_nLx2N, SIZE_nRx2N)} */
  107. if(sps.getUseAMP() && uiDepth < sps.getLog2DiffMaxMinCodingBlockSize() ) // 如果允许非对称分割 且 当前块的深度不是当前的四叉树底层
  108. {
  109. /* 【省略代码】如果AMP_ENC_SPEEDUP(AMP编码加速)则根据之前尝试划分的最好情况省去尝试一些AMP的划分情况,以此达到加快编码的目的
  110. 否则,则朴素的尝试所有的AMP划分方式(这种情况的代码省去了,下面只解释加速情况下的代码)*/
  111. Bool bTestAMP_Hor = false, bTestAMP_Ver = false; // 是否使用AMP横向划分,纵向划分的标识
  112. /* 【省略代码】根据之前划分的最佳模式是横切或竖切或四分等判断使用横向或者竖向的非对称划分 */
  113. /* 【省略代码】如果AMP_MRG(AMP Merge) 则增加一对bTestMergeAMP_Hor,bTestMergeAMP_Ver标识横向和纵向划分;
  114. 在AMG_MRG下,调用xCheckRDCostInter()时在最后增加一个布尔类型为真的参数,会在predInterSearch()函数中对传入的残差清零;
  115. 其他代码结构与非AMG_MRG相同,因此以下将AMG_MRG预编译判断内的部分都省去了 */
  116.  
  117. /* {做横向的非对称运动分割} */
  118. if ( bTestAMP_Hor ) // 如果可以进行横向AMP划分
  119. {
  120. /* {2NxnU} */
  121. if(doNotBlockPu) // 和之前的对称划分的普通模式一样,只是传入参数PartSize改为相应的非对称划分
  122. {
  123. xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2NxnU DEBUG_STRING_PASS_INTO(sDebug) ); rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
  124. if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize() == SIZE_2NxnU )
  125. {
  126. doNotBlockPu = rpcBestCU->getQtRootCbf( ) != ;
  127. }
  128. }
  129. /* {2NxnD} */
  130. if(doNotBlockPu)
  131. {
  132. xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2NxnD DEBUG_STRING_PASS_INTO(sDebug) );
  133. rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
  134. if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize() == SIZE_2NxnD )
  135. {
  136. doNotBlockPu = rpcBestCU->getQtRootCbf( ) != ;
  137. }
  138. }
  139. }
  140.  
  141. /* {做纵向的非对称运动分割} */
  142. if ( bTestAMP_Ver ) // 如果可以进行横向AMP划分
  143. {
  144. /* {nLx2N} */
  145. if(doNotBlockPu)
  146. {
  147. xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_nLx2N DEBUG_STRING_PASS_INTO(sDebug) );
  148. rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
  149. if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize() == SIZE_nLx2N )
  150. {
  151. doNotBlockPu = rpcBestCU->getQtRootCbf( ) != ;
  152. }
  153. }
  154. /* {nRx2N} */
  155. if(doNotBlockPu)
  156. {
  157. xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_nRx2N DEBUG_STRING_PASS_INTO(sDebug) );
  158. rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
  159. }
  160. }
  161.  
  162. } /* {结束:AMP} */
  163.  
  164. } /* {结束:帧间预测} */
  165.  
  166. /* {帧内预测} */
  167. if( (rpcBestCU->getSlice()->getSliceType() == I_SLICE) ||
  168. ( (!m_pcEncCfg->getDisableIntraPUsInInterSlices()) &&
  169. (
  170. ( rpcBestCU->getCbf( , COMPONENT_Y ) != ) ||
  171. ( (rpcBestCU->getCbf( , COMPONENT_Cb ) != ) && (numberValidComponents > COMPONENT_Cb) ) ||
  172. ( (rpcBestCU->getCbf( , COMPONENT_Cr ) != ) && (numberValidComponents > COMPONENT_Cr) )
  173. )
  174. )
  175. ) // 如果是I帧 或者 允许做帧间预测且CU已被标记CBF(预测残差为0)
  176. {
  177. xCheckRDCostIntra( rpcBestCU, rpcTempCU, SIZE_2Nx2N DEBUG_STRING_PASS_INTO(sDebug) ); // 尝试2Nx2N帧内预测
  178. rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
  179. if( uiDepth == sps.getLog2DiffMaxMinCodingBlockSize() ) // 如果当前深度为四叉树最底层
  180. {
  181. if( rpcTempCU->getWidth() > ( << sps.getQuadtreeTULog2MinSize() ) ) // 如果当前CU宽度大于最小的TU宽度
  182. {
  183. xCheckRDCostIntra( rpcBestCU, rpcTempCU, SIZE_NxN DEBUG_STRING_PASS_INTO(sDebug) ); // 尝试NxN帧内预测
  184. rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
  185. }
  186. }
  187. } /* {结束:帧内预测} */
  188.  
  189. /* {尝试PCM模式(PCM见术语表)} */
  190. if(sps.getUsePCM()
  191. && rpcTempCU->getWidth() <= (<<sps.getPCMLog2MaxSize())
  192. && rpcTempCU->getWidth() >= (<<sps.getPCMLog2MinSize()) ) // 如果允许PCM且当前CU的宽度在PCM最小到最大范围内
  193. {
  194. UInt uiRawBits = getTotalBits(rpcBestCU->getWidth(), rpcBestCU->getHeight(), rpcBestCU->getPic()->getChromaFormat(), sps.getBitDepths().recon); // 直接传递整个CU像素的码率
  195. UInt uiBestBits = rpcBestCU->getTotalBits(); // 对CU进行最佳预测编码的码率
  196. if((uiBestBits > uiRawBits) || (rpcBestCU->getTotalCost() > m_pcRdCost->calcRdCost(uiRawBits, )))
  197. { // 如果进行预测编码的码率大于传递整个CU像素的码率 或者 前者的RDO大于后者的RDO
  198. xCheckIntraPCM (rpcBestCU, rpcTempCU); // 尝试使用PCM模式
  199. rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
  200. }
  201. }
  202.  
  203. } /* {结束:枚举iQP} */
  204. }/* {结束:尝试所有划分方式} */
  205.  
  206. if( rpcBestCU->getTotalCost() != MAX_DOUBLE ) // 正在测试的配置没有超过最大字节数,进行熵编码
  207. {
  208. m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[uiDepth][CI_NEXT_BEST]);
  209. m_pcEntropyCoder->resetBits(); // 重置码率
  210. m_pcEntropyCoder->encodeSplitFlag( rpcBestCU, , uiDepth, true ); // 对分割标志进行编码
  211. rpcBestCU->getTotalBits() += m_pcEntropyCoder->getNumberOfWrittenBits(); // split bits
  212. rpcBestCU->getTotalBins() += ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded(); // 计算熵编码码率
  213. rpcBestCU->getTotalCost() = m_pcRdCost->calcRdCost( rpcBestCU->getTotalBits(), rpcBestCU->getTotalDistortion() );
  214. m_pcRDGoOnSbacCoder->store(m_pppcRDSbacCoder[uiDepth][CI_NEXT_BEST]); // 计算总的RD Cost
  215. }
  216.  
  217. } /* {结束:如果不在边界的判断} */
  218.  
  219. if( rpcBestCU->getTotalCost()!=MAX_DOUBLE && rpcBestCU->isLosslessCoded() && (rpcBestCU->getIPCMFlag() == false))
  220. {
  221. xFillPCMBuffer(rpcBestCU, m_ppcOrigYuv[uiDepth]); // 将原始YUV样本复制到PCM缓冲区
  222. }
  223.  
  224. /* 【省略代码】根据最大CUDeltaQP深度、是否使用码率控制调整QP最大和最小的范围(iMinQP-iMaxQP) */
  225.  
  226. const Bool bSubBranch = bBoundary || !( m_pcEncCfg->getUseEarlyCU() && rpcBestCU->getTotalCost()!=MAX_DOUBLE && rpcBestCU->isSkipped() ); // 是否继续划分四叉树标识(Early CU见术语表)
  227.  
  228. if( bSubBranch && uiDepth < sps.getLog2DiffMaxMinCodingBlockSize() && (!getFastDeltaQp() || uiWidth > fastDeltaQPCuMaxSize || bBoundary)) // 如果可以继续划分并且当前深度不在四叉树最底层
  229. {
  230. for (Int iQP=iMinQP; iQP<=iMaxQP; iQP++) // 枚举QP
  231. {
  232. UChar uhNextDepth = uiDepth+; // 下一层的深度
  233. TComDataCU* pcSubBestPartCU = m_ppcBestCU[uhNextDepth]; // 下一层的最好CU数组
  234. TComDataCU* pcSubTempPartCU = m_ppcTempCU[uhNextDepth]; // 下一层的临时CU数组
  235.  
  236. for ( UInt uiPartUnitIdx = ; uiPartUnitIdx < ; uiPartUnitIdx++ ) // 枚举划分四叉树的四个子块的下标
  237. {
  238. pcSubBestPartCU->initSubCU( rpcTempCU, uiPartUnitIdx, uhNextDepth, iQP ); // 清空或初始化BestCU子块的数据
  239. pcSubTempPartCU->initSubCU( rpcTempCU, uiPartUnitIdx, uhNextDepth, iQP ); // 清空或初始化TempCU子块的数据
  240.  
  241. if( ( pcSubBestPartCU->getCUPelX() < sps.getPicWidthInLumaSamples() ) && ( pcSubBestPartCU->getCUPelY() < sps.getPicHeightInLumaSamples() ) ) // 子块CU的横纵坐标位置在亮度样本图像之内(可以继续往下迭代)
  242. {
  243. if ( == uiPartUnitIdx) // 如果迭代到第一块子块(左上角)
  244. {
  245. m_pppcRDSbacCoder[uhNextDepth][CI_CURR_BEST]->load(m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST]); // 使用之前(当前深度)的缓存初始化RDO
  246. }
  247. else // 迭代其他子块
  248. {
  249. m_pppcRDSbacCoder[uhNextDepth][CI_CURR_BEST]->load(m_pppcRDSbacCoder[uhNextDepth][CI_NEXT_BEST]); // 使用现在(下一深度)的缓存初始RDO
  250. }
  251.  
  252. /* 【省略代码】如果使用AMP_ENC_SPEEDUP(与在AMP加速是同一个),在递归调用xCompressXU()时在最后增加一个PartSize参数
  253. 仅用于在加速的AMP决策时判断较优的划分方式用;总之不管使不使用AMP加速,这里都要递归调用子块的compressCU*/
  254.  
  255. xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth ); // 递归下一层的子块
  256.  
  257. rpcTempCU->copyPartFrom( pcSubBestPartCU, uiPartUnitIdx, uhNextDepth ); // 将最好的子块的数据存在当前的临时数据中
  258. xCopyYuv2Tmp( pcSubBestPartCU->getTotalNumPart()*uiPartUnitIdx, uhNextDepth ); // 复制预测图像和重建图像的YUV数据
  259. } /* {结束:可以继续往下迭代} */
  260. else
  261. {
  262. pcSubBestPartCU->copyToPic( uhNextDepth ); // 将当前预测的部分复制到图片中的CU,用于预测下一个子块
  263. rpcTempCU->copyPartFrom( pcSubBestPartCU, uiPartUnitIdx, uhNextDepth ); // 将最好的子块的数据存在当前的临时数据中
  264. }
  265. }/* {结束:枚举四叉树的子块} */
  266.  
  267. m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[uhNextDepth][CI_NEXT_BEST]); // 使用现在(下一深度)的缓存初始RDO
  268.  
  269. if( !bBoundary ) // 如果当前块不在边界,进行熵编码
  270. {
  271. m_pcEntropyCoder->resetBits(); // 重置码率
  272. m_pcEntropyCoder->encodeSplitFlag( rpcTempCU, , uiDepth, true ); // 对分割标志进行编码
  273. rpcTempCU->getTotalBits() += m_pcEntropyCoder->getNumberOfWrittenBits(); // split bits
  274. rpcTempCU->getTotalBins() += ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded(); // 计算熵编码码率
  275. }
  276. rpcTempCU->getTotalCost() = m_pcRdCost->calcRdCost( rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion() ); // 计算总的RD Cost
  277.  
  278. if( uiDepth == pps.getMaxCuDQPDepth() && pps.getUseDQP()) // 如果使用DeltaQP且当前深度到达DeltaQP最大深度
  279. {
  280. Bool hasResidual = false; // 是否有残差的标识
  281. for( UInt uiBlkIdx = ; uiBlkIdx < rpcTempCU->getTotalNumPart(); uiBlkIdx ++) // 枚举所有划分到最小的CU块
  282. {
  283. if( ( rpcTempCU->getCbf(uiBlkIdx, COMPONENT_Y)
  284. || (rpcTempCU->getCbf(uiBlkIdx, COMPONENT_Cb) && (numberValidComponents > COMPONENT_Cb))
  285. || (rpcTempCU->getCbf(uiBlkIdx, COMPONENT_Cr) && (numberValidComponents > COMPONENT_Cr)) ) )
  286. { // Cbf != 0 代表有残差
  287. hasResidual = true; // 标识有残差为true
  288. break;
  289. }
  290. }
  291.  
  292. if ( hasResidual ) // 如果有残差,进行熵编码
  293. {
  294. m_pcEntropyCoder->resetBits();
  295. m_pcEntropyCoder->encodeQP( rpcTempCU, , false );
  296. rpcTempCU->getTotalBits() += m_pcEntropyCoder->getNumberOfWrittenBits(); // dQP bits
  297. rpcTempCU->getTotalBins() += ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded();
  298. rpcTempCU->getTotalCost() = m_pcRdCost->calcRdCost( rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion() ); // 之前都写过了
  299.  
  300. Bool foundNonZeroCbf = false; // 找到非零cbf标识
  301. rpcTempCU->setQPSubCUs( rpcTempCU->getRefQP( ), , uiDepth, foundNonZeroCbf ); // 设置子块QP
  302. assert( foundNonZeroCbf );
  303. }
  304. else // 所有最小CU都没有残差
  305. {
  306. rpcTempCU->setQPSubParts( rpcTempCU->getRefQP( ), , uiDepth ); // 将子块QP设置为默认值
  307. }
  308. } /* {结束:处理DeltaQP情况} */
  309.  
  310. m_pcRDGoOnSbacCoder->store(m_pppcRDSbacCoder[uiDepth][CI_TEMP_BEST]); // 存储当前深度缓存的临时最优RD Cost
  311.  
  312. if (rpcBestCU->getTotalCost() != MAX_DOUBLE) // 正在测试的配置没有超过最大字节数
  313. {
  314. const Bool isEndOfSlice = pcSlice->getSliceMode()==FIXED_NUMBER_OF_BYTES
  315. && ((pcSlice->getSliceBits()+rpcBestCU->getTotalBits())>pcSlice->getSliceArgument()<<)
  316. && rpcBestCU->getCtuRsAddr() != pcPic->getPicSym()->getCtuTsToRsAddrMap(pcSlice->getSliceCurStartCtuTsAddr())
  317. && rpcBestCU->getCtuRsAddr() != pcPic->getPicSym()->getCtuTsToRsAddrMap(pcSlice->getSliceSegmentCurStartCtuTsAddr()); // 是否是Slice最末的标识
  318. const Bool isEndOfSliceSegment = pcSlice->getSliceSegmentMode()==FIXED_NUMBER_OF_BYTES
  319. && ((pcSlice->getSliceSegmentBits()+rpcBestCU->getTotalBits()) > pcSlice->getSliceSegmentArgument()<<)
  320. && rpcBestCU->getCtuRsAddr() != pcPic->getPicSym()->getCtuTsToRsAddrMap(pcSlice->getSliceSegmentCurStartCtuTsAddr()); // 是否是SS最末的标识
  321.  
  322. if(isEndOfSlice || isEndOfSliceSegment) //由于切片段是切片的子集,因此不需要检查切片段的切片条件
  323. {
  324. rpcBestCU->getTotalCost() = MAX_DOUBLE; // 如果是最末端,将RD Cost设置为最大字节数
  325. }
  326. }
  327.  
  328. xCheckBestMode( rpcBestCU, rpcTempCU, uiDepth DEBUG_STRING_PASS_INTO(sDebug) DEBUG_STRING_PASS_INTO(sTempDebug) DEBUG_STRING_PASS_INTO(false) ); // 对RD Cost进行比较,检查最好的方式
  329.  
  330. } /* {结束:枚举iQP} */
  331. } /* {结束:可以继续划分} */
  332.  
  333. /* {子层和递归结束返回父层的每个块都要进行以下的部分} */
  334.  
  335. rpcBestCU->copyToPic(uiDepth); // 复制最好方式的数据用于下一个块的预测
  336. xCopyYuv2Pic( rpcBestCU->getPic(), rpcBestCU->getCtuRsAddr(), rpcBestCU->getZorderIdxInCtu(), uiDepth, uiDepth ); // 复制预测图像和重建图像的YUV数据
  337.  
  338. }
  339. /**************** Comment By HazelNut ******************/

基于理解加推测写的解释,可能有错误之处,欢迎留言与讨论。



HEVC-HM16.9源码学习(1)TEncCu::xCompressCU的更多相关文章

  1. Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结

    2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...

  2. jQuery源码学习感想

    还记得去年(2015)九月份的时候,作为一个大四的学生去参加美团霸面,结果被美团技术总监教育了一番,那次问了我很多jQuery源码的知识点,以前虽然喜欢研究框架,但水平还不足够来研究jQuery源码, ...

  3. MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)

    前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...

  4. MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)

    前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...

  5. MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)

    前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...

  6. MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)

    前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎.这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之 ...

  7. 我的angularjs源码学习之旅2——依赖注入

    依赖注入起源于实现控制反转的典型框架Spring框架,用来削减计算机程序的耦合问题.简单来说,在定义方法的时候,方法所依赖的对象就被隐性的注入到该方法中,在方法中可以直接使用,而不需要在执行该函数的时 ...

  8. ddms(基于 Express 的表单管理系统)源码学习

    ddms是基于express的一个表单管理系统,今天抽时间看了下它的代码,其实算不上源码学习,只是对它其中一些小的开发技巧做一些记录,希望以后在项目开发中能够实践下. 数据层封装 模块只对外暴露mod ...

  9. leveldb源码学习系列

    楼主从2014年7月份开始学习<>,由于书籍比较抽象,为了加深思考,同时开始了Google leveldb的源码学习,主要是想学习leveldb的设计思想和Google的C++编程规范.目 ...

随机推荐

  1. VS-按F12无法跳转到函数定义,点击右键也无法跳转

    工具->选项->项目和解决方案->常规,把“在解决方案资源管理器中跟踪活动项”反选.

  2. Codeforces Round #267 (Div. 2) C. George and Job(DP)补题

    Codeforces Round #267 (Div. 2) C. George and Job题目链接请点击~ The new ITone 6 has been released recently ...

  3. [翻译]NUnit---Action Attributes(八)

    Attributes NUnit 1.0使用传统的基于继承和命名约定来识别测试.从2.0开始NUnit使用自定义特性来实现. 因为NUnit的test fixtures不是从框架类库继承,所以开发人员 ...

  4. python-----tuple用法

    有一种有序列表叫元组:tuple.tuple和list非常类似,但是tuple一旦初始化就不能修改,比如同样是列出同学的名字: >>> classmates = ('Michael' ...

  5. POJ1743 Musical Theme 最长重复子串 利用后缀数组

    POJ1743 题目意思是求不重叠的最长相同变化的子串,输出该长度 比如1 2 3 4 5 6 7 8 9 10,最长长度为5,因为子串1 2 3 4 5 和 6 7 8 9 10变化都一样的 思路: ...

  6. RDA 工模

    1.工模GUI如下图: 注意两个API接口,_APP_Update_Layer()/UpdateNodeFunctionContent() 这两个接口一个是刷新ListView,另一个刷新ListVi ...

  7. Eclipse 使用Anaconda python 解释器

    问题: ubuntu16.04 Anaconda 安装成功 Eclispe 写Python代码 无法使用 (pandas库等) 原因: Eclispe 此时的python解释器==>用的并不是A ...

  8. 【WIP】C基础语法

    创建: 2017/06/17 更新: 2017/10/14 标题加上[WIP],增加创建时间 更新: 2018/02/05 被Objective-C引用部分加上id方便链接,代码放进代码模板      ...

  9. bzoj 3401: [Usaco2009 Mar]Look Up 仰望【单调栈】

    用单调递减的栈从后往前扫一遍即可 #include<iostream> #include<cstdio> using namespace std; const int N=10 ...

  10. 基于.Net Core的API框架的搭建(3)

    5.加入缓存支持 我们希望为项目增加缓存支持,我们选择Redis做为缓存数据库. 首先,我们在Services目录增加一个缓存接口类ICacheService: using System; using ...