一 265支持wpp,按CU单元并行编码,所以逻辑会更复杂一些,特别是对于vbv出现上/下溢。
用2个值记录当前行的状态, 一个记录当前是否已经完成filterFrame,一个记录当前行号。
void FrameEncoder::processRow(int row, int threadId) //处理一行,row 是当前处理的行, threadId是线程Id
{
int64_t startTime = x265_mdate();
if (ATOMIC_INC(&m_activeWorkerCount) == 1 && m_stallStartTime)
m_totalNoWorkerTime += x265_mdate() - m_stallStartTime;
const uint32_t realRow = m_idx_to_row[row >> 1];//存放真实的行号,偶数行
const uint32_t typeNum = m_idx_to_row[row & 1]; //奇数行存放的是,是否FIlter过的状态
if (!typeNum) //偶数行 even number line
processRowEncoder(realRow, m_tld[threadId]); //处理偶数行编码
else //奇数行 odd number line
{
m_frameFilter.processRow(realRow); //滤波处理奇数行 ,当前行进行滤波处理,处理完了,插入encode 队列?
// NOTE: Active next row
if (realRow != m_sliceBaseRow[m_rows[realRow].sliceId + 1] - 1)
enqueueRowFilter(m_row_to_idx[realRow + 1]); //对下一行滤波,并插入到队列
}
if (ATOMIC_DEC(&m_activeWorkerCount) == 0)
m_stallStartTime = x265_mdate();
m_totalWorkerElapsedTime += x265_mdate() - startTime; // not thread safe, but good enough, 统计一行处理时间
}
二 当出现vbv上/下溢的时候是如何处理的
curRow.reEncode = m_top->m_rateControl->rowVbvRateControl(m_frame, row, &m_rce, qpBase, m_sliceBaseRow, sliceId);
qpBase = x265_clip3((double)m_param->rc.qpMin, (double)m_param->rc.qpMax, qpBase);
curEncData.m_rowStat[row].rowQp = qpBase;
curEncData.m_rowStat[row].rowQpScale = x265_qp2qScale(qpBase);
if (curRow.reEncode < 0) //vbv控制异常,需要重新编码
{
x265_log(m_param, X265_LOG_DEBUG, "POC %d row %d - encode restart required for VBV, to %.2f from %.2f\n",
m_frame->m_poc, row, qpBase, curEncData.m_cuStat[cuAddr].baseQp);
// prevent the WaveFront::findJob() method from providing new jobs
m_vbvResetTriggerRow[curRow.sliceId] = row; //触发重新编码的行号记录下来, 这里不需要加锁,
/*
* 外面有个条件 rowInSlice == col 它的方向是这样的, *
*
*
*
*
而编码的方向是 *
*
*
*
*
上面这两永远只有一个交叉点,所以不担心不加锁,设置这个stop会乱套,以及trigger 行
*
*
*/
m_bAllRowsStop[curRow.sliceId] = true;//所有的编码行都停止下来
for (uint32_t r = m_sliceBaseRow[sliceId + 1] - 1; r >= row; r--)
{//扫描触发重新编码行的后面那些行
CTURow& stopRow = m_rows[r];
if (r != row) //如果还没有遍历到当前这一行
{
/* if row was active (ready to be run) clear active bit and bitmap bit for this row */
stopRow.lock.acquire();
while (stopRow.active) //如果需要停止的行,还是在active状态
{
if (dequeueRow(m_row_to_idx[r] * 2)) //把编码状态从这一行的信息里面删除,
stopRow.active = false; //然后设置为false
else
{
/* we must release the row lock to allow the thread to exit
如果删除失败
*/
stopRow.lock.release();
GIVE_UP_TIME(); //释放CPU,等会再删除
stopRow.lock.acquire();
}
}
stopRow.lock.release();//释放锁
bool bRowBusy = true;
do
{
stopRow.lock.acquire();
bRowBusy = stopRow.busy;
stopRow.lock.release();
if (bRowBusy)
{
GIVE_UP_TIME();
}
}
while (bRowBusy);
}
m_outStreams[r].resetBits(); //bitstream reset
stopRow.completed = 0;//行完成状态重置
memset(&stopRow.rowStats, 0, sizeof(stopRow.rowStats));
curEncData.m_rowStat[r].numEncodedCUs = 0;
curEncData.m_rowStat[r].encodedBits = 0;
curEncData.m_rowStat[r].rowSatd = 0;
curEncData.m_rowStat[r].rowIntraSatd = 0;
curEncData.m_rowStat[r].sumQpRc = 0;
curEncData.m_rowStat[r].sumQpAq = 0;
}
m_bAllRowsStop[curRow.sliceId] = false; //所有的行都停下来了,实际上只管了后面的那些行
}
}
}
if (m_param->bEnableWavefront && curRow.completed >= 2 && !bLastRowInSlice &&
(!m_bAllRowsStop[curRow.sliceId] || intRow + 1 < m_vbvResetTriggerRow[curRow.sliceId]))
{ //之前的行还是继续编码
/* activate next row */
ScopedLock below(m_rows[row + 1].lock);
if (m_rows[row + 1].active == false &&
m_rows[row + 1].completed + 2 <= curRow.completed)
{
m_rows[row + 1].active = true;
enqueueRowEncoder(m_row_to_idx[row + 1]);
tryWakeOne(); /* wake up a sleeping thread or set the help wanted flag */
}
}
//后面的行需要停止再来
ScopedLock self(curRow.lock);
if ((m_bAllRowsStop[curRow.sliceId] && intRow > m_vbvResetTriggerRow[curRow.sliceId]) ||
(!bFirstRowInSlice && ((curRow.completed < numCols - 1) || (m_rows[row - 1].completed < numCols)) && m_rows[row - 1].completed < curRow.completed + 2))
{
curRow.active = false;
curRow.busy = false;
ATOMIC_INC(&m_countRowBlocks);
return;
}