[FFmpeg-devel] [PATCH] vc2enc: implement adaptive quantization
Rostislav Pehlivanov
atomnuker at gmail.com
Fri Mar 18 13:54:28 CET 2016
The encoder will first sort all slices based on their score (determined
by the slice energy divided by the bytes the first pass has decided) and
will then reduce their quantization index based on the log2 of the
distance between the slice energy and the average energy of the frame.
If the reservoir (bytes_left) doesn't have enough, start increasing the
quantization index of the slices with the least score (starting from the
one with the most score out of those) until there are enough bits in the
reservoir.
Signed-off-by: Rostislav Pehlivanov <atomnuker at gmail.com>
---
libavcodec/vc2enc.c | 144 +++++++++++++++++++++++++++++++++-------------------
1 file changed, 93 insertions(+), 51 deletions(-)
diff --git a/libavcodec/vc2enc.c b/libavcodec/vc2enc.c
index f110dfc..addc5d4 100644
--- a/libavcodec/vc2enc.c
+++ b/libavcodec/vc2enc.c
@@ -20,6 +20,7 @@
*/
#include "libavutil/pixdesc.h"
+#include "libavutil/qsort.h"
#include "libavutil/opt.h"
#include "dirac.h"
#include "put_bits.h"
@@ -36,9 +37,6 @@
* (COEF_LUT_TAB*MAX_QUANT_INDEX) since the sign is appended during encoding */
#define COEF_LUT_TAB 2048
-/* Decides the cutoff point in # of slices to distribute the leftover bytes */
-#define SLICE_REDIST_TOTAL 150
-
enum VC2_QM {
VC2_QM_DEF = 0,
VC2_QM_COL,
@@ -73,7 +71,7 @@ typedef struct SliceArgs {
int quant_idx;
int bits_ceil;
int bits_floor;
- int bytes_left;
+ int hh_ener;
int bytes;
} SliceArgs;
@@ -94,6 +92,7 @@ typedef struct VC2EncContext {
DiracVersionInfo ver;
SliceArgs *slice_args;
+ SliceArgs **slice_ratings;
TransformArgs transform_args[3];
/* For conversion from unsigned pixel values to signed */
@@ -557,6 +556,26 @@ static void encode_subband(VC2EncContext *s, PutBitContext *pb, int sx, int sy,
}
}
+static int slice_hh_ener(VC2EncContext *s, int slice_x, int slice_y)
+{
+ int x, y;
+ int ener = 0, er_avg = 0;
+ SubBand *b = &s->plane[0].band[0][3];
+ const int left = b->width * slice_x / s->num_x;
+ const int right = b->width *(slice_x+1) / s->num_x;
+ const int top = b->height * slice_y / s->num_y;
+ const int bottom = b->height *(slice_y+1) / s->num_y;
+ dwtcoef *buf = b->buf + top * b->stride;
+ for (y = top; y < bottom; y++) {
+ for (x = left; x < right; x++) {
+ er_avg = (buf[x] + er_avg)/2;
+ ener += FFABS(buf[x] - er_avg);
+ }
+ buf += b->stride;
+ }
+ return ener;
+}
+
static int count_hq_slice(VC2EncContext *s, int *cache,
int slice_x, int slice_y, int quant_idx)
{
@@ -636,6 +655,7 @@ static int rate_control(AVCodecContext *avctx, void *arg)
int quant_buf[2] = {-1, -1};
int quant = slice_dat->quant_idx, step = 1;
int bits_last, bits = count_hq_slice(s, slice_dat->cache, sx, sy, quant);
+ slice_dat->hh_ener = slice_hh_ener(s, sx, sy);
while ((bits > top) || (bits < bottom)) {
const int signed_step = bits > top ? +step : -step;
quant = av_clip(quant + signed_step, 0, s->q_ceil-1);
@@ -652,18 +672,26 @@ static int rate_control(AVCodecContext *avctx, void *arg)
}
slice_dat->quant_idx = av_clip(quant, 0, s->q_ceil-1);
slice_dat->bytes = FFALIGN((bits >> 3), s->size_scaler) + 4 + s->prefix_bytes;
- slice_dat->bytes_left = s->slice_max_bytes - slice_dat->bytes;
return 0;
}
+/* energy/bytes should grow as the slice gets more bitrate starved */
+static int slices_cmp(const void *a, const void *b)
+{
+ SliceArgs *s1 = *((SliceArgs **)a);
+ SliceArgs *s2 = *((SliceArgs **)b);
+ return (s1->hh_ener/s1->bytes) - (s2->hh_ener/s2->bytes);
+}
+
static int calc_slice_sizes(VC2EncContext *s)
{
- int i, slice_x, slice_y, bytes_left = 0;
- int bytes_top[SLICE_REDIST_TOTAL] = {0};
- int64_t total_bytes_needed = 0;
- int slice_redist_range = FFMIN(SLICE_REDIST_TOTAL, s->num_x*s->num_y);
SliceArgs *enc_args = s->slice_args;
- SliceArgs *top_loc[SLICE_REDIST_TOTAL] = {NULL};
+ SliceArgs **tmp = &s->slice_ratings[s->num_x*s->num_y];
+ int i, slice_x, slice_y, bytes_left = 0;
+ int median_ener = 0, total_bytes_needed = 0;
+
+ int best = s->num_x*s->num_y/5;
+ int l_pos = best;
init_quant_matrix(s);
@@ -675,7 +703,7 @@ static int calc_slice_sizes(VC2EncContext *s)
args->y = slice_y;
args->bits_ceil = s->slice_max_bytes << 3;
args->bits_floor = s->slice_min_bytes << 3;
- memset(args, 0, s->q_ceil*sizeof(int));
+ memset(args->cache, 0, s->q_ceil*sizeof(*args->cache));
}
}
@@ -683,53 +711,63 @@ static int calc_slice_sizes(VC2EncContext *s)
s->avctx->execute(s->avctx, rate_control, enc_args, NULL, s->num_x*s->num_y,
sizeof(SliceArgs));
- for (slice_y = 0; slice_y < s->num_y; slice_y++) {
- for (slice_x = 0; slice_x < s->num_x; slice_x++) {
- SliceArgs *args = &enc_args[s->num_x*slice_y + slice_x];
- bytes_left += args->bytes_left;
- for (i = 0; i < slice_redist_range; i++) {
- if (args->bytes > bytes_top[i]) {
- bytes_top[i] = args->bytes;
- top_loc[i] = args;
+ for (i = 0; i < s->num_x*s->num_y; i++) {
+ SliceArgs *args = &enc_args[i];
+ s->slice_ratings[i] = args;
+ bytes_left += s->slice_max_bytes - args->bytes;
+ median_ener = (median_ener + args->hh_ener)/2;
+ }
+
+ AV_MSORT(s->slice_ratings, tmp,
+ s->num_x*s->num_y, SliceArgs *, slices_cmp);
+
+ for (i = best; i >= 0; i--) {
+ SliceArgs *h = s->slice_ratings[s->num_x*s->num_y - i - 1];
+ int h_step, h_n_idx, h_bits, h_bytes, h_needs;
+
+ if (l_pos <= 0 && bytes_left <= 0)
+ break;
+
+ h_step = ff_log2(FFABS(h->hh_ener - median_ener));
+ h_n_idx = av_clip(h->quant_idx - h_step, 3, 10);
+ h_bits = count_hq_slice(s, h->cache, h->x, h->y, h_n_idx);
+ h_bytes = FFALIGN((h_bits >> 3), s->size_scaler) + 4 + s->prefix_bytes;
+ h_needs = h_bytes - h->bytes;
+
+ if (h_n_idx == h->quant_idx)
+ continue;
+
+ if (bytes_left < h_needs) { /* Steal from the poor, give to the rich! */
+ for (; l_pos > 0; l_pos--) {
+ SliceArgs *l = s->slice_ratings[l_pos];
+ int l_step = 1;
+ int l_new_idx = av_clip(l->quant_idx + l_step, 0, s->q_ceil - 1);
+ int l_bits = count_hq_slice(s, l->cache, l->x, l->y, l_new_idx);
+ int l_bytes = FFALIGN((l_bits >> 3), s->size_scaler) + 4 + s->prefix_bytes;
+ int l_diff = l->bytes - l_bytes;
+ if (l_diff <= 0)
+ continue;
+ l->quant_idx = l_new_idx;
+ l->bytes = l_bytes;
+ bytes_left += l_diff;
+ if (bytes_left > h_needs)
break;
- }
+ if (l_pos + 1 == (s->num_x*s->num_y - i - 1))
+ l_pos++;
}
}
- }
- /* Second pass - distribute leftover bytes */
- while (1) {
- int distributed = 0;
- for (i = 0; i < slice_redist_range; i++) {
- SliceArgs *args;
- int bits, bytes, diff, prev_bytes, new_idx;
- if (bytes_left <= 0)
- break;
- if (!top_loc[i] || !top_loc[i]->quant_idx)
- break;
- args = top_loc[i];
- prev_bytes = args->bytes;
- new_idx = FFMAX(args->quant_idx - 1, 0);
- bits = count_hq_slice(s, args->cache, args->x, args->y, new_idx);
- bytes = FFALIGN((bits >> 3), s->size_scaler) + 4 + s->prefix_bytes;
- diff = bytes - prev_bytes;
- if ((bytes_left - diff) > 0) {
- args->quant_idx = new_idx;
- args->bytes = bytes;
- bytes_left -= diff;
- distributed++;
- }
+ if (bytes_left > h_needs) { /* Do we have enough in the reservoir? */
+ h->bytes = h_bytes;
+ h->quant_idx = h_n_idx;
+ bytes_left -= h_needs;
}
- if (!distributed)
- break;
}
- for (slice_y = 0; slice_y < s->num_y; slice_y++) {
- for (slice_x = 0; slice_x < s->num_x; slice_x++) {
- SliceArgs *args = &enc_args[s->num_x*slice_y + slice_x];
- total_bytes_needed += args->bytes;
- s->q_avg = (s->q_avg + args->quant_idx)/2;
- }
+ for (i = 0; i < s->num_x*s->num_y; i++) {
+ SliceArgs *args = &enc_args[i];
+ total_bytes_needed += args->bytes;
+ s->q_avg = (s->q_avg + args->quant_idx)/2;
}
return total_bytes_needed;
@@ -1186,6 +1224,10 @@ static av_cold int vc2_encode_init(AVCodecContext *avctx)
if (!s->slice_args)
goto alloc_fail;
+ s->slice_ratings = av_malloc(2*s->num_x*s->num_y*sizeof(SliceArgs *));
+ if (!s->slice_ratings)
+ goto alloc_fail;
+
/* Lookup tables */
s->coef_lut_len = av_malloc(COEF_LUT_TAB*(s->q_ceil+1)*sizeof(*s->coef_lut_len));
if (!s->coef_lut_len)
--
2.8.0.rc3
More information about the ffmpeg-devel
mailing list