Add vp9 encoder API VP9E_GET_LEVEL to provide bitstream level

Change-Id: I1ef3df0192491035728fe9d5eb25cc66dc2965de
This commit is contained in:
hui su 2016-05-05 12:04:42 -07:00
parent 8767999493
commit 72d4890caf
9 changed files with 294 additions and 37 deletions

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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),

View File

@ -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) {

View File

@ -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) {

View File

@ -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))

View File

@ -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},
};

View File

@ -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