[x265] [PATCH 2 of 3] Implementation for Intra refresh
santhoshini at multicorewareinc.com
santhoshini at multicorewareinc.com
Thu Sep 24 05:30:01 CEST 2015
# HG changeset patch
# User Santhoshini Sekar<santhoshini at multicorewareinc.com>
# Date 1442393819 -19800
# Wed Sep 16 14:26:59 2015 +0530
# Node ID 97b62cb57bfa171e50d3fa736527634bb507cbe5
# Parent 98c0dcd5a10b8806aa1ceb775ff9342f7a7ae6c6
Implementation for Intra refresh
diff -r 98c0dcd5a10b -r 97b62cb57bfa source/encoder/analysis.cpp
--- a/source/encoder/analysis.cpp Wed Sep 09 14:52:35 2015 +0530
+++ b/source/encoder/analysis.cpp Wed Sep 16 14:26:59 2015 +0530
@@ -171,10 +171,14 @@
}
else
{
- if (!m_param->rdLevel)
+ if (m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE &&
+ ctu.m_cuPelX / g_maxCUSize >= frame.m_encData->m_pir.pirStartCol
+ && ctu.m_cuPelX / g_maxCUSize < frame.m_encData->m_pir.pirEndCol)
+ compressIntraCU(ctu, cuGeom, zOrder, qp);
+ else if (!m_param->rdLevel)
{
/* In RD Level 0/1, copy source pixels into the reconstructed block so
- * they are available for intra predictions */
+ * they are available for intra predictions */
m_modeDepth[0].fencYuv.copyToPicYuv(*m_frame->m_reconPic, ctu.m_cuAddr, 0);
compressInterCU_rd0_4(ctu, cuGeom, qp);
@@ -1458,13 +1462,23 @@
bestPred->sa8dCost = MAX_INT64;
int bestSadCand = -1;
int sizeIdx = cuGeom.log2CUSize - 2;
-
+ int safeX, maxSafeMv;
+ if (m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE)
+ {
+ safeX = m_slice->m_refFrameList[0][0]->m_encData->m_pir.pirEndCol * g_maxCUSize - 3;
+ maxSafeMv = (safeX - tempPred->cu.m_cuPelX) * 4;
+ }
for (uint32_t i = 0; i < numMergeCand; ++i)
{
if (m_bFrameParallel &&
(candMvField[i][0].mv.y >= (m_param->searchRange + 1) * 4 ||
candMvField[i][1].mv.y >= (m_param->searchRange + 1) * 4))
continue;
+ if (m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE &&
+ tempPred->cu.m_cuPelX / g_maxCUSize < m_frame->m_encData->m_pir.pirEndCol &&
+ candMvField[i][0].mv.x > maxSafeMv)
+ // skip merge candidates which reference beyond safe reference area
+ continue;
tempPred->cu.m_mvpIdx[0][0] = (uint8_t)i; // merge candidate ID is stored in L0 MVP idx
X265_CHECK(m_slice->m_sliceType == B_SLICE || !(candDir[i] & 0x10), " invalid merge for P slice\n");
@@ -1570,7 +1584,12 @@
first = *m_reuseBestMergeCand;
last = first + 1;
}
-
+ int safeX, maxSafeMv;
+ if (m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE)
+ {
+ safeX = m_slice->m_refFrameList[0][0]->m_encData->m_pir.pirEndCol * g_maxCUSize - 3;
+ maxSafeMv = (safeX - tempPred->cu.m_cuPelX) * 4;
+ }
for (uint32_t i = first; i < last; i++)
{
if (m_bFrameParallel &&
@@ -1593,7 +1612,11 @@
continue;
triedBZero = true;
}
-
+ if (m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE &&
+ tempPred->cu.m_cuPelX / g_maxCUSize < m_frame->m_encData->m_pir.pirEndCol &&
+ candMvField[i][0].mv.x > maxSafeMv)
+ // skip merge candidates which reference beyond safe reference area
+ continue;
tempPred->cu.m_mvpIdx[0][0] = (uint8_t)i; /* merge candidate ID is stored in L0 MVP idx */
tempPred->cu.m_interDir[0] = candDir[i];
tempPred->cu.m_mv[0][0] = candMvField[i][0].mv;
diff -r 98c0dcd5a10b -r 97b62cb57bfa source/encoder/api.cpp
--- a/source/encoder/api.cpp Wed Sep 09 14:52:35 2015 +0530
+++ b/source/encoder/api.cpp Wed Sep 16 14:26:59 2015 +0530
@@ -245,6 +245,16 @@
}
}
+int x265_encoder_intra_refresh(x265_encoder *enc)
+{
+ if (!enc)
+ return -1;
+
+ Encoder *encoder = static_cast<Encoder*>(enc);
+ encoder->m_param->bQueuedIntraRefresh = 1;
+ return 0;
+}
+
void x265_cleanup(void)
{
if (!g_ctuSizeConfigured)
@@ -317,6 +327,7 @@
&x265_cleanup,
sizeof(x265_frame_stats),
+ &x265_encoder_intra_refresh,
};
typedef const x265_api* (*api_get_func)(int bitDepth);
diff -r 98c0dcd5a10b -r 97b62cb57bfa source/encoder/encoder.cpp
--- a/source/encoder/encoder.cpp Wed Sep 09 14:52:35 2015 +0530
+++ b/source/encoder/encoder.cpp Wed Sep 16 14:26:59 2015 +0530
@@ -437,6 +437,46 @@
}
}
+void Encoder::calcRefreshInterval(Frame* frameEnc)
+{
+ Slice* slice = frameEnc->m_encData->m_slice;
+ uint32_t numBlocksInRow = slice->m_sps->numCuInWidth;
+ FrameData::PeriodicIR* pir = &frameEnc->m_encData->m_pir;
+ if (slice->m_sliceType == I_SLICE)
+ {
+ pir->framesSinceLastPir = 0;
+ m_param->bQueuedIntraRefresh = 0;
+ /* PIR is currently only supported with ref == 1, so any intra frame effectively refreshes
+ * the whole frame and counts as an intra refresh. */
+ pir->position = numBlocksInRow;
+ }
+ else if (slice->m_sliceType == P_SLICE)
+ {
+ Frame* ref = frameEnc->m_encData->m_slice->m_refFrameList[0][0];
+ int pocdiff = frameEnc->m_poc - ref->m_poc;
+ float increment = X265_MAX(((float)numBlocksInRow - 1) / m_param->keyframeMax, 1);
+ pir->position = ref->m_encData->m_pir.position;
+ pir->framesSinceLastPir = ref->m_encData->m_pir.framesSinceLastPir + pocdiff;
+ if (pir->framesSinceLastPir >= m_param->keyframeMax ||
+ (m_param->bQueuedIntraRefresh && pir->position + 0.5 >= numBlocksInRow))
+ {
+ pir->position = 0;
+ pir->framesSinceLastPir = 0;
+ m_param->bQueuedIntraRefresh = 0;
+ frameEnc->m_lowres.bKeyframe = 1;
+ }
+ pir->pirStartCol = (uint32_t)(pir->position + 0.5);
+ pir->position += increment * pocdiff;
+ pir->pirEndCol = (uint32_t)(pir->position + 0.5);
+ /* If our intra refresh has reached the right side of the frame, we're done. */
+ if (pir->pirEndCol >= numBlocksInRow)
+ {
+ pir->position = numBlocksInRow;
+ pir->pirEndCol = numBlocksInRow;
+ }
+ }
+}
+
/**
* Feed one new input frame into the encoder, get one frame out. If pic_in is
* NULL, a flush condition is implied and pic_in must be NULL for all subsequent
@@ -768,6 +808,8 @@
if (m_param->rc.rateControlMode != X265_RC_CQP)
m_lookahead->getEstimatedPictureCost(frameEnc);
+ if (m_param->bIntraRefresh)
+ calcRefreshInterval(frameEnc);
/* Allow FrameEncoder::compressFrame() to start in the frame encoder thread */
if (!curEncoder->startCompressFrame(frameEnc))
diff -r 98c0dcd5a10b -r 97b62cb57bfa source/encoder/encoder.h
--- a/source/encoder/encoder.h Wed Sep 09 14:52:35 2015 +0530
+++ b/source/encoder/encoder.h Wed Sep 16 14:26:59 2015 +0530
@@ -168,6 +168,8 @@
void finishFrameStats(Frame* pic, FrameEncoder *curEncoder, uint64_t bits, x265_frame_stats* frameStats);
+ void calcRefreshInterval(Frame* frameEnc);
+
protected:
void initVPS(VPS *vps);
diff -r 98c0dcd5a10b -r 97b62cb57bfa source/encoder/search.cpp
--- a/source/encoder/search.cpp Wed Sep 09 14:52:35 2015 +0530
+++ b/source/encoder/search.cpp Wed Sep 16 14:26:59 2015 +0530
@@ -2460,6 +2460,17 @@
cu.clipMv(mvmin);
cu.clipMv(mvmax);
+ if (cu.m_encData->m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE &&
+ cu.m_cuPelX / g_maxCUSize < m_frame->m_encData->m_pir.pirStartCol &&
+ m_slice->m_refFrameList[0][0]->m_encData->m_pir.pirEndCol < m_slice->m_sps->numCuInWidth)
+ {
+ int safeX, maxSafeMv;
+ safeX = m_slice->m_refFrameList[0][0]->m_encData->m_pir.pirEndCol * g_maxCUSize - 3;
+ maxSafeMv = (safeX - cu.m_cuPelX) * 4;
+ mvmax.x = X265_MIN(mvmax.x, maxSafeMv);
+ mvmin.x = X265_MIN(mvmin.x, maxSafeMv);
+ }
+
/* Clip search range to signaled maximum MV length.
* We do not support this VUI field being changed from the default */
const int maxMvLen = (1 << 15) - 1;
diff -r 98c0dcd5a10b -r 97b62cb57bfa source/encoder/slicetype.cpp
--- a/source/encoder/slicetype.cpp Wed Sep 09 14:52:35 2015 +0530
+++ b/source/encoder/slicetype.cpp Wed Sep 16 14:26:59 2015 +0530
@@ -774,6 +774,7 @@
for (uint32_t cnt = 0; cnt < scale && lowresRow < heightInLowresCu; lowresRow++, cnt++)
{
sum = 0; intraSum = 0;
+ int diff = 0;
lowresCuIdx = lowresRow * widthInLowresCu;
for (lowresCol = 0; lowresCol < widthInLowresCu; lowresCol++, lowresCuIdx++)
{
@@ -781,14 +782,18 @@
if (qp_offset)
{
lowresCuCost = (uint16_t)((lowresCuCost * x265_exp2fix8(qp_offset[lowresCuIdx]) + 128) >> 8);
- int32_t intraCuCost = curFrame->m_lowres.intraCost[lowresCuIdx];
+ int32_t intraCuCost = curFrame->m_lowres.intraCost[lowresCuIdx];
curFrame->m_lowres.intraCost[lowresCuIdx] = (intraCuCost * x265_exp2fix8(qp_offset[lowresCuIdx]) + 128) >> 8;
}
+ if (m_param->bIntraRefresh && slice->m_sliceType == X265_TYPE_P)
+ for (uint32_t x = curFrame->m_encData->m_pir.pirStartCol; x <= curFrame->m_encData->m_pir.pirEndCol; x++)
+ diff += curFrame->m_lowres.intraCost[lowresCuIdx] - lowresCuCost;
curFrame->m_lowres.lowresCostForRc[lowresCuIdx] = lowresCuCost;
sum += lowresCuCost;
intraSum += curFrame->m_lowres.intraCost[lowresCuIdx];
}
curFrame->m_encData->m_rowStat[row].satdForVbv += sum;
+ curFrame->m_encData->m_rowStat[row].satdForVbv += diff;
curFrame->m_encData->m_rowStat[row].intraSatdForVbv += intraSum;
}
}
@@ -900,8 +905,7 @@
x265_log(m_param, X265_LOG_WARNING, "B-ref at frame %d incompatible with B-pyramid and %d reference frames\n",
frm.sliceType, m_param->maxNumReferences);
}
-
- if (/* (!param->intraRefresh || frm.frameNum == 0) && */ frm.frameNum - m_lastKeyframe >= m_param->keyframeMax)
+ if ((!m_param->bIntraRefresh || frm.frameNum == 0) && frm.frameNum - m_lastKeyframe >= m_param->keyframeMax)
{
if (frm.sliceType == X265_TYPE_AUTO || frm.sliceType == X265_TYPE_I)
frm.sliceType = m_param->bOpenGOP && m_lastKeyframe >= 0 ? X265_TYPE_I : X265_TYPE_IDR;
@@ -1184,7 +1188,7 @@
frames[framecnt + 1] = NULL;
keyintLimit = m_param->keyframeMax - frames[0]->frameNum + m_lastKeyframe - 1;
- origNumFrames = numFrames = X265_MIN(framecnt, keyintLimit);
+ origNumFrames = numFrames = m_param->bIntraRefresh ? framecnt : X265_MIN(framecnt, keyintLimit);
if (bIsVbvLookahead)
numFrames = framecnt;
@@ -1384,12 +1388,12 @@
if (m_param->rc.cuTree)
cuTree(frames, X265_MIN(numFrames, m_param->keyframeMax), bKeyframe);
- // if (!param->bIntraRefresh)
- for (int j = keyintLimit + 1; j <= numFrames; j += m_param->keyframeMax)
- {
- frames[j]->sliceType = X265_TYPE_I;
- resetStart = X265_MIN(resetStart, j + 1);
- }
+ if (!m_param->bIntraRefresh)
+ for (int j = keyintLimit + 1; j <= numFrames; j += m_param->keyframeMax)
+ {
+ frames[j]->sliceType = X265_TYPE_I;
+ resetStart = X265_MIN(resetStart, j + 1);
+ }
if (bIsVbvLookahead)
vbvLookahead(frames, numFrames, bKeyframe);
@@ -1504,7 +1508,7 @@
{
if (m_param->keyframeMin == m_param->keyframeMax)
threshMin = threshMax;
- if (gopSize <= m_param->keyframeMin / 4)
+ if (gopSize <= m_param->keyframeMin / 4 || m_param->bIntraRefresh)
bias = threshMin / 4;
else if (gopSize <= m_param->keyframeMin)
bias = threshMin * gopSize / m_param->keyframeMin;
diff -r 98c0dcd5a10b -r 97b62cb57bfa source/x265.def.in
--- a/source/x265.def.in Wed Sep 09 14:52:35 2015 +0530
+++ b/source/x265.def.in Wed Sep 16 14:26:59 2015 +0530
@@ -22,3 +22,4 @@
x265_cleanup
x265_api_get_${X265_BUILD}
x265_api_query
+x265_encoder_intra_refresh
diff -r 98c0dcd5a10b -r 97b62cb57bfa source/x265.h
--- a/source/x265.h Wed Sep 09 14:52:35 2015 +0530
+++ b/source/x265.h Wed Sep 16 14:26:59 2015 +0530
@@ -708,6 +708,8 @@
* big keyframe, the keyframe is "spread" over many frames. */
int bIntraRefresh;
+ int bQueuedIntraRefresh;
+
/*== Coding Unit (CU) definitions ==*/
/* Maximum CU width and height in pixels. The size must be 64, 32, or 16.
@@ -1379,6 +1381,22 @@
* close an encoder handler */
void x265_encoder_close(x265_encoder *);
+/* x265_encoder_intra_refresh:
+ * If an intra refresh is not in progress, begin one with the next P-frame.
+ * If an intra refresh is in progress, begin one as soon as the current one finishes.
+ * Requires bIntraRefresh to be set.
+ *
+ * Useful for interactive streaming where the client can tell the server that packet loss has
+ * occurred. In this case, keyint can be set to an extremely high value so that intra refreshes
+ * occur only when calling x265_encoder_intra_refresh.
+ *
+ * In multi-pass encoding, if x265_encoder_intra_refresh is called differently in each pass,
+ * behavior is undefined.
+ *
+ * Should not be called during an x265_encoder_encode. */
+
+int x265_encoder_intra_refresh(x265_encoder *);
+
/* x265_cleanup:
* release library static allocations, reset configured CTU size */
void x265_cleanup(void);
@@ -1426,6 +1444,7 @@
void (*cleanup)(void);
int sizeof_frame_stats; /* sizeof(x265_frame_stats) */
+ int (*encoder_intra_refresh)(x265_encoder*);
/* add new pointers to the end, or increment X265_MAJOR_VERSION */
} x265_api;
More information about the x265-devel
mailing list