[x265] [PATCH] slicetype: Added slicetypeAnalyse
gopu at multicorewareinc.com
gopu at multicorewareinc.com
Fri Aug 16 13:48:29 CEST 2013
# HG changeset patch
# User ggopu
# Date 1376653700 -19800
# Node ID 849e9cab953dd5d585791d0730dba97290a0febc
# Parent 16feaf4ec7110fc6c9f2078c47ae3f17f2f7dd62
slicetype: Added slicetypeAnalyse
diff -r 16feaf4ec711 -r 849e9cab953d source/common/lookahead.h
--- a/source/common/lookahead.h Fri Aug 16 06:32:59 2013 -0500
+++ b/source/common/lookahead.h Fri Aug 16 17:18:20 2013 +0530
@@ -39,6 +39,7 @@
#define X265_SLICE_TYPE_I 0
#define X265_SLICE_TYPE_P 1
#define X265_SLICE_TYPE_B 2
+#define X265_LOOKAHEAD_MAX 250
struct LookaheadFrame : public ReferencePlanes
{
@@ -51,6 +52,8 @@
int cuHeight; // height of lowres frame in downscale CUs
int bframes;
bool bIntraCalculated;
+ int frameNum; // Presentation frame number
+ int scenecut; // Set to zero if the frame cannot possibly be part of a real scenecut.
int sliceType; // Slice type decided by lookahead
int gopIdx; // temp output for fixed-GOP pseudo-lookahead
diff -r 16feaf4ec711 -r 849e9cab953d source/encoder/slicetype.cpp
--- a/source/encoder/slicetype.cpp Fri Aug 16 06:32:59 2013 -0500
+++ b/source/encoder/slicetype.cpp Fri Aug 16 17:18:20 2013 +0530
@@ -345,6 +345,325 @@
fenc->lowresCosts[b - p0][p1 - b][cu_xy] = (uint16_t)(X265_MIN(bcost, LOWRES_COST_MASK) | (listused << LOWRES_COST_SHIFT));
}
+void Lookahead::slicetypeAnalyse(int keyframe)
+{
+ int num_frames, orig_num_frames, keyint_limit, framecnt;
+ int i_max_search = X265_MIN(frameQueueSize , X265_LOOKAHEAD_MAX );
+ int cu_count = 0; //need to calculate
+ int cost1p0, cost2p0, cost1b1, cost2p1;
+ int reset_start;
+
+ /* vbv_lookahead = i_vbv_buffer_size && i_lookahead; need clarifications here atpresent default 1 */
+ int vbv_lookahead = 1;
+
+ // TODO source frames assigned into Lookahead->frames[] for Further Processing
+ for (framecnt = 0; framecnt < i_max_search && frames[framecnt]->sliceType == X265_TYPE_AUTO; framecnt++)
+ {
+ ;//frames[framecnt] = source Frame
+ }
+
+ //TODO if framecount = 0 then call x264_macroblock_tree(), currently ignored
+
+ keyint_limit = cfg->param.keyframeMax - frames[0]->frameNum + last_keyframe - 1;
+ orig_num_frames = num_frames = X265_MIN( framecnt, keyint_limit );
+
+ /* This is important psy-wise: if we have a non-scenecut keyframe,
+ * there will be significant visual artifacts if the frames just before
+ * go down in quality due to being referenced less, despite it being
+ * more RD-optimal. */
+
+ if (vbv_lookahead)
+ num_frames = framecnt;
+ else if (num_frames < framecnt)
+ num_frames++;
+ else if (num_frames == 0)
+ {
+ frames[1]->sliceType = X265_TYPE_I;
+ return;
+ }
+
+ int num_bframes = 0;
+ int num_analysed_frames = num_frames;
+ if (cfg->param.scenecut_threshold && scenecut(0, 1, 1, orig_num_frames, i_max_search) )
+ {
+ frames[1]->sliceType = X265_TYPE_I;
+ return;
+ }
+
+ if (cfg->param.bframes )
+ {
+ if ( cfg->param.bFrameAdaptive == X265_B_ADAPT_TRELLIS )
+ {
+ if ( num_frames > 1 )
+ {
+ char best_paths[X265_BFRAME_MAX + 1][X265_LOOKAHEAD_MAX + 1] = { "", "P" };
+ int best_path_index = num_frames % (X265_BFRAME_MAX + 1);
+
+ /* Perform the frametype analysis. */
+ for ( int j = 2; j <= num_frames; j++ )
+ {
+ slicetypePath(j, best_paths);
+ }
+
+ num_bframes = strspn(best_paths[best_path_index], "B");
+ /* Load the results of the analysis into the frame types. */
+ for ( int j = 1; j < num_frames; j++ )
+ {
+ frames[j]->sliceType = best_paths[best_path_index][j - 1] == 'B' ? X265_TYPE_B : X265_TYPE_P;
+ }
+ }
+ frames[num_frames]->sliceType = X265_TYPE_P;
+ }
+ else if ( cfg->param.bFrameAdaptive == X265_B_ADAPT_FAST )
+ {
+ for (int i = 0; i <= num_frames - 2; )
+ {
+ cost2p1 = estimateFrameCost(i + 0, i + 2, i + 2, 1);
+ if ( frames[i + 2]->intraMbs[2] > cu_count / 2 )
+ {
+ frames[i + 1]->sliceType = X265_TYPE_P;
+ frames[i + 2]->sliceType = X265_TYPE_P;
+ i += 2;
+ continue;
+ }
+
+ cost1b1 = estimateFrameCost( i + 0, i + 2, i + 1, 0);
+ cost1p0 = estimateFrameCost( i + 0, i + 1, i + 1, 0);
+ cost2p0 = estimateFrameCost( i + 1, i + 2, i + 2, 0);
+
+ if ( cost1p0 + cost2p0 < cost1b1 + cost2p1 )
+ {
+ frames[i + 1]->sliceType = X265_TYPE_P;
+ i += 1;
+ continue;
+ }
+
+// arbitrary and untuned
+#define INTER_THRESH 300
+#define P_SENS_BIAS (50 - cfg->param.bFrameBias)
+ frames[i + 1]->sliceType = X265_TYPE_B;
+
+ int j;
+ for (j = i + 2; j <= X265_MIN(i + cfg->param.bframes, num_frames - 1); j++)
+ {
+ int pthresh = X265_MAX(INTER_THRESH - P_SENS_BIAS * (j - i - 1), INTER_THRESH / 10);
+ int pcost = estimateFrameCost( i + 0, j + 1, j + 1, 1);
+ if (pcost > pthresh * cu_count || frames[j + 1]->intraMbs[j - i + 1] > cu_count / 3)
+ break;
+ frames[j]->sliceType = X265_TYPE_B;
+ }
+
+ frames[j]->sliceType = X265_TYPE_P;
+ i = j;
+ }
+ frames[num_frames]->sliceType = X265_TYPE_P;
+ num_bframes = 0;
+ while (num_bframes < num_frames && frames[num_bframes + 1]->sliceType == X265_TYPE_B)
+ {
+ num_bframes++;
+ }
+ }
+ else
+ {
+ num_bframes = X265_MIN(num_frames - 1, cfg->param.bframes);
+ for (int j = 1; j < num_frames; j++)
+ {
+ frames[j]->sliceType = (j % (num_bframes + 1)) ? X265_TYPE_B : X265_TYPE_P;
+ }
+ frames[num_frames]->sliceType = X265_TYPE_P;
+ }
+ /* Check scenecut on the first minigop. */
+ for (int j = 1; j < num_bframes + 1; j++)
+ {
+ if (cfg->param.scenecut_threshold && scenecut(j, j + 1, 0, orig_num_frames, i_max_search))
+ {
+ frames[j]->sliceType = X265_TYPE_P;
+ num_analysed_frames = j;
+ break;
+ }
+ }
+ reset_start = keyframe ? 1 : X265_MIN(num_bframes + 2, num_analysed_frames + 1);
+ }
+ else
+ {
+ for (int j = 1; j <= num_frames; j++)
+ {
+ frames[j]->sliceType = X265_TYPE_P;
+ }
+ reset_start = !keyframe + 1;
+ num_bframes = 0;
+ }
+
+ // TODO if rc.b_mb_tree Enabled the need to call x264_macroblock_tree currently Ignored the call
+
+ /* Restore frametypes for all frames that haven't actually been decided yet. */
+ for (int j = reset_start; j <= num_frames; j++)
+ {
+ frames[j]->sliceType = X265_TYPE_AUTO;
+ }
+}
+
+int Lookahead::scenecut(int p0, int p1, int real_scenecut, int num_frames, int i_max_search)
+{
+ /* Only do analysis during a normal scenecut check. */
+ if (real_scenecut && cfg->param.bframes)
+ {
+ int origmaxp1 = p0 + 1;
+ /* Look ahead to avoid coding short flashes as scenecuts. */
+ if (cfg->param.bFrameAdaptive == X265_B_ADAPT_TRELLIS)
+ /* Don't analyse any more frames than the trellis would have covered. */
+ origmaxp1 += cfg->param.bframes;
+ else
+ origmaxp1++;
+ int maxp1 = X265_MIN(origmaxp1, num_frames);
+
+ /* Where A and B are scenes: AAAAAABBBAAAAAA
+ * If BBB is shorter than (maxp1-p0), it is detected as a flash
+ * and not considered a scenecut. */
+ for (int curp1 = p1; curp1 <= maxp1; curp1++)
+ {
+ if (!scenecut_internal(p0, curp1, 0))
+ /* Any frame in between p0 and cur_p1 cannot be a real scenecut. */
+ for ( int i = curp1; i > p0; i-- )
+ {
+ frames[i]->scenecut = 0;
+ }
+ }
+
+ /* Where A-F are scenes: AAAAABBCCDDEEFFFFFF
+ * If each of BB ... EE are shorter than (maxp1-p0), they are
+ * detected as flashes and not considered scenecuts.
+ * Instead, the first F frame becomes a scenecut.
+ * If the video ends before F, no frame becomes a scenecut. */
+ for (int curp0 = p0; curp0 <= maxp1; curp0++)
+ {
+ if (origmaxp1 > i_max_search || (curp0 < maxp1 && scenecut_internal( curp0, maxp1, 0)))
+ /* If cur_p0 is the p0 of a scenecut, it cannot be the p1 of a scenecut. */
+ frames[curp0]->scenecut = 0;
+ }
+ }
+
+ /* Ignore frames that are part of a flash, i.e. cannot be real scenecuts. */
+ if (!frames[p1]->scenecut)
+ return 0;
+ return scenecut_internal(p0, p1, real_scenecut);
+}
+
+int Lookahead::scenecut_internal( int p0, int p1, int real_scenecut)
+{
+ LookaheadFrame *frame = frames[p1];
+ estimateFrameCost(p0, p1, p1, 0);
+
+ int icost = frame->costEst[0][0];
+ int pcost = frame->costEst[p1 - p0][0];
+ float f_bias;
+ int i_gop_size = frame->frameNum - last_keyframe;
+ float f_thresh_max = (float) (cfg->param.scenecut_threshold / 100.0);
+ /* magic numbers pulled out of thin air */
+ float f_thresh_min = (float) (f_thresh_max * 0.25);
+ int res;
+
+ if (cfg->param.keyframeMin == cfg->param.keyframeMax)
+ f_thresh_min = f_thresh_max;
+ if (i_gop_size <= cfg->param.keyframeMin / 4)
+ f_bias = f_thresh_min / 4;
+ else if (i_gop_size <= cfg->param.keyframeMin)
+ f_bias = f_thresh_min * i_gop_size / cfg->param.keyframeMin;
+ else
+ {
+ f_bias = f_thresh_min
+ + (f_thresh_max - f_thresh_min)
+ * (i_gop_size - cfg->param.keyframeMin)
+ / (cfg->param.keyframeMax - cfg->param.keyframeMin);
+ }
+
+ res = pcost >= (1.0 - f_bias) * icost;
+ return res;
+}
+
+void Lookahead::slicetypePath(int length, char(*best_paths)[X265_LOOKAHEAD_MAX + 1])
+{
+ char paths[2][X265_LOOKAHEAD_MAX + 1];
+ int num_paths = X265_MIN(cfg->param.bframes + 1, length);
+ int best_cost = me.COST_MAX;
+ int idx = 0;
+
+ /* Iterate over all currently possible paths */
+ for (int path = 0; path < num_paths; path++)
+ {
+ /* Add suffixes to the current path */
+ int len = length - (path + 1);
+ memcpy(paths[idx], best_paths[len % (X265_BFRAME_MAX + 1)], len);
+ memset(paths[idx] + len, 'B', path);
+ strcpy(paths[idx] + len + path, "P");
+
+ /* Calculate the actual cost of the current path */
+ int cost = slicetypePathCost(paths[idx], best_cost);
+ if (cost < best_cost)
+ {
+ best_cost = cost;
+ idx ^= 1;
+ }
+ }
+
+ /* Store the best path. */
+ memcpy(best_paths[length % (X265_BFRAME_MAX + 1)], paths[idx ^ 1], length);
+
+}
+
+int Lookahead::slicetypePathCost( char *path, int threshold)
+{
+ int loc = 1;
+ int cost = 0;
+ int cur_p = 0;
+
+ path--; /* Since the 1st path element is really the second frame */
+ while ( path[loc] )
+ {
+ int next_p = loc;
+ /* Find the location of the next P-frame. */
+ while ( path[next_p] != 'P' )
+ {
+ next_p++;
+ }
+
+ /* Add the cost of the P-frame found above */
+ cost += estimateFrameCost(cur_p, next_p, next_p, 0);
+ /* Early terminate if the cost we have found is larger than the best path cost so far */
+ if (cost > threshold)
+ break;
+
+ /* Keep some B-frames as references: 0=off, 1=strict hierarchical, 2=normal */
+ //TODO Add this into param
+ int bframe_pyramid = 0;
+
+ if (bframe_pyramid && next_p - cur_p > 2)
+ {
+ int middle = cur_p + (next_p - cur_p) / 2;
+ cost += estimateFrameCost(cur_p, next_p, middle, 0);
+ for (int next_b = loc; next_b < middle && cost < threshold; next_b++)
+ {
+ cost += estimateFrameCost( cur_p, middle, next_b, 0);
+ }
+
+ for (int next_b = middle + 1; next_b < next_p && cost < threshold; next_b++)
+ {
+ cost += estimateFrameCost( middle, next_p, next_b, 0);
+ }
+ }
+ else
+ for (int next_b = loc; next_b < next_p && cost < threshold; next_b++)
+ {
+ cost += estimateFrameCost( cur_p, next_p, next_b, 0);
+ }
+
+ loc = next_p + 1;
+ cur_p = next_p;
+ }
+
+ return cost;
+}
+
#if 0
// Indexed by pic_struct values
static const uint8_t delta_tfi_divisor[10] = { 0, 2, 1, 1, 2, 2, 3, 3, 4, 6 };
diff -r 16feaf4ec711 -r 849e9cab953d source/encoder/slicetype.h
--- a/source/encoder/slicetype.h Fri Aug 16 06:32:59 2013 -0500
+++ b/source/encoder/slicetype.h Fri Aug 16 17:18:20 2013 +0530
@@ -33,6 +33,17 @@
// arbitrary, but low because SATD scores are 1/4 normal
#define X265_LOOKAHEAD_QP (12 + QP_BD_OFFSET)
+/* Slice type */
+#define X265_TYPE_AUTO 0x0000 /* Let x264 choose the right type */
+#define X265_TYPE_IDR 0x0001
+#define X265_TYPE_I 0x0002
+#define X265_TYPE_P 0x0003
+#define X265_TYPE_BREF 0x0004 /* Non-disposable B-frame */
+#define X265_TYPE_B 0x0005
+#define X265_TYPE_KEYFRAME 0x0006 /* IDR or I depending on b_open_gop option */
+#define IS_X265_TYPE_I(x) ((x)==X265_TYPE_I || (x)==X265_TYPE_IDR)
+#define IS_X265_TYPE_B(x) ((x)==X265_TYPE_B || (x)==X265_TYPE_BREF)
+
namespace x265 {
// private namespace
@@ -47,6 +58,8 @@
int frameQueueSize;
int bAdaptMode;
int numDecided;
+ uint8_t analyse_keyframe;
+ int last_keyframe;
TComList<TComPic*> inputQueue; // input pictures in order received
TComList<TComPic*> outputQueue; // pictures to be encoded, in encode order
@@ -60,6 +73,11 @@
int estimateFrameCost(int p0, int p1, int b, int bIntraPenalty);
void estimateCUCost(int cux, int cuy, int p0, int p1, int b, int do_search[2]);
+ void slicetypeAnalyse(int key_frame);
+ int scenecut(int p0, int p1, int real_scenecut, int num_frames, int i_max_search);
+ int scenecut_internal( int p0, int p1, int real_scenecut);
+ void slicetypePath( int length, char(*best_paths)[X265_LOOKAHEAD_MAX + 1]);
+ int slicetypePathCost( char *path, int threshold);
};
}
diff -r 16feaf4ec711 -r 849e9cab953d source/x265.h
--- a/source/x265.h Fri Aug 16 06:32:59 2013 -0500
+++ b/source/x265.h Fri Aug 16 17:18:20 2013 +0530
@@ -198,6 +198,7 @@
int lookaheadDepth; ///< Number of frames to use for lookahead, determines encoder latency
int bFrameAdaptive; ///< 0 - none, 1 - fast, 2 - full (trellis) adaptive B frame scheduling
int bFrameBias;
+ int scenecut_threshold; /// how aggressively to insert extra I frames
// Intra coding tools
int bEnableConstrainedIntra; ///< enable constrained intra prediction (ignore inter predicted reference samples)
More information about the x265-devel
mailing list