From 72d4890caf2c2b8b740c5bf9e7f8906cd5910d0e Mon Sep 17 00:00:00 2001 From: hui su Date: Thu, 5 May 2016 12:04:42 -0700 Subject: [PATCH] Add vp9 encoder API VP9E_GET_LEVEL to provide bitstream level Change-Id: I1ef3df0192491035728fe9d5eb25cc66dc2965de --- test/level_test.cc | 23 ++++- vp9/common/vp9_common.h | 19 ---- vp9/common/vp9_onyxc_int.h | 2 - vp9/encoder/vp9_bitstream.c | 6 +- vp9/encoder/vp9_bitstream.h | 2 + vp9/encoder/vp9_encoder.c | 183 +++++++++++++++++++++++++++++++++++- vp9/encoder/vp9_encoder.h | 72 ++++++++++++++ vp9/vp9_cx_iface.c | 13 ++- vpx/vp8cx.h | 11 ++- 9 files changed, 294 insertions(+), 37 deletions(-) diff --git a/test/level_test.cc b/test/level_test.cc index 5b9ce417f..62d0247d4 100644 --- a/test/level_test.cc +++ b/test/level_test.cc @@ -22,7 +22,9 @@ class LevelTest : EncoderTest(GET_PARAM(0)), encoding_mode_(GET_PARAM(1)), cpu_used_(GET_PARAM(2)), - target_level_(0) {} + min_gf_internal_(24), + target_level_(0), + level_(0) {} virtual ~LevelTest() {} virtual void SetUp() { @@ -47,6 +49,7 @@ class LevelTest if (video->frame() == 0) { encoder->Control(VP8E_SET_CPUUSED, cpu_used_); encoder->Control(VP9E_SET_TARGET_LEVEL, target_level_); + encoder->Control(VP9E_SET_MIN_GF_INTERVAL, min_gf_internal_); if (encoding_mode_ != ::libvpx_test::kRealTime) { encoder->Control(VP8E_SET_ENABLEAUTOALTREF, 1); encoder->Control(VP8E_SET_ARNR_MAXFRAMES, 7); @@ -54,20 +57,33 @@ class LevelTest encoder->Control(VP8E_SET_ARNR_TYPE, 3); } } + encoder->Control(VP9E_GET_LEVEL, &level_); + ASSERT_LE(level_, 51); + ASSERT_GE(level_, 0); } ::libvpx_test::TestMode encoding_mode_; int cpu_used_; + int min_gf_internal_; int target_level_; + int level_; }; +// Test for keeping level stats only TEST_P(LevelTest, TestTargetLevel0) { ::libvpx_test::I420VideoSource video("hantro_odd.yuv", 208, 144, 30, 1, 0, - 30); + 40); target_level_ = 0; + min_gf_internal_ = 4; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + ASSERT_EQ(11, level_); + + cfg_.rc_target_bitrate = 1600; ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + ASSERT_EQ(20, level_); } +// Test for level control being turned off TEST_P(LevelTest, TestTargetLevel255) { ::libvpx_test::I420VideoSource video("hantro_odd.yuv", 208, 144, 30, 1, 0, 30); @@ -98,7 +114,6 @@ TEST_P(LevelTest, TestTargetLevelApi) { VP9_INSTANTIATE_TEST_CASE(LevelTest, ::testing::Values(::libvpx_test::kTwoPassGood, - ::libvpx_test::kOnePassGood, - ::libvpx_test::kRealTime), + ::libvpx_test::kOnePassGood), ::testing::Range(0, 9)); } // namespace diff --git a/vp9/common/vp9_common.h b/vp9/common/vp9_common.h index 2aff132e6..908fa80a3 100644 --- a/vp9/common/vp9_common.h +++ b/vp9/common/vp9_common.h @@ -67,25 +67,6 @@ static INLINE int get_unsigned_bits(unsigned int num_values) { #define VP9_FRAME_MARKER 0x2 -typedef enum { - LEVEL_UNKNOWN = 0, - LEVEL_1 = 10, - LEVEL_1_1 = 11, - LEVEL_2 = 20, - LEVEL_2_1 = 21, - LEVEL_3 = 30, - LEVEL_3_1 = 31, - LEVEL_4 = 40, - LEVEL_4_1 = 41, - LEVEL_5 = 50, - LEVEL_5_1 = 51, - LEVEL_5_2 = 52, - LEVEL_6 = 60, - LEVEL_6_1 = 61, - LEVEL_6_2 = 62, - LEVEL_NOT_CARE = 255, -} VP9_LEVEL; - #ifdef __cplusplus } // extern "C" #endif diff --git a/vp9/common/vp9_onyxc_int.h b/vp9/common/vp9_onyxc_int.h index 1df6f0850..3fd935e62 100644 --- a/vp9/common/vp9_onyxc_int.h +++ b/vp9/common/vp9_onyxc_int.h @@ -168,8 +168,6 @@ typedef struct VP9Common { int allow_high_precision_mv; - int keep_level_stats; - // Flag signaling that the frame context should be reset to default values. // 0 or 1 implies don't reset, 2 reset just the context specified in the // frame header, 3 reset all contexts. diff --git a/vp9/encoder/vp9_bitstream.c b/vp9/encoder/vp9_bitstream.c index 61cca397b..73a2db09a 100644 --- a/vp9/encoder/vp9_bitstream.c +++ b/vp9/encoder/vp9_bitstream.c @@ -891,7 +891,7 @@ static void write_tile_info(const VP9_COMMON *const cm, vpx_wb_write_bit(wb, cm->log2_tile_rows != 1); } -static int get_refresh_mask(VP9_COMP *cpi) { +int vp9_get_refresh_mask(VP9_COMP *cpi) { if (vp9_preserve_existing_gf(cpi)) { // We have decided to preserve the previously existing golden frame as our // new ARF frame. However, in the short term we leave it in the GF slot and, @@ -1107,11 +1107,11 @@ static void write_uncompressed_header(VP9_COMP *cpi, write_bitdepth_colorspace_sampling(cm, wb); } - vpx_wb_write_literal(wb, get_refresh_mask(cpi), REF_FRAMES); + vpx_wb_write_literal(wb, vp9_get_refresh_mask(cpi), REF_FRAMES); write_frame_size(cm, wb); } else { MV_REFERENCE_FRAME ref_frame; - vpx_wb_write_literal(wb, get_refresh_mask(cpi), REF_FRAMES); + vpx_wb_write_literal(wb, vp9_get_refresh_mask(cpi), REF_FRAMES); for (ref_frame = LAST_FRAME; ref_frame <= ALTREF_FRAME; ++ref_frame) { assert(get_ref_frame_map_idx(cpi, ref_frame) != INVALID_IDX); vpx_wb_write_literal(wb, get_ref_frame_map_idx(cpi, ref_frame), diff --git a/vp9/encoder/vp9_bitstream.h b/vp9/encoder/vp9_bitstream.h index da6b41464..f24d20f31 100644 --- a/vp9/encoder/vp9_bitstream.h +++ b/vp9/encoder/vp9_bitstream.h @@ -18,6 +18,8 @@ extern "C" { #include "vp9/encoder/vp9_encoder.h" +int vp9_get_refresh_mask(VP9_COMP *cpi); + void vp9_pack_bitstream(VP9_COMP *cpi, uint8_t *dest, size_t *size); static INLINE int vp9_preserve_existing_gf(VP9_COMP *cpi) { diff --git a/vp9/encoder/vp9_encoder.c b/vp9/encoder/vp9_encoder.c index 82017940d..9bae1d382 100644 --- a/vp9/encoder/vp9_encoder.c +++ b/vp9/encoder/vp9_encoder.c @@ -86,6 +86,25 @@ FILE *kf_list; FILE *keyfile; #endif +static const Vp9LevelSpec vp9_level_defs[VP9_LEVELS] = { + {LEVEL_1, 829440, 36864, 200, 400, 2, 1, 4, 8}, + {LEVEL_1_1, 2764800, 73728, 800, 1000, 2, 1, 4, 8}, + {LEVEL_2, 4608000, 122880, 1800, 1500, 2, 1, 4, 8}, + {LEVEL_2_1, 9216000, 245760, 3600, 2800, 2, 2, 4, 8}, + {LEVEL_3, 20736000, 552960, 7200, 6000, 2, 4, 4, 8}, + {LEVEL_3_1, 36864000, 983040, 12000, 10000, 2, 4, 4, 8}, + {LEVEL_4, 83558400, 2228224, 18000, 16000, 4, 4, 4, 8}, + {LEVEL_4_1, 160432128, 2228224, 30000, 18000, 4, 4, 5, 6}, + {LEVEL_5, 311951360, 8912896, 60000, 36000, 6, 8, 6, 4}, + {LEVEL_5_1, 588251136, 8912896, 120000, 46000, 8, 8, 10, 4}, + // TODO(huisu): update max_cpb_size for level 5_2 ~ 6_2 when + // they are finalized (currently TBD). + {LEVEL_5_2, 1176502272, 8912896, 180000, 0, 8, 8, 10, 4}, + {LEVEL_6, 1176502272, 35651584, 180000, 0, 8, 16, 10, 4}, + {LEVEL_6_1, 2353004544u, 35651584, 240000, 0, 8, 16, 10, 4}, + {LEVEL_6_2, 4706009088u, 35651584, 480000, 0, 8, 16, 10, 4}, +}; + static INLINE void Scale2Ratio(VPX_SCALING mode, int *hr, int *hs) { switch (mode) { case NORMAL: @@ -159,6 +178,39 @@ static void apply_active_map(VP9_COMP *cpi) { } } +static void init_level_info(Vp9LevelInfo *level_info) { + Vp9LevelStats *const level_stats = &level_info->level_stats; + Vp9LevelSpec *const level_spec = &level_info->level_spec; + + memset(level_stats, 0, sizeof(*level_stats)); + memset(level_spec, 0, sizeof(*level_spec)); + level_spec->level = LEVEL_UNKNOWN; + level_spec->min_altref_distance = INT_MAX; +} + +VP9_LEVEL vp9_get_level(const Vp9LevelSpec * const level_spec) { + int i; + const Vp9LevelSpec *this_level; + + vpx_clear_system_state(); + + for (i = 0; i < VP9_LEVELS; ++i) { + this_level = &vp9_level_defs[i]; + if ((double)level_spec->max_luma_sample_rate * (1 + SAMPLE_RATE_GRACE_P) > + (double)this_level->max_luma_sample_rate || + level_spec->max_luma_picture_size > this_level->max_luma_picture_size || + level_spec->average_bitrate > this_level->average_bitrate || + level_spec->max_cpb_size > this_level->max_cpb_size || + level_spec->compression_ratio < this_level->compression_ratio || + level_spec->max_col_tiles > this_level->max_col_tiles || + level_spec->min_altref_distance < this_level->min_altref_distance || + level_spec->max_ref_frame_buffers > this_level->max_ref_frame_buffers) + continue; + break; + } + return (i == VP9_LEVELS) ? LEVEL_UNKNOWN : vp9_level_defs[i].level; +} + int vp9_set_active_map(VP9_COMP* cpi, unsigned char* new_map_16x16, int rows, @@ -783,7 +835,7 @@ static void init_config(struct VP9_COMP *cpi, VP9EncoderConfig *oxcf) { cm->color_range = oxcf->color_range; cpi->target_level = oxcf->target_level; - cm->keep_level_stats = oxcf->target_level != LEVEL_NOT_CARE; + cpi->keep_level_stats = oxcf->target_level != LEVEL_MAX; cm->width = oxcf->width; cm->height = oxcf->height; @@ -1476,7 +1528,7 @@ void vp9_change_config(struct VP9_COMP *cpi, const VP9EncoderConfig *oxcf) { cm->color_range = oxcf->color_range; cpi->target_level = oxcf->target_level; - cm->keep_level_stats = oxcf->target_level != LEVEL_NOT_CARE; + cpi->keep_level_stats = oxcf->target_level != LEVEL_MAX; if (cm->profile <= PROFILE_1) assert(cm->bit_depth == VPX_BITS_8); @@ -1660,7 +1712,6 @@ static void cal_nmvsadcosts_hp(int *mvsadcost[2]) { } while (++i <= MV_MAX); } - VP9_COMP *vp9_create_compressor(VP9EncoderConfig *oxcf, BufferPool *const pool) { unsigned int i; @@ -1749,6 +1800,9 @@ VP9_COMP *vp9_create_compressor(VP9EncoderConfig *oxcf, cpi->multi_arf_last_grp_enabled = 0; cpi->b_calculate_psnr = CONFIG_INTERNAL_STATS; + + init_level_info(&cpi->level_info); + #if CONFIG_INTERNAL_STATS cpi->b_calculate_ssimg = 0; cpi->b_calculate_blockiness = 1; @@ -2798,7 +2852,7 @@ void vp9_update_reference_frames(VP9_COMP *cpi) { } else if (vp9_preserve_existing_gf(cpi)) { // We have decided to preserve the previously existing golden frame as our // new ARF frame. However, in the short term in function - // vp9_bitstream.c::get_refresh_mask() we left it in the GF slot and, if + // vp9_get_refresh_mask() we left it in the GF slot and, if // we're updating the GF with the current decoded frame, we save it to the // ARF slot instead. // We now have to update the ARF with the current frame and swap gld_fb_idx @@ -4420,6 +4474,124 @@ static void adjust_image_stat(double y, double u, double v, double all, } #endif // CONFIG_INTERNAL_STATS +static void update_level_info(VP9_COMP *cpi, size_t *size, int arf_src_index) { + VP9_COMMON *const cm = &cpi->common; + Vp9LevelInfo *const level_info = &cpi->level_info; + Vp9LevelSpec *const level_spec = &level_info->level_spec; + Vp9LevelStats *const level_stats = &level_info->level_stats; + int i, idx; + uint64_t luma_samples, dur_end; + const uint32_t luma_pic_size = cm->width * cm->height; + double cpb_data_size; + + vpx_clear_system_state(); + + // update level_stats + level_stats->total_compressed_size += *size; + if (cm->show_frame) { + level_stats->total_uncompressed_size += + luma_pic_size + + 2 * (luma_pic_size >> (cm->subsampling_x + cm->subsampling_y)); + level_stats->time_encoded = + (cpi->last_end_time_stamp_seen - cpi->first_time_stamp_ever) / + (double)TICKS_PER_SEC; + } + + if (arf_src_index > 0) { + if (!level_stats->seen_first_altref) { + level_stats->seen_first_altref = 1; + } else if (level_stats->frames_since_last_altref < + level_spec->min_altref_distance) { + level_spec->min_altref_distance = level_stats->frames_since_last_altref; + } + level_stats->frames_since_last_altref = 0; + } else { + ++level_stats->frames_since_last_altref; + } + + if (level_stats->frame_window_buffer.len < FRAME_WINDOW_SIZE - 1) { + idx = (level_stats->frame_window_buffer.start + + level_stats->frame_window_buffer.len++) % FRAME_WINDOW_SIZE; + } else { + idx = level_stats->frame_window_buffer.start; + level_stats->frame_window_buffer.start = (idx + 1) % FRAME_WINDOW_SIZE; + } + level_stats->frame_window_buffer.buf[idx].ts = cpi->last_time_stamp_seen; + level_stats->frame_window_buffer.buf[idx].size = (uint32_t)(*size); + level_stats->frame_window_buffer.buf[idx].luma_samples = luma_pic_size; + + if (cm->frame_type == KEY_FRAME) { + level_stats->ref_refresh_map = 0; + } else { + int count = 0; + level_stats->ref_refresh_map |= vp9_get_refresh_mask(cpi); + // Also need to consider the case where the encoder refers to a buffer + // that has been implicitly refreshed after encoding a keyframe. + if (!cm->intra_only) { + level_stats->ref_refresh_map |= (1 << cpi->lst_fb_idx); + level_stats->ref_refresh_map |= (1 << cpi->gld_fb_idx); + level_stats->ref_refresh_map |= (1 << cpi->alt_fb_idx); + } + for (i = 0; i < REF_FRAMES; ++i) { + count += (level_stats->ref_refresh_map >> i) & 1; + } + if (count > level_spec->max_ref_frame_buffers) { + level_spec->max_ref_frame_buffers = count; + } + } + + // update average_bitrate + level_spec->average_bitrate = + (double)level_stats->total_compressed_size / 125.0 / + level_stats->time_encoded; + + // update max_luma_sample_rate + luma_samples = 0; + for (i = 0; i < level_stats->frame_window_buffer.len; ++i) { + idx = (level_stats->frame_window_buffer.start + + level_stats->frame_window_buffer.len - 1 - i) % FRAME_WINDOW_SIZE; + if (i == 0) { + dur_end = level_stats->frame_window_buffer.buf[idx].ts; + } + if (dur_end - level_stats->frame_window_buffer.buf[idx].ts >= + TICKS_PER_SEC) { + break; + } + luma_samples += level_stats->frame_window_buffer.buf[idx].luma_samples; + } + if (luma_samples > level_spec->max_luma_sample_rate) { + level_spec->max_luma_sample_rate = luma_samples; + } + + // update max_cpb_size + cpb_data_size = 0; + for (i = 0; i < CPB_WINDOW_SIZE; ++i) { + if (i >= level_stats->frame_window_buffer.len) break; + idx = (level_stats->frame_window_buffer.start + + level_stats->frame_window_buffer.len - 1 - i) % FRAME_WINDOW_SIZE; + cpb_data_size += level_stats->frame_window_buffer.buf[idx].size; + } + cpb_data_size = cpb_data_size / 125.0; + if (cpb_data_size > level_spec->max_cpb_size) { + level_spec->max_cpb_size = cpb_data_size; + } + + // update max_luma_picture_size + if (luma_pic_size > level_spec->max_luma_picture_size) { + level_spec->max_luma_picture_size = luma_pic_size; + } + + // update compression_ratio + level_spec->compression_ratio = + (double)level_stats->total_uncompressed_size * cm->bit_depth / + level_stats->total_compressed_size / 8.0; + + // update max_col_tiles + if (level_spec->max_col_tiles < (1 << cm->log2_tile_cols)) { + level_spec->max_col_tiles = (1 << cm->log2_tile_cols); + } +} + int vp9_get_compressed_data(VP9_COMP *cpi, unsigned int *frame_flags, size_t *size, uint8_t *dest, int64_t *time_stamp, int64_t *time_end, int flush) { @@ -4690,6 +4862,9 @@ int vp9_get_compressed_data(VP9_COMP *cpi, unsigned int *frame_flags, if (cpi->b_calculate_psnr && oxcf->pass != 1 && cm->show_frame) generate_psnr_packet(cpi); + if (cpi->keep_level_stats && oxcf->pass != 1) + update_level_info(cpi, size, arf_src_index); + #if CONFIG_INTERNAL_STATS if (oxcf->pass != 1) { diff --git a/vp9/encoder/vp9_encoder.h b/vp9/encoder/vp9_encoder.h index 6be61acb3..b65dfa8a6 100644 --- a/vp9/encoder/vp9_encoder.h +++ b/vp9/encoder/vp9_encoder.h @@ -20,6 +20,7 @@ #include "vpx_dsp/ssim.h" #endif #include "vpx_dsp/variance.h" +#include "vpx_ports/system_state.h" #include "vpx_util/vpx_thread.h" #include "vp9/common/vp9_alloccommon.h" @@ -51,6 +52,9 @@ extern "C" { #endif +// vp9 uses 10,000,000 ticks/second as time stamp +#define TICKS_PER_SEC 10000000 + typedef struct { int nmvjointcost[MV_JOINTS]; int nmvcosts[2][MV_VALS]; @@ -297,6 +301,69 @@ typedef struct IMAGE_STAT { double worst; } ImageStat; +#define CPB_WINDOW_SIZE 4 +#define FRAME_WINDOW_SIZE 128 +#define SAMPLE_RATE_GRACE_P 0.015 +#define VP9_LEVELS 14 + +typedef enum { + LEVEL_UNKNOWN = 0, + LEVEL_1 = 10, + LEVEL_1_1 = 11, + LEVEL_2 = 20, + LEVEL_2_1 = 21, + LEVEL_3 = 30, + LEVEL_3_1 = 31, + LEVEL_4 = 40, + LEVEL_4_1 = 41, + LEVEL_5 = 50, + LEVEL_5_1 = 51, + LEVEL_5_2 = 52, + LEVEL_6 = 60, + LEVEL_6_1 = 61, + LEVEL_6_2 = 62, + LEVEL_MAX = 255 +} VP9_LEVEL; + +typedef struct { + VP9_LEVEL level; + uint64_t max_luma_sample_rate; + uint32_t max_luma_picture_size; + double average_bitrate; // in kilobits per second + double max_cpb_size; // in kilobits + double compression_ratio; + uint8_t max_col_tiles; + uint32_t min_altref_distance; + uint8_t max_ref_frame_buffers; +} Vp9LevelSpec; + +typedef struct { + int64_t ts; // timestamp + uint32_t luma_samples; + uint32_t size; // in bytes +} FrameRecord; + +typedef struct { + FrameRecord buf[FRAME_WINDOW_SIZE]; + uint8_t start; + uint8_t len; +} FrameWindowBuffer; + +typedef struct { + uint8_t seen_first_altref; + uint32_t frames_since_last_altref; + uint64_t total_compressed_size; + uint64_t total_uncompressed_size; + double time_encoded; // in seconds + FrameWindowBuffer frame_window_buffer; + int ref_refresh_map; +} Vp9LevelStats; + +typedef struct { + Vp9LevelStats level_stats; + Vp9LevelSpec level_spec; +} Vp9LevelInfo; + typedef struct VP9_COMP { QUANTS quants; ThreadData td; @@ -519,6 +586,9 @@ typedef struct VP9_COMP { VPxWorker *workers; struct EncWorkerData *tile_thr_data; VP9LfSync lf_row_sync; + + int keep_level_stats; + Vp9LevelInfo level_info; } VP9_COMP; void vp9_initialize_enc(void); @@ -674,6 +744,8 @@ static INLINE int *cond_cost_list(const struct VP9_COMP *cpi, int *cost_list) { return cpi->sf.mv.subpel_search_method != SUBPEL_TREE ? cost_list : NULL; } +VP9_LEVEL vp9_get_level(const Vp9LevelSpec *const level_spec); + void vp9_new_framerate(VP9_COMP *cpi, double framerate); #define LAYER_IDS_TO_IDX(sl, tl, num_tl) ((sl) * (num_tl) + (tl)) diff --git a/vp9/vp9_cx_iface.c b/vp9/vp9_cx_iface.c index 1a11a6d5b..32c852292 100644 --- a/vp9/vp9_cx_iface.c +++ b/vp9/vp9_cx_iface.c @@ -205,7 +205,7 @@ static vpx_codec_err_t validate_config(vpx_codec_alg_priv_t *ctx, level != LEVEL_4 && level != LEVEL_4_1 && level != LEVEL_5 && level != LEVEL_5_1 && level != LEVEL_5_2 && level != LEVEL_6 && level != LEVEL_6_1 && level != LEVEL_6_2 && - level != LEVEL_UNKNOWN && level != LEVEL_NOT_CARE) + level != LEVEL_UNKNOWN && level != LEVEL_MAX) ERROR("target_level is invalid"); } @@ -807,6 +807,13 @@ static vpx_codec_err_t ctrl_set_target_level(vpx_codec_alg_priv_t *ctx, return update_extra_cfg(ctx, &extra_cfg); } +static vpx_codec_err_t ctrl_get_level(vpx_codec_alg_priv_t *ctx, va_list args) { + int *const arg = va_arg(args, int *); + if (arg == NULL) return VPX_CODEC_INVALID_PARAM; + *arg = (int)vp9_get_level(&ctx->cpi->level_info.level_spec); + return VPX_CODEC_OK; +} + static vpx_codec_err_t encoder_init(vpx_codec_ctx_t *ctx, vpx_codec_priv_enc_mr_cfg_t *data) { vpx_codec_err_t res = VPX_CODEC_OK; @@ -964,9 +971,6 @@ static int write_superframe_index(vpx_codec_alg_priv_t *ctx) { return index_sz; } -// vp9 uses 10,000,000 ticks/second as time stamp -#define TICKS_PER_SEC 10000000LL - static int64_t timebase_units_to_ticks(const vpx_rational_t *timebase, int64_t n) { return n * TICKS_PER_SEC * timebase->num / timebase->den; @@ -1547,6 +1551,7 @@ static vpx_codec_ctrl_fn_map_t encoder_ctrl_maps[] = { {VP9_GET_REFERENCE, ctrl_get_reference}, {VP9E_GET_SVC_LAYER_ID, ctrl_get_svc_layer_id}, {VP9E_GET_ACTIVEMAP, ctrl_get_active_map}, + {VP9E_GET_LEVEL, ctrl_get_level}, { -1, NULL}, }; diff --git a/vpx/vp8cx.h b/vpx/vp8cx.h index b059d47d1..2752a8627 100644 --- a/vpx/vp8cx.h +++ b/vpx/vp8cx.h @@ -562,7 +562,13 @@ enum vp8e_enc_control_id { * * Supported in codecs: VP9 */ - VP9E_SET_TARGET_LEVEL + VP9E_SET_TARGET_LEVEL, + + /*!\brief Codec control function to get bitstream level. + * + * Supported in codecs: VP9 + */ + VP9E_GET_LEVEL }; /*!\brief vpx 1-D scaling mode @@ -821,6 +827,9 @@ VPX_CTRL_USE_TYPE(VP9E_SET_RENDER_SIZE, int *) VPX_CTRL_USE_TYPE(VP9E_SET_TARGET_LEVEL, unsigned int) #define VPX_CTRL_VP9E_SET_TARGET_LEVEL +VPX_CTRL_USE_TYPE(VP9E_GET_LEVEL, int *) +#define VPX_CTRL_VP9E_GET_LEVEL + /*!\endcond */ /*! @} - end defgroup vp8_encoder */ #ifdef __cplusplus -- 2.40.0