参考帧初始化函数
1、xInitRPS()函数
xInitLib()内部初始化了SPS、VPS、PPS、RPS。参考帧集合初始化函数主要是RPS的初始化函数xInitRPS();
- 代码追踪
Void TEncTop::xInitRPS(TComSPS &sps, Bool isFieldCoding)
{
//参考图像集合rps
TComReferencePictureSet* rps;
//rpsList 中包括getGOPSize() + m_extraRPSs + 1数量的RPS(参考图像集合列表)
/*
*解释一下为什么需要创建这么多的RPS!
*因为在lowdelay_p配置编码的时候,POC范围0~9之内的帧编码所需要的参考帧不能按照
*配置文件中的deltaPOC去获取,所以rpsList 中4~13是为编码帧0~9准备的参考帧集合。
*而Sps中0~3是为后续编码帧准备的集合,后续帧将根据他们的GOPID来循环在0~3中选择参考帧集 合。
*/
sps.createRPSList(getGOPSize() + m_extraRPSs + 1);
TComRPSList* rpsList = sps.getRPSList();
/*
* 13个参考帧集合的初始化工作。
* 为每一个参考帧集合初始化deltaPOC、Used列表、RefIdc等
*/
for( Int i = 0; i < getGOPSize()+m_extraRPSs; i++)
{
const GOPEntry &ge = getGOPEntry(i);
rps = rpsList->getReferencePictureSet(i);
rps->setNumberOfPictures(ge.m_numRefPics);
rps->setNumRefIdc(ge.m_numRefIdc);
Int numNeg = 0;
Int numPos = 0;
for( Int j = 0; j < ge.m_numRefPics; j++)
{
rps->setDeltaPOC(j,ge.m_referencePics[j]);
rps->setUsed(j,ge.m_usedByCurrPic[j]);
if(ge.m_referencePics[j]>0)
{
numPos++;
}
else
{
numNeg++;
}
}
rps->setNumberOfNegativePictures(numNeg);
rps->setNumberOfPositivePictures(numPos);
// handle inter RPS intialization from the config file.
/*
*这个主要配置文件中的predict这个参数。0代表不需要RPS预测、1代表需要RPS预测。
*/
rps->setInterRPSPrediction(ge.m_interRPSPrediction > 0); // not very clean, converting anything > 0 to true.
rps->setDeltaRIdxMinus1(0); // index to the Reference RPS is always the previous one.
//配置参考RPSref呢。如果RPS预测关掉,这些都没有意义。
TComReferencePictureSet* RPSRef = i>0 ? rpsList->getReferencePictureSet(i-1): NULL; // get the reference RPS
//
if (ge.m_interRPSPrediction == 2) // Automatic generation of the inter RPS idc based on the RIdx provided.
{
assert (RPSRef!=NULL);
Int deltaRPS = getGOPEntry(i-1).m_POC - ge.m_POC; // the ref POC - current POC
Int numRefDeltaPOC = RPSRef->getNumberOfPictures();
rps->setDeltaRPS(deltaRPS); // set delta RPS
rps->setNumRefIdc(numRefDeltaPOC+1); // set the numRefIdc to the number of pictures in the reference RPS + 1.
Int count=0;
for (Int j = 0; j <= numRefDeltaPOC; j++ ) // cycle through pics in reference RPS.
{
Int RefDeltaPOC = (j<numRefDeltaPOC)? RPSRef->getDeltaPOC(j): 0; // if it is the last decoded picture, set RefDeltaPOC = 0
rps->setRefIdc(j, 0);
for (Int k = 0; k < rps->getNumberOfPictures(); k++ ) // cycle through pics in current RPS.
{
if (rps->getDeltaPOC(k) == ( RefDeltaPOC + deltaRPS)) // if the current RPS has a same picture as the reference RPS.
{
rps->setRefIdc(j, (rps->getUsed(k)?1:2));
count++;
break;
}
}
}
if (count != rps->getNumberOfPictures())
{
printf("Warning: Unable fully predict all delta POCs using the reference RPS index given in the config file. Setting Inter RPS to false for this RPS.\n");
rps->setInterRPSPrediction(0);
}
}
else if (ge.m_interRPSPrediction == 1) // inter RPS idc based on the RefIdc values provided in config file.
{
assert (RPSRef!=NULL);
rps->setDeltaRPS(ge.m_deltaRPS);
rps->setNumRefIdc(ge.m_numRefIdc);
for (Int j = 0; j < ge.m_numRefIdc; j++ )
{
rps->setRefIdc(j, ge.m_refIdc[j]);
}
// the following code overwrite the deltaPOC and Used by current values read from the config file with the ones
// computed from the RefIdc. A warning is printed if they are not identical.
numNeg = 0;
numPos = 0;
TComReferencePictureSet RPSTemp; // temporary variable
for (Int j = 0; j < ge.m_numRefIdc; j++ )
{
if (ge.m_refIdc[j])
{
Int deltaPOC = ge.m_deltaRPS + ((j < RPSRef->getNumberOfPictures())? RPSRef->getDeltaPOC(j) : 0);
RPSTemp.setDeltaPOC((numNeg+numPos),deltaPOC);
RPSTemp.setUsed((numNeg+numPos),ge.m_refIdc[j]==1?1:0);
if (deltaPOC<0)
{
numNeg++;
}
else
{
numPos++;
}
}
}
if (numNeg != rps->getNumberOfNegativePictures())
{
printf("Warning: number of negative pictures in RPS is different between intra and inter RPS specified in the config file.\n");
rps->setNumberOfNegativePictures(numNeg);
rps->setNumberOfPictures(numNeg+numPos);
}
if (numPos != rps->getNumberOfPositivePictures())
{
printf("Warning: number of positive pictures in RPS is different between intra and inter RPS specified in the config file.\n");
rps->setNumberOfPositivePictures(numPos);
rps->setNumberOfPictures(numNeg+numPos);
}
RPSTemp.setNumberOfPictures(numNeg+numPos);
RPSTemp.setNumberOfNegativePictures(numNeg);
RPSTemp.sortDeltaPOC(); // sort the created delta POC before comparing
// check if Delta POC and Used are the same
// print warning if they are not.
for (Int j = 0; j < ge.m_numRefIdc; j++ )
{
if (RPSTemp.getDeltaPOC(j) != rps->getDeltaPOC(j))
{
printf("Warning: delta POC is different between intra RPS and inter RPS specified in the config file.\n");
rps->setDeltaPOC(j,RPSTemp.getDeltaPOC(j));
}
if (RPSTemp.getUsed(j) != rps->getUsed(j))
{
printf("Warning: Used by Current in RPS is different between intra and inter RPS specified in the config file.\n");
rps->setUsed(j,RPSTemp.getUsed(j));
}
}
}
}
//In case of field coding, we need to set special parameters for the first bottom field of the sequence, since it is not specified in the cfg file.
//The position = GOPSize + extraRPSs which is (a priori) unused is reserved for this field in the RPS.
if (isFieldCoding)
{
rps = rpsList->getReferencePictureSet(getGOPSize()+m_extraRPSs);
rps->setNumberOfPictures(1);
rps->setNumberOfNegativePictures(1);
rps->setNumberOfPositivePictures(0);
rps->setNumberOfLongtermPictures(0);
rps->setDeltaPOC(0,-1);
rps->setPOC(0,0);
rps->setUsed(0,true);
rps->setInterRPSPrediction(false);
rps->setDeltaRIdxMinus1(0);
rps->setDeltaRPS(0);
rps->setNumRefIdc(0);
}
}
2、selectReferencePictureSet()函数
该函数位于compressGOP中,主要设置LocalRPS
Void TEncTop::selectReferencePictureSet(TComSlice* slice, Int POCCurr, Int GOPid )
{
//GOPid等于该帧在GOP中的相对位置,如果GOP-Size=4,则GOPid为0~3
slice->setRPSidx(GOPid);
//在LDP默认配置中,为POC等于0~9的帧设置RPSidx
//extraNum范围4~13
for(Int extraNum=m_iGOPSize; extraNum<m_extraRPSs+m_iGOPSize; extraNum++)
{
if(m_uiIntraPeriod > 0 && getDecodingRefreshType() > 0)
{
Int POCIndex = POCCurr%m_uiIntraPeriod;
if(POCIndex == 0)
{
POCIndex = m_uiIntraPeriod;
}
//m_GOPList 4~`13的POC分别为0~9
if(POCIndex == m_GOPList[extraNum].m_POC)
{
slice->setRPSidx(extraNum);
}
}
else
{
if(POCCurr==m_GOPList[extraNum].m_POC)
{
slice->setRPSidx(extraNum);
}
}
}
if(POCCurr == 1 && slice->getPic()->isField())
{
slice->setRPSidx(m_iGOPSize+m_extraRPSs);
}
//根据RPSidx设置最终的rps
const TComReferencePictureSet *rps = (slice->getSPS()->getRPSList()->getReferencePictureSet(slice->getRPSidx()));
slice->setRPS(rps);
}
3、applyReferencePictureSet()
根据上个函数获得的RPS,去遍历rpclist,也就是可获得的图像,去遍历对应的参考图像是否存在,存在的话则对相应的rpcPic进行设置setUsedByCurr、
setIsLongTerm等。
/*
* 设置里面的帧是否允许被参考,是否允许被当前帧所参考
* Function for applying picture marking based on the Reference Picture Set in pReferencePictureSet.
*/
Void TComSlice::applyReferencePictureSet( TComList<TComPic*>& rcListPic, const TComReferencePictureSet *pReferencePictureSet)
{
TComPic* rpcPic;
Int i, isReference;
checkLeadingPictureRestrictions(rcListPic);
// loop through all pictures in the reference picture buffer
TComList<TComPic*>::iterator iterPic = rcListPic.begin();
while ( iterPic != rcListPic.end())
{
rpcPic = *(iterPic++);
if(!rpcPic->getSlice( 0 )->isReferenced())
{
continue;
}
//找到isReferenced=1的rpcPic
isReference = 0;
// loop through all pictures in the Reference Picture Set
// to see if the picture should be kept as reference picture
//对 rpcList中的图片进行遍历,查找参考图像集中的图像是否在列表中存在,如果存在设置rpcPic。
for(i=0;i<pReferencePictureSet->getNumberOfPositivePictures()+pReferencePictureSet->getNumberOfNegativePictures();i++)
{
if(!rpcPic->getIsLongTerm() && rpcPic->getPicSym()->getSlice(0)->getPOC() == this->getPOC() + pReferencePictureSet->getDeltaPOC(i))
{
isReference = 1;
rpcPic->setUsedByCurr(pReferencePictureSet->getUsed(i));
rpcPic->setIsLongTerm(0);
}
}
//这是对LTR进行遍历查找。
for(;i<pReferencePictureSet->getNumberOfPictures();i++)
{
if(pReferencePictureSet->getCheckLTMSBPresent(i)==true)
{
if(rpcPic->getIsLongTerm() && (rpcPic->getPicSym()->getSlice(0)->getPOC()) == pReferencePictureSet->getPOC(i))
{
isReference = 1;
rpcPic->setUsedByCurr(pReferencePictureSet->getUsed(i));
}
}
else
{
Int pocCycle = 1<<rpcPic->getPicSym()->getSlice(0)->getSPS()->getBitsForPOC();
Int curPoc = rpcPic->getPicSym()->getSlice(0)->getPOC() & (pocCycle-1);
Int refPoc = pReferencePictureSet->getPOC(i) & (pocCycle-1);
if(rpcPic->getIsLongTerm() && curPoc == refPoc)
{
isReference = 1;
rpcPic->setUsedByCurr(pReferencePictureSet->getUsed(i));
}
}
}
// mark the picture as "unused for reference" if it is not in
// the Reference Picture Set
if(rpcPic->getPicSym()->getSlice(0)->getPOC() != this->getPOC() && isReference == 0)
{
rpcPic->getSlice( 0 )->setReferenced( false );
rpcPic->setUsedByCurr(0);
rpcPic->setIsLongTerm(0);
}
//check that pictures of higher temporal layers are not used
//时域层的判定就是从这进行判断的。不满足要求就会中断。
assert(rpcPic->getSlice( 0 )->isReferenced()==0||rpcPic->getUsedByCurr()==0||rpcPic->getTLayer()<=this->getTLayer());
//check that pictures of higher or equal temporal layer are not in the RPS if the current picture is a TSA picture
if(this->getNalUnitType() == NAL_UNIT_CODED_SLICE_TSA_R || this->getNalUnitType() == NAL_UNIT_CODED_SLICE_TSA_N)
{
assert(rpcPic->getSlice( 0 )->isReferenced()==0||rpcPic->getTLayer()<this->getTLayer());
}
//check that pictures marked as temporal layer non-reference pictures are not used for reference
if(rpcPic->getPicSym()->getSlice(0)->getPOC() != this->getPOC() && rpcPic->getTLayer()==this->getTLayer())
{
assert(rpcPic->getSlice( 0 )->isReferenced()==0||rpcPic->getUsedByCurr()==0||rpcPic->getSlice( 0 )->getTemporalLayerNonReferenceFlag()==false);
}
}
}
4、arrangeLongtermPicturesInRPS()
长时参考帧的设置,略。
5、setRefPicList ( rcListPic )
设置参考图像列表(m_apcRefPicList),根据参考图像集设置list0和list1
之前列表存放的都是deltaPOC,没有一个列表存放的是参考帧的真实数据,这个函数就是把RPS里的参考帧POC,到rpcList中找到并且将帧的真实指针转存到m_apcRefPicList列表中。
Void TComSlice::setRefPicList( TComList<TComPic*>& rcListPic, Bool checkNumPocTotalCurr )
{
if ( m_eSliceType == I_SLICE)
{
::memset( m_apcRefPicList, 0, sizeof (m_apcRefPicList));
::memset( m_aiNumRefIdx, 0, sizeof ( m_aiNumRefIdx ));
if (!checkNumPocTotalCurr)
{
return;
}
}
TComPic* pcRefPic= NULL;
static const UInt MAX_NUM_NEGATIVE_PICTURES=16;
TComPic* RefPicSetStCurr0[MAX_NUM_NEGATIVE_PICTURES];
TComPic* RefPicSetStCurr1[MAX_NUM_NEGATIVE_PICTURES];
TComPic* RefPicSetLtCurr[MAX_NUM_NEGATIVE_PICTURES];
UInt NumPicStCurr0 = 0;
UInt NumPicStCurr1 = 0;
UInt NumPicLtCurr = 0;
Int i;
for(i=0; i < m_pRPS->getNumberOfNegativePictures(); i++)
{
if(m_pRPS->getUsed(i))
{
//根据相对poc取得真实的帧的指针
pcRefPic = xGetRefPic(rcListPic, getPOC()+m_pRPS->getDeltaPOC(i));
pcRefPic->setIsLongTerm(0);
pcRefPic->getPicYuvRec()->extendPicBorder();
//真实的帧的指针放入临时的list0中
RefPicSetStCurr0[NumPicStCurr0] = pcRefPic;
NumPicStCurr0++;
pcRefPic->setCheckLTMSBPresent(false);
}
}
// 这块就是遍历前向参考帧了,前向参考帧在LDP中不可用。存放进临时列表curr1
for(; i < m_pRPS->getNumberOfNegativePictures()+m_pRPS->getNumberOfPositivePictures(); i++)
{
if(m_pRPS->getUsed(i))
{
pcRefPic = xGetRefPic(rcListPic, getPOC()+m_pRPS->getDeltaPOC(i));
pcRefPic->setIsLongTerm(0);
pcRefPic->getPicYuvRec()->extendPicBorder();
RefPicSetStCurr1[NumPicStCurr1] = pcRefPic;
NumPicStCurr1++;
pcRefPic->setCheckLTMSBPresent(false);
}
}
//当然也要care一下长期参考帧,长期参考帧存放进curr中。
for(i = m_pRPS->getNumberOfNegativePictures()+m_pRPS->getNumberOfPositivePictures()+m_pRPS->getNumberOfLongtermPictures()-1; i > m_pRPS->getNumberOfNegativePictures()+m_pRPS->getNumberOfPositivePictures()-1 ; i--)
{
if(m_pRPS->getUsed(i))
{
pcRefPic = xGetLongTermRefPic(rcListPic, m_pRPS->getPOC(i), m_pRPS->getCheckLTMSBPresent(i));
pcRefPic->setIsLongTerm(1);
pcRefPic->getPicYuvRec()->extendPicBorder();
RefPicSetLtCurr[NumPicLtCurr] = pcRefPic;
NumPicLtCurr++;
}
if(pcRefPic==NULL)
{
pcRefPic = xGetLongTermRefPic(rcListPic, m_pRPS->getPOC(i), m_pRPS->getCheckLTMSBPresent(i));
}
pcRefPic->setCheckLTMSBPresent(m_pRPS->getCheckLTMSBPresent(i));
}
// ref_pic_list_init
TComPic* rpsCurrList0[MAX_NUM_REF+1];
TComPic* rpsCurrList1[MAX_NUM_REF+1];
Int numPicTotalCurr = NumPicStCurr0 + NumPicStCurr1 + NumPicLtCurr;
if (checkNumPocTotalCurr)
{
// The variable NumPocTotalCurr is derived as specified in subclause 7.4.7.2. It is a requirement of bitstream conformance that the following applies to the value of NumPocTotalCurr:
// - If the current picture is a BLA or CRA picture, the value of NumPocTotalCurr shall be equal to 0.
// - Otherwise, when the current picture contains a P or B slice, the value of NumPocTotalCurr shall not be equal to 0.
if (getRapPicFlag())
{
assert(numPicTotalCurr == 0);
}
if (m_eSliceType == I_SLICE)
{
return;
}
assert(numPicTotalCurr > 0);
// general tier and level limit:
assert(numPicTotalCurr <= 8);
}
Int cIdx = 0;
/*
* 临时前向、后向参考列表、长期参考列表放入rpsCurrList0中。
* 如果不是B帧,不需要list1
*/
for ( i=0; i<NumPicStCurr0; i++, cIdx++)
{
rpsCurrList0[cIdx] = RefPicSetStCurr0[i];
}
for ( i=0; i<NumPicStCurr1; i++, cIdx++)
{
rpsCurrList0[cIdx] = RefPicSetStCurr1[i];
}
for ( i=0; i<NumPicLtCurr; i++, cIdx++)
{
rpsCurrList0[cIdx] = RefPicSetLtCurr[i];
}
assert(cIdx == numPicTotalCurr);
if (m_eSliceType==B_SLICE)
{
cIdx = 0;
for ( i=0; i<NumPicStCurr1; i++, cIdx++)
{
rpsCurrList1[cIdx] = RefPicSetStCurr1[i];
}
for ( i=0; i<NumPicStCurr0; i++, cIdx++)
{
rpsCurrList1[cIdx] = RefPicSetStCurr0[i];
}
for ( i=0; i<NumPicLtCurr; i++, cIdx++)
{
rpsCurrList1[cIdx] = RefPicSetLtCurr[i];
}
assert(cIdx == numPicTotalCurr);
}
::memset(m_bIsUsedAsLongTerm, 0, sizeof(m_bIsUsedAsLongTerm));
// 然后把list0和list1(上面的是临时的)放到当前帧的参考列表中
for (Int rIdx = 0; rIdx < m_aiNumRefIdx[REF_PIC_LIST_0]; rIdx ++)
{
cIdx = m_RefPicListModification.getRefPicListModificationFlagL0() ? m_RefPicListModification.getRefPicSetIdxL0(rIdx) : rIdx % numPicTotalCurr;
assert(cIdx >= 0 && cIdx < numPicTotalCurr);
m_apcRefPicList[REF_PIC_LIST_0][rIdx] = rpsCurrList0[ cIdx ];
m_bIsUsedAsLongTerm[REF_PIC_LIST_0][rIdx] = ( cIdx >= NumPicStCurr0 + NumPicStCurr1 );
}
if ( m_eSliceType != B_SLICE )
{
m_aiNumRefIdx[REF_PIC_LIST_1] = 0;
::memset( m_apcRefPicList[REF_PIC_LIST_1], 0, sizeof(m_apcRefPicList[REF_PIC_LIST_1]));
}
else
{
for (Int rIdx = 0; rIdx < m_aiNumRefIdx[REF_PIC_LIST_1]; rIdx ++)
{
cIdx = m_RefPicListModification.getRefPicListModificationFlagL1() ? m_RefPicListModification.getRefPicSetIdxL1(rIdx) : rIdx % numPicTotalCurr;
assert(cIdx >= 0 && cIdx < numPicTotalCurr);
m_apcRefPicList[REF_PIC_LIST_1][rIdx] = rpsCurrList1[ cIdx ];
m_bIsUsedAsLongTerm[REF_PIC_LIST_1][rIdx] = ( cIdx >= NumPicStCurr0 + NumPicStCurr1 );
}
}
}
6、setRefPOCList()函数
调用setRefPOCList函数,设置m_aiRefPOCList,m_apcRefPicList中存放的是帧的指针,而m_aiRefPOCList中存放的是帧的poc
Void TComSlice::setRefPOCList ()
{
for (Int iDir = 0; iDir < NUM_REF_PIC_LIST_01; iDir++)
{
for (Int iNumRefIdx = 0; iNumRefIdx < m_aiNumRefIdx[iDir]; iNumRefIdx++)
{
m_aiRefPOCList[iDir][iNumRefIdx] = m_apcRefPicList[iDir][iNumRefIdx]->getPOC();
}
}
}
7、setList1IdxToList0Idx()
List0到List1的映射