From 653e69e3343c49a0230c6591729515cb52e8edf5 Mon Sep 17 00:00:00 2001 From: Alexis Ballier Date: Wed, 22 Jun 2011 12:48:27 -0400 Subject: [PATCH 1/7] Fix parallel install Require the destination to be present before trying to create the symlink. See: http://bugs.gentoo.org/show_bug.cgi?id=323805 Change-Id: I14ed4a9792dedc289885a9a43bc5a86cb792206d --- libs.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs.mk b/libs.mk index c09581cc3..ae86fbd9e 100644 --- a/libs.mk +++ b/libs.mk @@ -198,7 +198,7 @@ libvpx.ver: $(call enabled,CODEC_EXPORTS) $(qexec)echo "local: *; };" >> $@ CLEAN-OBJS += libvpx.ver -$(addprefix $(DIST_DIR)/,$(LIBVPX_SO_SYMLINKS)): +$(addprefix $(DIST_DIR)/,$(LIBVPX_SO_SYMLINKS)): $(DIST_DIR)/$(LIBSUBDIR)/$(LIBVPX_SO) @echo " [LN] $@" $(qexec)ln -sf $(LIBVPX_SO) $@ From 2bd90c13a04fddddc4379688eab6b6132aabe933 Mon Sep 17 00:00:00 2001 From: James Berry Date: Wed, 22 Jun 2011 12:41:17 -0400 Subject: [PATCH 2/7] get/set reference buffer dimension check added vp8_yv12_copy_frame_ptr() expects same size buffers which was not previously gaurenteed. Using an improperly allocated buffer would cause a crash before. Change-Id: I904982313ce9352474f80de842013dcd89f48685 --- vp8/common/onyxd.h | 5 ++-- vp8/decoder/onyxd_if.c | 57 +++++++++++++++++++++++++++++------------- vp8/vp8_dx_iface.c | 6 ++--- 3 files changed, 45 insertions(+), 23 deletions(-) diff --git a/vp8/common/onyxd.h b/vp8/common/onyxd.h index 140dc5728..cf16a26da 100644 --- a/vp8/common/onyxd.h +++ b/vp8/common/onyxd.h @@ -22,6 +22,7 @@ extern "C" #include "vpx_scale/yv12config.h" #include "ppflags.h" #include "vpx_ports/mem.h" +#include "vpx/vpx_codec.h" typedef void *VP8D_PTR; typedef struct @@ -54,8 +55,8 @@ extern "C" int vp8dx_receive_compressed_data(VP8D_PTR comp, unsigned long size, const unsigned char *dest, INT64 time_stamp); int vp8dx_get_raw_frame(VP8D_PTR comp, YV12_BUFFER_CONFIG *sd, INT64 *time_stamp, INT64 *time_end_stamp, vp8_ppflags_t *flags); - int vp8dx_get_reference(VP8D_PTR comp, VP8_REFFRAME ref_frame_flag, YV12_BUFFER_CONFIG *sd); - int vp8dx_set_reference(VP8D_PTR comp, VP8_REFFRAME ref_frame_flag, YV12_BUFFER_CONFIG *sd); + vpx_codec_err_t vp8dx_get_reference(VP8D_PTR comp, VP8_REFFRAME ref_frame_flag, YV12_BUFFER_CONFIG *sd); + vpx_codec_err_t vp8dx_set_reference(VP8D_PTR comp, VP8_REFFRAME ref_frame_flag, YV12_BUFFER_CONFIG *sd); VP8D_PTR vp8dx_create_decompressor(VP8D_CONFIG *oxcf); diff --git a/vp8/decoder/onyxd_if.c b/vp8/decoder/onyxd_if.c index 4845cd076..9de5ce2c4 100644 --- a/vp8/decoder/onyxd_if.c +++ b/vp8/decoder/onyxd_if.c @@ -134,7 +134,7 @@ void vp8dx_remove_decompressor(VP8D_PTR ptr) } -int vp8dx_get_reference(VP8D_PTR ptr, VP8_REFFRAME ref_frame_flag, YV12_BUFFER_CONFIG *sd) +vpx_codec_err_t vp8dx_get_reference(VP8D_PTR ptr, VP8_REFFRAME ref_frame_flag, YV12_BUFFER_CONFIG *sd) { VP8D_COMP *pbi = (VP8D_COMP *) ptr; VP8_COMMON *cm = &pbi->common; @@ -146,16 +146,27 @@ int vp8dx_get_reference(VP8D_PTR ptr, VP8_REFFRAME ref_frame_flag, YV12_BUFFER_C ref_fb_idx = cm->gld_fb_idx; else if (ref_frame_flag == VP8_ALT_FLAG) ref_fb_idx = cm->alt_fb_idx; + else{ + vpx_internal_error(&pbi->common.error, VPX_CODEC_ERROR, + "Invalid reference frame"); + return pbi->common.error.error_code; + } + + if(cm->yv12_fb[ref_fb_idx].y_height != sd->y_height || + cm->yv12_fb[ref_fb_idx].y_width != sd->y_width || + cm->yv12_fb[ref_fb_idx].uv_height != sd->uv_height || + cm->yv12_fb[ref_fb_idx].uv_width != sd->uv_width){ + vpx_internal_error(&pbi->common.error, VPX_CODEC_ERROR, + "Incorrect buffer dimensions"); + } else - return -1; + vp8_yv12_copy_frame_ptr(&cm->yv12_fb[ref_fb_idx], sd); - vp8_yv12_copy_frame_ptr(&cm->yv12_fb[ref_fb_idx], sd); - - return 0; + return pbi->common.error.error_code; } -int vp8dx_set_reference(VP8D_PTR ptr, VP8_REFFRAME ref_frame_flag, YV12_BUFFER_CONFIG *sd) +vpx_codec_err_t vp8dx_set_reference(VP8D_PTR ptr, VP8_REFFRAME ref_frame_flag, YV12_BUFFER_CONFIG *sd) { VP8D_COMP *pbi = (VP8D_COMP *) ptr; VP8_COMMON *cm = &pbi->common; @@ -168,20 +179,32 @@ int vp8dx_set_reference(VP8D_PTR ptr, VP8_REFFRAME ref_frame_flag, YV12_BUFFER_C ref_fb_ptr = &cm->gld_fb_idx; else if (ref_frame_flag == VP8_ALT_FLAG) ref_fb_ptr = &cm->alt_fb_idx; - else - return -1; + else{ + vpx_internal_error(&pbi->common.error, VPX_CODEC_ERROR, + "Invalid reference frame"); + return pbi->common.error.error_code; + } - /* Find an empty frame buffer. */ - free_fb = get_free_fb(cm); - /* Decrease fb_idx_ref_cnt since it will be increased again in - * ref_cnt_fb() below. */ - cm->fb_idx_ref_cnt[free_fb]--; + if(cm->yv12_fb[*ref_fb_ptr].y_height != sd->y_height || + cm->yv12_fb[*ref_fb_ptr].y_width != sd->y_width || + cm->yv12_fb[*ref_fb_ptr].uv_height != sd->uv_height || + cm->yv12_fb[*ref_fb_ptr].uv_width != sd->uv_width){ + vpx_internal_error(&pbi->common.error, VPX_CODEC_ERROR, + "Incorrect buffer dimensions"); + } + else{ + /* Find an empty frame buffer. */ + free_fb = get_free_fb(cm); + /* Decrease fb_idx_ref_cnt since it will be increased again in + * ref_cnt_fb() below. */ + cm->fb_idx_ref_cnt[free_fb]--; - /* Manage the reference counters and copy image. */ - ref_cnt_fb (cm->fb_idx_ref_cnt, ref_fb_ptr, free_fb); - vp8_yv12_copy_frame_ptr(sd, &cm->yv12_fb[*ref_fb_ptr]); + /* Manage the reference counters and copy image. */ + ref_cnt_fb (cm->fb_idx_ref_cnt, ref_fb_ptr, free_fb); + vp8_yv12_copy_frame_ptr(sd, &cm->yv12_fb[*ref_fb_ptr]); + } - return 0; + return pbi->common.error.error_code; } /*For ARM NEON, d8-d15 are callee-saved registers, and need to be saved by us.*/ diff --git a/vp8/vp8_dx_iface.c b/vp8/vp8_dx_iface.c index e0f4c0a96..d74dc87bc 100644 --- a/vp8/vp8_dx_iface.c +++ b/vp8/vp8_dx_iface.c @@ -588,8 +588,7 @@ static vpx_codec_err_t vp8_set_reference(vpx_codec_alg_priv_t *ctx, image2yuvconfig(&frame->img, &sd); - vp8dx_set_reference(ctx->pbi, frame->frame_type, &sd); - return VPX_CODEC_OK; + return vp8dx_set_reference(ctx->pbi, frame->frame_type, &sd); } else return VPX_CODEC_INVALID_PARAM; @@ -610,8 +609,7 @@ static vpx_codec_err_t vp8_get_reference(vpx_codec_alg_priv_t *ctx, image2yuvconfig(&frame->img, &sd); - vp8dx_get_reference(ctx->pbi, frame->frame_type, &sd); - return VPX_CODEC_OK; + return vp8dx_get_reference(ctx->pbi, frame->frame_type, &sd); } else return VPX_CODEC_INVALID_PARAM; From 3fde9964ce31948ebf2a11f1a0eae77d876600ee Mon Sep 17 00:00:00 2001 From: John Koleszar Date: Mon, 20 Jun 2011 17:09:29 -0400 Subject: [PATCH 3/7] vpxenc: add quantizer histogram display Add the --q-hist=n option, which displays a histogram with n buckets for the quantizer selected on each frame. Change-Id: I59b020c26b0acae0b938685081d9932bd98df5c9 --- vpxenc.c | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 164 insertions(+), 1 deletion(-) diff --git a/vpxenc.c b/vpxenc.c index 89cdef01e..264a719ea 100644 --- a/vpxenc.c +++ b/vpxenc.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "vpx/vpx_encoder.h" #if USE_POSIX_MMAP #include @@ -909,12 +910,14 @@ static const arg_def_t framerate = ARG_DEF(NULL, "fps", 1, "Stream frame rate (rate/scale)"); static const arg_def_t use_ivf = ARG_DEF(NULL, "ivf", 0, "Output IVF (default is WebM)"); +static const arg_def_t q_hist_n = ARG_DEF(NULL, "q-hist", 1, + "Show quantizer histogram (n-buckets)"); static const arg_def_t *main_args[] = { &debugmode, &outputfile, &codecarg, &passes, &pass_arg, &fpf_name, &limit, &deadline, &best_dl, &good_dl, &rt_dl, - &verbosearg, &psnrarg, &use_ivf, + &verbosearg, &psnrarg, &use_ivf, &q_hist_n, NULL }; @@ -1107,6 +1110,149 @@ static void usage_exit() exit(EXIT_FAILURE); } + +#define HIST_BAR_MAX 40 +struct hist_bucket +{ + int low, high, count; +}; + + +static int merge_hist_buckets(struct hist_bucket *bucket, + int *buckets_, + int max_buckets) +{ + int small_bucket = 0, merge_bucket = INT_MAX, big_bucket=0; + int buckets = *buckets_; + int i; + + /* Find the extrema for this list of buckets */ + big_bucket = small_bucket = 0; + for(i=0; i < buckets; i++) + { + if(bucket[i].count < bucket[small_bucket].count) + small_bucket = i; + if(bucket[i].count > bucket[big_bucket].count) + big_bucket = i; + } + + /* If we have too many buckets, merge the smallest with an ajacent + * bucket. + */ + while(buckets > max_buckets) + { + int last_bucket = buckets - 1; + + // merge the small bucket with an adjacent one. + if(small_bucket == 0) + merge_bucket = 1; + else if(small_bucket == last_bucket) + merge_bucket = last_bucket - 1; + else if(bucket[small_bucket - 1].count < bucket[small_bucket + 1].count) + merge_bucket = small_bucket - 1; + else + merge_bucket = small_bucket + 1; + + assert(abs(merge_bucket - small_bucket) <= 1); + assert(small_bucket < buckets); + assert(big_bucket < buckets); + assert(merge_bucket < buckets); + + if(merge_bucket < small_bucket) + { + bucket[merge_bucket].high = bucket[small_bucket].high; + bucket[merge_bucket].count += bucket[small_bucket].count; + } + else + { + bucket[small_bucket].high = bucket[merge_bucket].high; + bucket[small_bucket].count += bucket[merge_bucket].count; + merge_bucket = small_bucket; + } + + assert(bucket[merge_bucket].low != bucket[merge_bucket].high); + + buckets--; + + /* Remove the merge_bucket from the list, and find the new small + * and big buckets while we're at it + */ + big_bucket = small_bucket = 0; + for(i=0; i < buckets; i++) + { + if(i > merge_bucket) + bucket[i] = bucket[i+1]; + + if(bucket[i].count < bucket[small_bucket].count) + small_bucket = i; + if(bucket[i].count > bucket[big_bucket].count) + big_bucket = i; + } + + } + + *buckets_ = buckets; + return bucket[big_bucket].count; +} + + +static void show_histogram(const struct hist_bucket *bucket, + int buckets, + int total, + int scale) +{ + int i; + + for(i=0; i Date: Tue, 21 Jun 2011 16:42:33 -0400 Subject: [PATCH 4/7] vpxenc: add rate histogram display Add the --rate-hist=n option, which displays a histogram with n buckets for the rate over the --buf-sz window. Change-Id: I2807b5a1525c7972e9ba40839b37e92b23ceffaf --- vpxenc.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 149 insertions(+), 4 deletions(-) diff --git a/vpxenc.c b/vpxenc.c index 264a719ea..d311a14ce 100644 --- a/vpxenc.c +++ b/vpxenc.c @@ -912,12 +912,14 @@ static const arg_def_t use_ivf = ARG_DEF(NULL, "ivf", 0, "Output IVF (default is WebM)"); static const arg_def_t q_hist_n = ARG_DEF(NULL, "q-hist", 1, "Show quantizer histogram (n-buckets)"); +static const arg_def_t rate_hist_n = ARG_DEF(NULL, "rate-hist", 1, + "Show rate histogram (n-buckets)"); static const arg_def_t *main_args[] = { &debugmode, &outputfile, &codecarg, &passes, &pass_arg, &fpf_name, &limit, &deadline, &best_dl, &good_dl, &rt_dl, - &verbosearg, &psnrarg, &use_ivf, &q_hist_n, + &verbosearg, &psnrarg, &use_ivf, &q_hist_n, &rate_hist_n, NULL }; @@ -1201,8 +1203,42 @@ static void show_histogram(const struct hist_bucket *bucket, int total, int scale) { + const char *pat1, *pat2; int i; + switch((int)(log(bucket[buckets-1].high)/log(10))+1) + { + case 1: + case 2: + pat1 = "%4d %2s: "; + pat2 = "%4d-%2d: "; + break; + case 3: + pat1 = "%5d %3s: "; + pat2 = "%5d-%3d: "; + break; + case 4: + pat1 = "%6d %4s: "; + pat2 = "%6d-%4d: "; + break; + case 5: + pat1 = "%7d %5s: "; + pat2 = "%7d-%5d: "; + break; + case 6: + pat1 = "%8d %6s: "; + pat2 = "%8d-%6d: "; + break; + case 7: + pat1 = "%9d %7s: "; + pat2 = "%9d-%7d: "; + break; + default: + pat1 = "%12d %10s: "; + pat2 = "%12d-%10d: "; + break; + } + for(i=0; isamples = cfg->rc_buf_sz * 60 / 1000; // max 60 fps + hist->pts = calloc(hist->samples, sizeof(*hist->pts)); + hist->sz = calloc(hist->samples, sizeof(*hist->sz)); + for(i=0; ibucket[i].low = INT_MAX; + hist->bucket[i].high = 0; + hist->bucket[i].count = 0; + } +} + + +static void destroy_rate_histogram(struct rate_hist *hist) +{ + free(hist->pts); + free(hist->sz); +} + + +static void update_rate_histogram(struct rate_hist *hist, + const vpx_codec_enc_cfg_t *cfg, + const vpx_codec_cx_pkt_t *pkt) +{ + int i, idx; + int64_t now, then, sum_sz = 0, avg_bitrate; + + now = pkt->data.frame.pts * 1000 + * (uint64_t)cfg->g_timebase.num / (uint64_t)cfg->g_timebase.den; + + idx = hist->frames++ % hist->samples; + hist->pts[idx] = now; + hist->sz[idx] = pkt->data.frame.sz; + + if(now < cfg->rc_buf_initial_sz) + return; + + /* Sum the size over the past rc_buf_sz ms */ + for(i = hist->frames; i > 0; i--) + { + int i_idx = (i-1) % hist->samples; + + then = hist->pts[i_idx]; + if(now - then > cfg->rc_buf_sz) + break; + sum_sz += hist->sz[i_idx]; + } + + avg_bitrate = sum_sz * 8 * 1000 / (now - then); + idx = avg_bitrate * (RATE_BINS/2) / (cfg->rc_target_bitrate * 1000); + if(idx < 0) + idx = 0; + if(idx > RATE_BINS-1) + idx = RATE_BINS-1; + if(hist->bucket[idx].low > avg_bitrate) + hist->bucket[idx].low = avg_bitrate; + if(hist->bucket[idx].high < avg_bitrate) + hist->bucket[idx].high = avg_bitrate; + hist->bucket[idx].count++; + hist->total++; +} + + +static void show_rate_histogram(struct rate_hist *hist, + const vpx_codec_enc_cfg_t *cfg, + int max_buckets) +{ + int i, scale; + int buckets = 0; + + for(i = 0; i < RATE_BINS; i++) + { + if(hist->bucket[i].low == INT_MAX) + continue; + hist->bucket[buckets++] = hist->bucket[i]; + } + + fprintf(stderr, "\nRate (over %dms window):\n", cfg->rc_buf_sz); + scale = merge_hist_buckets(hist->bucket, &buckets, max_buckets); + show_histogram(hist->bucket, buckets, hist->total, scale); +} + #define ARG_CTRL_CNT_MAX 10 int main(int argc, const char **argv_) @@ -1293,6 +1427,8 @@ int main(int argc, const char **argv_) stereo_format_t stereo_fmt = STEREO_FORMAT_MONO; int counts[64]={0}; int show_q_hist_buckets=0; + int show_rate_hist_buckets=0; + struct rate_hist rate_hist={0}; exec_name = argv_[0]; ebml.last_pts_ms = -1; @@ -1378,6 +1514,8 @@ int main(int argc, const char **argv_) ebml.debug = 1; else if (arg_match(&arg, &q_hist_n, argi)) show_q_hist_buckets = arg_parse_uint(&arg); + else if (arg_match(&arg, &rate_hist_n, argi)) + show_rate_hist_buckets = arg_parse_uint(&arg); else argj++; } @@ -1561,6 +1699,8 @@ int main(int argc, const char **argv_) memset(&stats, 0, sizeof(stats)); + init_rate_histogram(&rate_hist, &cfg); + for (pass = one_pass_only ? one_pass_only - 1 : 0; pass < arg_passes; pass++) { int frames_in = 0, frames_out = 0; @@ -1821,6 +1961,7 @@ int main(int argc, const char **argv_) fprintf(stderr, " %6luF", (unsigned long)pkt->data.frame.sz); + update_rate_histogram(&rate_hist, &cfg, pkt); if(write_webm) { /* Update the hash */ @@ -1921,6 +2062,10 @@ int main(int argc, const char **argv_) if (show_q_hist_buckets) show_q_histogram(counts, show_q_hist_buckets); + if (show_rate_hist_buckets) + show_rate_histogram(&rate_hist, &cfg, show_rate_hist_buckets); + destroy_rate_histogram(&rate_hist); + vpx_img_free(&raw); free(argv); return EXIT_SUCCESS; From db67dcba6a0f10b79b261b9f7ba084b3d937c2bc Mon Sep 17 00:00:00 2001 From: John Koleszar Date: Thu, 23 Jun 2011 11:47:09 -0400 Subject: [PATCH 5/7] Revert "Reduce overshoot in 1 pass rate control" This reverts commit 212f6183739d448ad5fa2ccf1b4edd30829b2806. Further testing shows that the overshoot accumulation/damping is too aggressive on some clips. Allowing the accumulated overshoot to decay and limiting to damping to golden frames shows some promise. But some clips show significant overshoot in the buffer window, so I think this still needs work. Change-Id: Ic02a9ca34f55229f9cc04786f4fab54cdc1a3ef5 --- vp8/encoder/firstpass.c | 73 +++++++++++++++-------------------------- vp8/encoder/onyx_if.c | 61 +++++++++------------------------- vp8/encoder/onyx_int.h | 4 --- vp8/encoder/ratectrl.c | 43 +++--------------------- 4 files changed, 46 insertions(+), 135 deletions(-) diff --git a/vp8/encoder/firstpass.c b/vp8/encoder/firstpass.c index a0457609d..71e285f48 100644 --- a/vp8/encoder/firstpass.c +++ b/vp8/encoder/firstpass.c @@ -214,11 +214,25 @@ static int frame_max_bits(VP8_COMP *cpi) int max_bits; // For CBR we need to also consider buffer fullness. + // If we are running below the optimal level then we need to gradually tighten up on max_bits. if (cpi->oxcf.end_usage == USAGE_STREAM_FROM_SERVER) { - max_bits = 2 * cpi->av_per_frame_bandwidth; - max_bits -= cpi->buffered_av_per_frame_bandwidth; - max_bits *= ((double)cpi->oxcf.two_pass_vbrmax_section / 100.0); + double buffer_fullness_ratio = (double)cpi->buffer_level / DOUBLE_DIVIDE_CHECK((double)cpi->oxcf.optimal_buffer_level); + + // For CBR base this on the target average bits per frame plus the maximum sedction rate passed in by the user + max_bits = (int)(cpi->av_per_frame_bandwidth * ((double)cpi->oxcf.two_pass_vbrmax_section / 100.0)); + + // If our buffer is below the optimum level + if (buffer_fullness_ratio < 1.0) + { + // The lower of max_bits / 4 or cpi->av_per_frame_bandwidth / 4. + int min_max_bits = ((cpi->av_per_frame_bandwidth >> 2) < (max_bits >> 2)) ? cpi->av_per_frame_bandwidth >> 2 : max_bits >> 2; + + max_bits = (int)(max_bits * buffer_fullness_ratio); + + if (max_bits < min_max_bits) + max_bits = min_max_bits; // Lowest value we will set ... which should allow the buffer to refil. + } } // VBR else @@ -235,45 +249,6 @@ static int frame_max_bits(VP8_COMP *cpi) } -static int gf_group_max_bits(VP8_COMP *cpi) -{ - // Max allocation for a golden frame group - int max_bits; - - // For CBR we need to also consider buffer fullness. - if (cpi->oxcf.end_usage == USAGE_STREAM_FROM_SERVER) - { - max_bits = cpi->av_per_frame_bandwidth * cpi->baseline_gf_interval; - if (max_bits > cpi->oxcf.optimal_buffer_level) - { - max_bits -= cpi->oxcf.optimal_buffer_level; - max_bits += cpi->buffer_level; - } - else - { - max_bits -= (cpi->buffered_av_per_frame_bandwidth - - cpi->av_per_frame_bandwidth) - * cpi->baseline_gf_interval; - } - - max_bits *= ((double)cpi->oxcf.two_pass_vbrmax_section / 100.0); - } - else - { - // For VBR base this on the bits and frames left plus the two_pass_vbrmax_section rate passed in by the user - max_bits = (int)(((double)cpi->twopass.bits_left / (cpi->twopass.total_stats->count - (double)cpi->common.current_video_frame)) * ((double)cpi->oxcf.two_pass_vbrmax_section / 100.0)); - max_bits *= cpi->baseline_gf_interval; - } - - - // Trap case where we are out of bits - if (max_bits < 0) - max_bits = 0; - - return max_bits; -} - - static void output_stats(const VP8_COMP *cpi, struct vpx_codec_pkt_list *pktlist, FIRSTPASS_STATS *stats) @@ -1358,7 +1333,7 @@ static void define_gf_group(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame) double abs_mv_in_out_accumulator = 0.0; double mod_err_per_mb_accumulator = 0.0; - int max_group_bits; + int max_bits = frame_max_bits(cpi); // Max for a single frame unsigned int allow_alt_ref = cpi->oxcf.play_alternate && cpi->oxcf.lag_in_frames; @@ -1711,9 +1686,8 @@ static void define_gf_group(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame) cpi->twopass.gf_group_bits = (cpi->twopass.gf_group_bits < 0) ? 0 : (cpi->twopass.gf_group_bits > cpi->twopass.kf_group_bits) ? cpi->twopass.kf_group_bits : cpi->twopass.gf_group_bits; // Clip cpi->twopass.gf_group_bits based on user supplied data rate variability limit (cpi->oxcf.two_pass_vbrmax_section) - max_group_bits = gf_group_max_bits(cpi); - if (cpi->twopass.gf_group_bits > max_group_bits) - cpi->twopass.gf_group_bits = max_group_bits; + if (cpi->twopass.gf_group_bits > max_bits * cpi->baseline_gf_interval) + cpi->twopass.gf_group_bits = max_bits * cpi->baseline_gf_interval; // Reset the file position reset_fpf_position(cpi, start_pos); @@ -1808,6 +1782,13 @@ static void define_gf_group(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame) } } + // Apply an additional limit for CBR + if (cpi->oxcf.end_usage == USAGE_STREAM_FROM_SERVER) + { + if (cpi->twopass.gf_bits > (cpi->buffer_level >> 1)) + cpi->twopass.gf_bits = cpi->buffer_level >> 1; + } + // Dont allow a negative value for gf_bits if (gf_bits < 0) gf_bits = 0; diff --git a/vp8/encoder/onyx_if.c b/vp8/encoder/onyx_if.c index bebc9417d..0296d9290 100644 --- a/vp8/encoder/onyx_if.c +++ b/vp8/encoder/onyx_if.c @@ -1477,7 +1477,6 @@ static void init_config(VP8_PTR ptr, VP8_CONFIG *oxcf) cpi->rolling_actual_bits = cpi->av_per_frame_bandwidth; cpi->long_rolling_target_bits = cpi->av_per_frame_bandwidth; cpi->long_rolling_actual_bits = cpi->av_per_frame_bandwidth; - cpi->buffered_av_per_frame_bandwidth = cpi->av_per_frame_bandwidth; cpi->total_actual_bits = 0; cpi->total_target_vs_actual = 0; @@ -1573,7 +1572,7 @@ void vp8_change_config(VP8_PTR ptr, VP8_CONFIG *oxcf) break; } - if (cpi->pass == 0 && cpi->oxcf.end_usage != USAGE_STREAM_FROM_SERVER) + if (cpi->pass == 0) cpi->auto_worst_q = 1; cpi->oxcf.worst_allowed_q = q_trans[oxcf->worst_allowed_q]; @@ -3454,8 +3453,7 @@ static void encode_frame_to_data_rate // For CBR if the buffer reaches its maximum level then we can no longer // save up bits for later frames so we might as well use them up // on the current frame. - if (cpi->pass == 2 - && (cpi->oxcf.end_usage == USAGE_STREAM_FROM_SERVER) && + if ((cpi->oxcf.end_usage == USAGE_STREAM_FROM_SERVER) && (cpi->buffer_level >= cpi->oxcf.optimal_buffer_level) && cpi->buffered_mode) { int Adjustment = cpi->active_worst_quality / 4; // Max adjustment is 1/4 @@ -3546,9 +3544,6 @@ static void encode_frame_to_data_rate } else { - if(cpi->pass != 2) - Q = cpi->avg_frame_qindex; - cpi->active_best_quality = inter_minq[Q]; // For the constant/constrained quality mode we dont want @@ -3850,16 +3845,15 @@ static void encode_frame_to_data_rate (cpi->active_worst_quality < cpi->worst_quality) && (cpi->projected_frame_size > frame_over_shoot_limit)) { - /* step down active_worst_quality such that the corresponding - * active_best_quality will be equal to the current - * active_worst_quality + 1 - */ - int i; + int over_size_percent = ((cpi->projected_frame_size - frame_over_shoot_limit) * 100) / frame_over_shoot_limit; - for(i=cpi->active_worst_quality; iworst_quality; i++) - if(inter_minq[i] >= cpi->active_worst_quality + 1) - break; - cpi->active_worst_quality = i; + // If so is there any scope for relaxing it + while ((cpi->active_worst_quality < cpi->worst_quality) && (over_size_percent > 0)) + { + cpi->active_worst_quality++; + top_index = cpi->active_worst_quality; + over_size_percent = (int)(over_size_percent * 0.96); // Assume 1 qstep = about 4% on frame size. + } // If we have updated the active max Q do not call vp8_update_rate_correction_factors() this loop. active_worst_qchanged = TRUE; @@ -4247,9 +4241,10 @@ static void encode_frame_to_data_rate // Update the buffer level variable. // Non-viewable frames are a special case and are treated as pure overhead. - if ( cm->show_frame ) - cpi->bits_off_target += cpi->av_per_frame_bandwidth; - cpi->bits_off_target -= cpi->projected_frame_size; + if ( !cm->show_frame ) + cpi->bits_off_target -= cpi->projected_frame_size; + else + cpi->bits_off_target += cpi->av_per_frame_bandwidth - cpi->projected_frame_size; // Rolling monitors of whether we are over or underspending used to help regulate min and Max Q in two pass. cpi->rolling_target_bits = ((cpi->rolling_target_bits * 3) + cpi->this_frame_target + 2) / 4; @@ -4263,33 +4258,7 @@ static void encode_frame_to_data_rate // Debug stats cpi->total_target_vs_actual += (cpi->this_frame_target - cpi->projected_frame_size); - // Update the buffered average bitrate - { - long long numerator; - - numerator = cpi->oxcf.maximum_buffer_size - - cpi->buffered_av_per_frame_bandwidth - + cpi->projected_frame_size; - numerator *= cpi->buffered_av_per_frame_bandwidth; - cpi->buffered_av_per_frame_bandwidth = numerator - / cpi->oxcf.maximum_buffer_size; - } - - { - long long tmp = (long long)cpi->buffered_av_per_frame_bandwidth - * cpi->oxcf.maximum_buffer_size - / cpi->av_per_frame_bandwidth; - cpi->buffer_level = cpi->oxcf.maximum_buffer_size - - tmp - + cpi->oxcf.optimal_buffer_level; - } - - // Accumulate overshoot error. - cpi->accumulated_overshoot += - (cpi->projected_frame_size > cpi->av_per_frame_bandwidth) - ? cpi->projected_frame_size - cpi->av_per_frame_bandwidth - : 0; - + cpi->buffer_level = cpi->bits_off_target; // Update bits left to the kf and gf groups to account for overshoot or undershoot on these frames if (cm->frame_type == KEY_FRAME) diff --git a/vp8/encoder/onyx_int.h b/vp8/encoder/onyx_int.h index c460b9da9..3ca61a7cd 100644 --- a/vp8/encoder/onyx_int.h +++ b/vp8/encoder/onyx_int.h @@ -351,10 +351,6 @@ typedef struct VP8_COMP int per_frame_bandwidth; // Current section per frame bandwidth target int av_per_frame_bandwidth; // Average frame size target for clip int min_frame_bandwidth; // Minimum allocation that should be used for any frame - int buffered_av_per_frame_bandwidth; // Average bitrate over the last buffer - int buffered_av_per_frame_bandwidth_rem; // Average bitrate remainder - int accumulated_overshoot; // Accumulated # of bits spent > target - int inter_frame_target; double output_frame_rate; long long last_time_stamp_seen; diff --git a/vp8/encoder/ratectrl.c b/vp8/encoder/ratectrl.c index 73e1437b5..54c394dfc 100644 --- a/vp8/encoder/ratectrl.c +++ b/vp8/encoder/ratectrl.c @@ -605,10 +605,10 @@ static void calc_gf_params(VP8_COMP *cpi) static void calc_pframe_target_size(VP8_COMP *cpi) { - int min_frame_target, max_frame_target; + int min_frame_target; int Adjustment; - min_frame_target = 1; + min_frame_target = 0; if (cpi->pass == 2) { @@ -616,19 +616,10 @@ static void calc_pframe_target_size(VP8_COMP *cpi) if (min_frame_target < (cpi->av_per_frame_bandwidth >> 5)) min_frame_target = cpi->av_per_frame_bandwidth >> 5; - - max_frame_target = INT_MAX; } - else - { - if (min_frame_target < cpi->per_frame_bandwidth / 4) - min_frame_target = cpi->per_frame_bandwidth / 4; + else if (min_frame_target < cpi->per_frame_bandwidth / 4) + min_frame_target = cpi->per_frame_bandwidth / 4; - /* Don't allow the target to completely deplete the buffer. */ - max_frame_target = cpi->buffer_level + cpi->av_per_frame_bandwidth; - if(max_frame_target < min_frame_target) - max_frame_target = min_frame_target; - } // Special alt reference frame case if (cpi->common.refresh_alt_ref_frame) @@ -1121,32 +1112,6 @@ static void calc_pframe_target_size(VP8_COMP *cpi) } } - - if (cpi->pass==0 && cpi->oxcf.end_usage == USAGE_STREAM_FROM_SERVER){ - /* determine the accumulated error to apply to this frame. Apply - * more of the error when we've been undershooting, less when - * we've been overshooting - */ - long long adjust; - int bitrate_error; - - bitrate_error = cpi->av_per_frame_bandwidth - - cpi->buffered_av_per_frame_bandwidth; - - adjust = cpi->accumulated_overshoot; - adjust *= cpi->av_per_frame_bandwidth + bitrate_error; - adjust /= cpi->oxcf.maximum_buffer_size; - if (adjust > (cpi->this_frame_target - min_frame_target)) - adjust = (cpi->this_frame_target - min_frame_target); - else if (adjust < 0) - adjust = 0; - - cpi->this_frame_target -= adjust; - cpi->accumulated_overshoot -= adjust; - } - - if(cpi->this_frame_target > max_frame_target) - cpi->this_frame_target = max_frame_target; } From deca8cfc4461187a0dcc2fe588c983897b87aa7e Mon Sep 17 00:00:00 2001 From: Adrian Grange Date: Fri, 24 Jun 2011 08:43:40 -0700 Subject: [PATCH 6/7] Fixed initialization of frame buffer ref counters Only the first frame buffer ref counter was being initialized because the index was fixed at 0 rather than using i. Change-Id: Ib842298be4a5e3607f9e21c2cd4bfbee4054ffc4 --- vp8/common/alloccommon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vp8/common/alloccommon.c b/vp8/common/alloccommon.c index 216590cdc..376707ec6 100644 --- a/vp8/common/alloccommon.c +++ b/vp8/common/alloccommon.c @@ -70,7 +70,7 @@ int vp8_alloc_frame_buffers(VP8_COMMON *oci, int width, int height) for (i = 0; i < NUM_YV12_BUFFERS; i++) { - oci->fb_idx_ref_cnt[0] = 0; + oci->fb_idx_ref_cnt[i] = 0; oci->yv12_fb[i].flags = 0; if (vp8_yv12_alloc_frame_buffer(&oci->yv12_fb[i], width, height, VP8BORDERINPIXELS) < 0) { From ba0822ba96f0e910b50c85017de9b621dd007d0f Mon Sep 17 00:00:00 2001 From: Stefan Holmer Date: Thu, 16 Jun 2011 11:44:50 +0200 Subject: [PATCH 7/7] Adding support for error concealment in multi-threaded decoding Also includes a couple of error concealment bug fixes: - the segment_id wasn't properly initialized when missing - when interpolating and no neighbors are found, set to zero - clear the qcoef buffer when concealing an MB Change-Id: Id79c876b41d78b559a2241e9cd0fd2cae6198f49 --- examples/decode_with_partial_drops.txt | 14 +++-- vp8/decoder/decodframe.c | 11 +++- vp8/decoder/error_concealment.c | 8 +++ vp8/decoder/threading.c | 73 ++++++++++++++++++++++++-- 4 files changed, 96 insertions(+), 10 deletions(-) diff --git a/examples/decode_with_partial_drops.txt b/examples/decode_with_partial_drops.txt index 30854d669..7b0d3d2ca 100644 --- a/examples/decode_with_partial_drops.txt +++ b/examples/decode_with_partial_drops.txt @@ -110,7 +110,7 @@ void throw_packets(unsigned char* frame, int* size, int loss_rate, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DEC_INIT /* Initialize codec */ flags = VPX_CODEC_USE_ERROR_CONCEALMENT; -res = vpx_codec_dec_init(&codec, interface, NULL, flags); +res = vpx_codec_dec_init(&codec, interface, &dec_cfg, flags); if(res) die_codec(&codec, "Failed to initialize decoder"); @@ -123,11 +123,15 @@ which specifies the range or pattern of frames to drop. The parameter is parsed as follows: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ USAGE -if(argc!=4 && argc != 5) - die("Usage: %s \n", argv[0]); +if(argc < 4 || argc > 6) + die("Usage: %s [-t ] \n", + argv[0]); { char *nptr; - n = strtol(argv[3], &nptr, 0); + int arg_num = 3; + if (argc == 6 && strncmp(argv[arg_num++], "-t", 2) == 0) + dec_cfg.threads = strtol(argv[arg_num++], NULL, 0); + n = strtol(argv[arg_num], &nptr, 0); mode = (*nptr == '\0' || *nptr == ',') ? 2 : (*nptr == '-') ? 1 : 0; m = strtol(nptr+1, NULL, 0); @@ -138,6 +142,7 @@ if(argc!=4 && argc != 5) seed = (m > 0) ? m : (unsigned int)time(NULL); srand(seed);thrown_frame = 0; printf("Seed: %u\n", seed); +printf("Threads: %d\n", dec_cfg.threads); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ USAGE @@ -181,6 +186,7 @@ int n, m, mode; unsigned int seed; int thrown=0, kept=0; int thrown_frame=0, kept_frame=0; +vpx_codec_dec_cfg_t dec_cfg = {0}; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EXTRA_VARS diff --git a/vp8/decoder/decodframe.c b/vp8/decoder/decodframe.c index 734ab36fc..00e001733 100644 --- a/vp8/decoder/decodframe.c +++ b/vp8/decoder/decodframe.c @@ -239,12 +239,13 @@ static void decode_macroblock(VP8D_COMP *pbi, MACROBLOCKD *xd, #if CONFIG_ERROR_CONCEALMENT if (pbi->ec_enabled && - (mb_idx > pbi->mvs_corrupt_from_mb || + (mb_idx >= pbi->mvs_corrupt_from_mb || vp8dx_bool_error(xd->current_bc))) { /* MB with corrupt residuals or corrupt mode/motion vectors. * Better to use the predictor as reconstruction. */ + vpx_memset(xd->qcoeff, 0, sizeof(xd->qcoeff)); vp8_conceal_corrupt_mb(xd); return; } @@ -474,7 +475,13 @@ static void setup_token_decoder(VP8D_COMP *pbi, const unsigned char *partition; /* Parse number of token partitions to use */ - pc->multi_token_partition = (TOKEN_PARTITION)vp8_read_literal(&pbi->bc, 2); + const TOKEN_PARTITION multi_token_partition = + (TOKEN_PARTITION)vp8_read_literal(&pbi->bc, 2); + /* Only update the multi_token_partition field if we are sure the value + * is correct. */ + if (!pbi->ec_enabled || !vp8dx_bool_error(&pbi->bc)) + pc->multi_token_partition = multi_token_partition; + num_part = 1 << pc->multi_token_partition; /* Set up pointers to the first partition */ diff --git a/vp8/decoder/error_concealment.c b/vp8/decoder/error_concealment.c index c06bdcf07..4f8f1a66c 100644 --- a/vp8/decoder/error_concealment.c +++ b/vp8/decoder/error_concealment.c @@ -382,6 +382,7 @@ static void estimate_missing_mvs(MB_OVERLAP *overlaps, mi->mbmi.mode = SPLITMV; mi->mbmi.uv_mode = DC_PRED; mi->mbmi.partitioning = 3; + mi->mbmi.segment_id = 0; estimate_mb_mvs(block_overlaps, mi, mb_to_left_edge, @@ -563,6 +564,12 @@ static void interpolate_mvs(MACROBLOCKD *mb, mb->mb_to_top_edge, mb->mb_to_bottom_edge); } + else + { + mv->as_int = 0; + mi->bmi[row*4 + col].as_mode = NEW4X4; + mi->mbmi.need_to_clamp_mvs = 0; + } } } } @@ -597,6 +604,7 @@ void vp8_interpolate_motion(MACROBLOCKD *mb, mb->mode_info_context->mbmi.mode = SPLITMV; mb->mode_info_context->mbmi.uv_mode = DC_PRED; mb->mode_info_context->mbmi.partitioning = 3; + mb->mode_info_context->mbmi.segment_id = 0; } void vp8_conceal_corrupt_mb(MACROBLOCKD *xd) diff --git a/vp8/decoder/threading.c b/vp8/decoder/threading.c index c1df823f1..a7af9acfb 100644 --- a/vp8/decoder/threading.c +++ b/vp8/decoder/threading.c @@ -22,6 +22,9 @@ #include "detokenize.h" #include "vp8/common/reconinter.h" #include "reconintra_mt.h" +#if CONFIG_ERROR_CONCEALMENT +#include "error_concealment.h" +#endif extern void mb_init_dequantizer(VP8D_COMP *pbi, MACROBLOCKD *xd); extern void clamp_mvs(MACROBLOCKD *xd); @@ -151,6 +154,20 @@ static void decode_macroblock(VP8D_COMP *pbi, MACROBLOCKD *xd, int mb_row, int m vp8_build_inter_predictors_mb(xd); } +#if CONFIG_ERROR_CONCEALMENT + if (pbi->ec_enabled && + (mb_row * pbi->common.mb_cols + mb_col >= pbi->mvs_corrupt_from_mb || + vp8dx_bool_error(xd->current_bc))) + { + /* MB with corrupt residuals or corrupt mode/motion vectors. + * Better to use the predictor as reconstruction. + */ + vpx_memset(xd->qcoeff, 0, sizeof(xd->qcoeff)); + vp8_conceal_corrupt_mb(xd); + return; + } +#endif + /* dequantization and idct */ if (xd->mode_info_context->mbmi.mode != B_PRED && xd->mode_info_context->mbmi.mode != SPLITMV) { @@ -291,12 +308,37 @@ static THREAD_FUNCTION thread_decoding_proc(void *p_data) update_blockd_bmi(xd); - /* Distance of Mb to the various image edges. - * These are specified to 8th pel as they are always compared to values that are in 1/8th pel units + /* Distance of MB to the various image edges. + * These are specified to 8th pel as they are always + * compared to values that are in 1/8th pel units. */ xd->mb_to_left_edge = -((mb_col * 16) << 3); xd->mb_to_right_edge = ((pc->mb_cols - 1 - mb_col) * 16) << 3; +#if CONFIG_ERROR_CONCEALMENT + if (pbi->ec_enabled && + (xd->mode_info_context->mbmi.ref_frame == + INTRA_FRAME) && + vp8dx_bool_error(xd->current_bc)) + { + /* We have an intra block with corrupt coefficients, + * better to conceal with an inter block. + * Interpolate MVs from neighboring MBs + * + * Note that for the first mb with corrupt residual + * in a frame, we might not discover that before + * decoding the residual. That happens after this + * check, and therefore no inter concealment will be + * done. + */ + vp8_interpolate_motion(xd, + mb_row, mb_col, + pc->mb_rows, pc->mb_cols, + pc->mode_info_stride); + } +#endif + + xd->dst.y_buffer = pc->yv12_fb[dst_fb_idx].y_buffer + recon_yoffset; xd->dst.u_buffer = pc->yv12_fb[dst_fb_idx].u_buffer + recon_uvoffset; xd->dst.v_buffer = pc->yv12_fb[dst_fb_idx].v_buffer + recon_uvoffset; @@ -772,12 +814,35 @@ void vp8mt_decode_mb_rows( VP8D_COMP *pbi, MACROBLOCKD *xd) update_blockd_bmi(xd); - /* Distance of Mb to the various image edges. - * These are specified to 8th pel as they are always compared to values that are in 1/8th pel units + /* Distance of MB to the various image edges. + * These are specified to 8th pel as they are always compared to + * values that are in 1/8th pel units. */ xd->mb_to_left_edge = -((mb_col * 16) << 3); xd->mb_to_right_edge = ((pc->mb_cols - 1 - mb_col) * 16) << 3; +#if CONFIG_ERROR_CONCEALMENT + if (pbi->ec_enabled && + (xd->mode_info_context->mbmi.ref_frame == INTRA_FRAME) && + vp8dx_bool_error(xd->current_bc)) + { + /* We have an intra block with corrupt coefficients, better + * to conceal with an inter block. Interpolate MVs from + * neighboring MBs + * + * Note that for the first mb with corrupt residual in a + * frame, we might not discover that before decoding the + * residual. That happens after this check, and therefore no + * inter concealment will be done. + */ + vp8_interpolate_motion(xd, + mb_row, mb_col, + pc->mb_rows, pc->mb_cols, + pc->mode_info_stride); + } +#endif + + xd->dst.y_buffer = pc->yv12_fb[dst_fb_idx].y_buffer + recon_yoffset; xd->dst.u_buffer = pc->yv12_fb[dst_fb_idx].u_buffer + recon_uvoffset; xd->dst.v_buffer = pc->yv12_fb[dst_fb_idx].v_buffer + recon_uvoffset;