// Creates a setjmp target using `CPI->common.error->jmp` and sets // `CPI->common.error->setjmp = 1`. Returns `CPI->common.error->error_code` on // longjmp. This macro expects `aom_codec_alg_priv_t *ctx` to be available. // This should be accompanied by a call to DISABLE_SETJMP using the same CPI // before going out of scope. #define ENABLE_SETJMP(CPI) \ do { \ struct aom_internal_error_info *const enable_setjmp_error = \
(CPI)->common.error; \
if (setjmp(enable_setjmp_error->jmp)) { \
enable_setjmp_error->setjmp = 0; \
ctx->base.err_detail = enable_setjmp_error->has_detail \
? enable_setjmp_error->detail \
: NULL; \ return enable_setjmp_error->error_code; \
} \
enable_setjmp_error->setjmp = 1; \
} while (0)
struct av1_extracfg {
int cpu_used; unsigned int enable_auto_alt_ref; unsigned int enable_auto_bwd_ref; unsigned int noise_sensitivity; unsigned int sharpness; unsigned int enable_adaptive_sharpness; unsigned int static_thresh; unsigned int row_mt; unsigned int fp_mt; unsigned int tile_columns; // log2 number of tile columns unsigned int tile_rows; // log2 number of tile rows unsigned int auto_tiles; unsigned int enable_tpl_model; unsigned int enable_keyframe_filtering; unsigned int arnr_max_frames; unsigned int arnr_strength; unsigned int min_gf_interval; unsigned int max_gf_interval; unsigned int gf_min_pyr_height; unsigned int gf_max_pyr_height;
aom_tune_metric tuning; const char *vmaf_model_path; const char *partition_info_path; unsigned int enable_rate_guide_deltaq; const char *rate_distribution_info;
aom_dist_metric dist_metric; unsigned int cq_level; // constrained quality level unsigned int rc_max_intra_bitrate_pct; unsigned int rc_max_inter_bitrate_pct; unsigned int gf_cbr_boost_pct; unsigned int lossless; unsigned int enable_cdef; unsigned int enable_restoration; unsigned int force_video_mode; unsigned int enable_obmc; unsigned int disable_trellis_quant; unsigned int enable_qm; unsigned int qm_y; unsigned int qm_u; unsigned int qm_v; unsigned int qm_min; unsigned int qm_max; unsigned int num_tg; unsigned int mtu_size;
aom_timing_info_type_t timing_info_type; unsigned int frame_parallel_decoding_mode;
int enable_dual_filter; unsigned int enable_chroma_deltaq;
AQ_MODE aq_mode;
DELTAQ_MODE deltaq_mode; unsigned int deltaq_strength;
int deltalf_mode; unsigned int frame_periodic_boost;
aom_tune_content content;
aom_color_primaries_t color_primaries;
aom_transfer_characteristics_t transfer_characteristics;
aom_matrix_coefficients_t matrix_coefficients;
aom_chroma_sample_position_t chroma_sample_position;
int color_range;
int render_width;
int render_height;
aom_superblock_size_t superblock_size; unsigned int single_tile_decoding;
int error_resilient_mode;
int s_frame_mode;
int film_grain_test_vector; const char *film_grain_table_filename; unsigned int motion_vector_unit_test; #if CONFIG_FPMT_TEST unsigned int fpmt_unit_test; #endif unsigned int cdf_update_mode;
int enable_rect_partitions; // enable rectangular partitions for sequence
int enable_ab_partitions; // enable AB partitions for sequence
int enable_1to4_partitions; // enable 1:4 and 4:1 partitions for sequence
int min_partition_size; // min partition size [4,8,16,32,64,128]
int max_partition_size; // max partition size [4,8,16,32,64,128]
int enable_intra_edge_filter; // enable intra-edge filter for sequence
int enable_order_hint; // enable order hint for sequence
int enable_tx64; // enable 64-pt transform usage for sequence
int enable_flip_idtx; // enable flip and identity transform types
int enable_rect_tx; // enable rectangular transform usage for sequence
int enable_dist_wtd_comp; // enable dist wtd compound for sequence
int max_reference_frames; // maximum number of references per frame
int enable_reduced_reference_set; // enable reduced set of references
int enable_ref_frame_mvs; // sequence level
int allow_ref_frame_mvs; // frame level
int enable_masked_comp; // enable masked compound for sequence
int enable_onesided_comp; // enable one sided compound for sequence
int enable_interintra_comp; // enable interintra compound for sequence
int enable_smooth_interintra; // enable smooth interintra mode usage
int enable_diff_wtd_comp; // enable diff-wtd compound usage
int enable_interinter_wedge; // enable interinter-wedge compound usage
int enable_interintra_wedge; // enable interintra-wedge compound usage
int enable_global_motion; // enable global motion usage for sequence
int enable_warped_motion; // sequence level
int allow_warped_motion; // frame level
int enable_filter_intra; // enable filter intra for sequence
int enable_smooth_intra; // enable smooth intra modes for sequence
int enable_paeth_intra; // enable Paeth intra mode for sequence
int enable_cfl_intra; // enable CFL uv intra mode for sequence
int enable_directional_intra; // enable directional modes for sequence
int enable_diagonal_intra; // enable D45 to D203 intra modes for sequence
int enable_superres;
int enable_overlay; // enable overlay for filtered arf frames
int enable_palette;
int enable_intrabc;
int enable_angle_delta; #if CONFIG_DENOISE
float noise_level;
int noise_block_size;
int enable_dnl_denoising; #endif
unsigned int enable_low_complexity_decode; unsigned int chroma_subsampling_x; unsigned int chroma_subsampling_y;
int reduced_tx_type_set;
int use_intra_dct_only;
int use_inter_dct_only;
int use_intra_default_tx_only;
int enable_tx_size_search;
int quant_b_adapt; unsigned int vbr_corpus_complexity_lap;
AV1_LEVEL target_seq_level_idx[MAX_NUM_OPERATING_POINTS]; // Bit mask to specify which tier each of the 32 possible operating points // conforms to. unsigned int tier_mask; // min_cr / 100 is the target minimum compression ratio for each frame. unsigned int min_cr;
COST_UPDATE_TYPE coeff_cost_upd_freq;
COST_UPDATE_TYPE mode_cost_upd_freq;
COST_UPDATE_TYPE mv_cost_upd_freq;
COST_UPDATE_TYPE dv_cost_upd_freq; unsigned int ext_tile_debug; unsigned int sb_multipass_unit_test; // Total number of passes. If this number is -1, then we assume passes = 1 or // 2 (passes = 1 if pass == AOM_RC_ONE_PASS and passes = 2 otherwise).
int passes;
int fwd_kf_dist;
LOOPFILTER_CONTROL loopfilter_control; // Indicates if the application of post-processing filters should be skipped // on reconstructed frame. unsigned int skip_postproc_filtering; // the name of the second pass output file when passes > 2 const char *two_pass_output; const char *second_pass_log; // Automatically determine whether to disable several intra tools // when "--deltaq-mode=3" is true. // Default as 0. // When set to 1, the encoder will analyze the reconstruction quality // as compared to the source image in the preprocessing pass. // If the recontruction quality is considered high enough, we disable // the following intra coding tools, for better encoding speed: // "--enable_smooth_intra", // "--enable_paeth_intra", // "--enable_cfl_intra", // "--enable_diagonal_intra".
int auto_intra_tools_off;
int strict_level_conformance;
int kf_max_pyr_height;
int sb_qp_sweep;
aom_screen_detection_mode screen_detection_mode; unsigned int validate_hbd_input;
};
// Called by encoder_encode() only. Must not be called by encoder_init() // because the `error` paramerer will be destroyed by aom_codec_enc_init_ver() // after encoder_init() returns an error. See the "IMPORTANT" comment in // aom_codec_enc_init_ver(). static aom_codec_err_t update_error_state(
aom_codec_alg_priv_t *ctx, conststruct aom_internal_error_info *error) { const aom_codec_err_t res = error->error_code;
// This function deep copies a string src to *dst. For default string we will // use a string literal, and otherwise we will allocate memory for the string. static aom_codec_err_t allocate_and_set_string(const char *src, const char *default_src, const char **dst,
char *err_detail) {
if (!src) {
snprintf(err_detail, ARG_ERR_MSG_MAX_LEN, "Null pointer given to a string parameter."); return AOM_CODEC_INVALID_PARAM;
}
if (*dst && strcmp(src, *dst) == 0) return AOM_CODEC_OK; // If the input is exactly the same as default, we will use the string // literal, so do not free here.
if (*dst != default_src) {
aom_free((void *)*dst);
}
if (default_src && strcmp(src, default_src) == 0) { // default_src should be a string literal
*dst = default_src;
} else {
size_t len = strlen(src) + 1;
char *tmp = aom_malloc(len * sizeof(*tmp));
if (!tmp) {
snprintf(err_detail, ARG_ERR_MSG_MAX_LEN, "Failed to allocate memory for copying parameters."); return AOM_CODEC_MEM_ERROR;
}
memcpy(tmp, src, len);
*dst = tmp;
} return0;
}
#undef ERROR #define ERROR(str) \ do { \
ctx->base.err_detail = str; \ return AOM_CODEC_INVALID_PARAM; \
} while (0)
#define RANGE_CHECK(p, memb, lo, hi) \ do { \
if (!((p)->memb >= (lo) && (p)->memb <= (hi))) \
ERROR(#memb" out of range ["#lo".."#hi"]"); \
} while (0)
#define RANGE_CHECK_HI(p, memb, hi) \ do { \
if (!((p)->memb <= (hi))) ERROR(#memb" out of range [.."#hi"]"); \
} while (0)
#define RANGE_CHECK_BOOL(p, memb) \ do { \
if (!!((p)->memb) != (p)->memb) ERROR(#memb" expected boolean"); \
} while (0)
static aom_codec_err_t validate_config(aom_codec_alg_priv_t *ctx, const aom_codec_enc_cfg_t *cfg, conststruct av1_extracfg *extra_cfg) {
RANGE_CHECK(cfg, g_w, 1, 65536); // 16 bits available
RANGE_CHECK(cfg, g_h, 1, 65536); // 16 bits available
RANGE_CHECK_HI(cfg, g_forced_max_frame_width, 65536); // 16 bits available
RANGE_CHECK_HI(cfg, g_forced_max_frame_height, 65536); // 16 bits available
if (cfg->g_forced_max_frame_width) {
RANGE_CHECK_HI(cfg, g_w, cfg->g_forced_max_frame_width);
}
if (cfg->g_forced_max_frame_height) {
RANGE_CHECK_HI(cfg, g_h, cfg->g_forced_max_frame_height);
} // To avoid integer overflows when multiplying width by height (or values // derived from width and height) using the int type, impose a maximum frame // area (width * height) of 2^30. constunsigned int max_frame_width =
cfg->g_forced_max_frame_width ? cfg->g_forced_max_frame_width : cfg->g_w; constunsigned int max_frame_height = cfg->g_forced_max_frame_height
? cfg->g_forced_max_frame_height
: cfg->g_h; const int64_t max_frame_area = (int64_t)max_frame_width * max_frame_height;
if (max_frame_area > (1 << 30)) {
ERROR("max_frame_area out of range [..2^30]");
}
RANGE_CHECK(cfg, g_timebase.den, 1, 1000000000);
RANGE_CHECK(cfg, g_timebase.num, 1, 1000000000);
RANGE_CHECK_HI(cfg, g_profile, MAX_PROFILES - 1);
if (extra_cfg->passes != -1 && (int)cfg->g_pass > extra_cfg->passes) {
ERROR("Current pass is larger than total number of passes.");
}
if (cfg->g_profile == (unsigned int)PROFILE_1 && cfg->monochrome) {
ERROR("Monochrome is not supported in profile 1");
}
if (cfg->g_profile <= (unsigned int)PROFILE_1 &&
cfg->g_bit_depth > AOM_BITS_10) {
ERROR("Codec bit-depth 12 not supported in profile < 2");
}
if (cfg->rc_end_usage == AOM_Q) {
RANGE_CHECK_HI(cfg, use_fixed_qp_offsets, 2);
} else {
if (cfg->use_fixed_qp_offsets > 0) {
ERROR("--use_fixed_qp_offsets can only be used with --end-usage=q");
}
}
RANGE_CHECK(extra_cfg, color_primaries, AOM_CICP_CP_BT_709,
AOM_CICP_CP_EBU_3213); // Need to check range more precisely to // check for reserved values?
RANGE_CHECK(extra_cfg, transfer_characteristics, AOM_CICP_TC_BT_709,
AOM_CICP_TC_HLG);
RANGE_CHECK(extra_cfg, matrix_coefficients, AOM_CICP_MC_IDENTITY,
AOM_CICP_MC_YCGCO_RO);
RANGE_CHECK(extra_cfg, color_range, 0, 1);
/* Average corpus complexity is supported only in the case of single pass
* VBR*/
if (cfg->g_pass == AOM_RC_ONE_PASS && cfg->rc_end_usage == AOM_VBR)
RANGE_CHECK_HI(extra_cfg, vbr_corpus_complexity_lap,
MAX_VBR_CORPUS_COMPLEXITY); else if (extra_cfg->vbr_corpus_complexity_lap != 0)
ERROR( "VBR corpus complexity is supported only in the case of single pass " "VBR mode.");
#if !CONFIG_TUNE_BUTTERAUGLI
if (extra_cfg->tuning == AOM_TUNE_BUTTERAUGLI) {
ERROR( "This error may be related to the wrong configuration options: try to " "set -DCONFIG_TUNE_BUTTERAUGLI=1 at the time CMake is run.");
} #endif
#if !CONFIG_TUNE_VMAF
if (extra_cfg->tuning >= AOM_TUNE_VMAF_WITH_PREPROCESSING &&
extra_cfg->tuning <= AOM_TUNE_VMAF_NEG_MAX_GAIN) {
ERROR( "This error may be related to the wrong configuration options: try to " "set -DCONFIG_TUNE_VMAF=1 at the time CMake is run.");
} #endif
if (extra_cfg->lossless) {
if (extra_cfg->aq_mode != 0)
ERROR("Only --aq_mode=0 can be used with --lossless=1.");
if (extra_cfg->enable_chroma_deltaq)
ERROR("Only --enable_chroma_deltaq=0 can be used with --lossless=1.");
}
RANGE_CHECK_HI(extra_cfg, enable_low_complexity_decode, 1);
RANGE_CHECK_HI(extra_cfg, chroma_subsampling_x, 1);
RANGE_CHECK_HI(extra_cfg, chroma_subsampling_y, 1); // 6.4.2 Color config semantics // If matrix_coefficients is equal to MC_IDENTITY, it is a requirement of // bitstream conformance that subsampling_x is equal to 0 and subsampling_y // is equal to 0.
if (extra_cfg->matrix_coefficients == AOM_CICP_MC_IDENTITY &&
(extra_cfg->chroma_subsampling_x != 0 ||
extra_cfg->chroma_subsampling_y != 0)) {
ERROR("Subsampling must be 0 with AOM_CICP_MC_IDENTITY.");
}
for (int i = 0; i < MAX_NUM_OPERATING_POINTS; ++i) { const int level_idx = extra_cfg->target_seq_level_idx[i];
if (!is_valid_seq_level_idx(level_idx) &&
level_idx != SEQ_LEVEL_KEEP_STATS) {
ERROR("Target sequence level index is invalid");
}
}
RANGE_CHECK(extra_cfg, kf_max_pyr_height, -1, 5);
if (extra_cfg->kf_max_pyr_height != -1 &&
extra_cfg->kf_max_pyr_height < (int)extra_cfg->gf_min_pyr_height) {
ERROR( "The value of kf-max-pyr-height should not be smaller than " "gf-min-pyr-height");
}
static aom_codec_err_t validate_img(aom_codec_alg_priv_t *ctx, const aom_image_t *img) { switch (img->fmt) { case AOM_IMG_FMT_YV12: case AOM_IMG_FMT_NV12: case AOM_IMG_FMT_I420: case AOM_IMG_FMT_YV1216: case AOM_IMG_FMT_I42016: break; case AOM_IMG_FMT_I444: case AOM_IMG_FMT_I44416:
if (ctx->cfg.g_profile == (unsigned int)PROFILE_0 &&
!ctx->cfg.monochrome) {
ERROR("Invalid image format. I444 images not supported in profile.");
} break; case AOM_IMG_FMT_I422: case AOM_IMG_FMT_I42216:
if (ctx->cfg.g_profile != (unsigned int)PROFILE_2) {
ERROR("Invalid image format. I422 images not supported in profile.");
} break; default:
ERROR( "Invalid image format. Only YV12, NV12, I420, I422, I444 images are " "supported."); break;
}
if (img->d_w != ctx->cfg.g_w || img->d_h != ctx->cfg.g_h)
ERROR("Image size must match encoder init configuration size");
assert(img->fmt & AOM_IMG_FMT_PLANAR);
if (!ctx->cfg.monochrome && img->fmt != AOM_IMG_FMT_NV12 &&
img->stride[AOM_PLANE_U] != img->stride[AOM_PLANE_V])
ERROR("Image U/V strides must match");
// 6.4.2 Color config semantics // If matrix_coefficients is equal to MC_IDENTITY, it is a requirement of // bitstream conformance that subsampling_x is equal to 0 and subsampling_y // is equal to 0.
if (ctx->oxcf.color_cfg.matrix_coefficients == AOM_CICP_MC_IDENTITY &&
(img->x_chroma_shift != 0 || img->y_chroma_shift != 0)) {
ERROR("Subsampling must be 0 with AOM_CICP_MC_IDENTITY.");
}
#if CONFIG_TUNE_BUTTERAUGLI
if (ctx->extra_cfg.tuning == AOM_TUNE_BUTTERAUGLI) {
if (img->bit_depth > 8) {
ERROR("Only 8 bit depth images supported in tune=butteraugli mode.");
}
if (img->mc != 0 && img->mc != AOM_CICP_MC_BT_709 &&
img->mc != AOM_CICP_MC_BT_601 && img->mc != AOM_CICP_MC_BT_470_B_G) {
ERROR( "Only BT.709 and BT.601 matrix coefficients supported in " "tune=butteraugli mode. Identity matrix is treated as BT.601.");
}
} #endif
#if CONFIG_AV1_HIGHBITDEPTH
if (ctx->extra_cfg.validate_hbd_input &&
(img->fmt & AOM_IMG_FMT_HIGHBITDEPTH)) { constunsigned int bit_depth = ctx->cfg.g_bit_depth; const int max_val = 1 << bit_depth; // Note there is no high bitdepth version of NV12 defined. If one is // added, `num_planes` should be 2 in that case. const int num_planes = img->monochrome ? 1 : 3;
for (int plane = 0; plane < num_planes; ++plane) { constunsignedshort *src = (constunsignedshort *)img->planes[plane]; constunsigned int stride = img->stride[plane] / 2; constunsigned int ph = aom_img_plane_height(img, plane); constunsigned int pw = aom_img_plane_width(img, plane);
for (unsigned int i = 0; i < ph; ++i) {
for (unsigned int j = 0; j < pw; ++j) {
if (src[j] >= max_val) { return AOM_CODEC_INVALID_PARAM;
}
}
src += stride;
}
}
} #endif// CONFIG_AV1_HIGHBITDEPTH
return AOM_CODEC_OK;
}
static int get_image_bps(const aom_image_t *img) { switch (img->fmt) { case AOM_IMG_FMT_YV12: case AOM_IMG_FMT_NV12: case AOM_IMG_FMT_I420: return12; case AOM_IMG_FMT_I422: return16; case AOM_IMG_FMT_I444: return24; case AOM_IMG_FMT_YV1216: case AOM_IMG_FMT_I42016: return24; case AOM_IMG_FMT_I42216: return32; case AOM_IMG_FMT_I44416: return48; default: assert(0 && "Invalid image format"); break;
} return0;
}
staticvoid set_auto_tiles(TileConfig *const tile_cfg, unsigned int width, unsigned int height, unsigned int threads) {
int tile_cols_log2 = 0;
int tile_rows_log2 = 0;
if (threads < 2) return; // Avoid small tiles because they are particularly bad for coding. // Use no more tiles than the number of threads. Aim for one tile per // thread. Using more than one thread inside one tile could be less // efficient. Using more tiles than the number of threads would result // in a compression penalty without much benefit. const uint32_t kMinTileArea = 128 * 128; const uint32_t kMaxTiles = 32;
uint32_t frame_area = width * height;
uint32_t tiles = (frame_area + kMinTileArea - 1) / kMinTileArea;
if (tiles > kMaxTiles) {
tiles = kMaxTiles;
}
if (tiles > threads) {
tiles = threads;
}
int tiles_log2 = (int)log2(tiles); // If the frame width is equal or greater than the height, use more tile // columns than tile rows.
if (width >= height) {
tile_cols_log2 = (tiles_log2 + 1) / 2;
tile_rows_log2 = tiles_log2 - tile_cols_log2;
} else {
tile_rows_log2 = (tiles_log2 + 1) / 2;
tile_cols_log2 = tiles_log2 - tile_rows_log2;
}
tile_cfg->tile_columns = tile_cols_log2;
tile_cfg->tile_rows = tile_rows_log2;
}
// Explicitly disable global motion in a few cases: // * For realtime mode, we never search global motion, and disabling // it here prevents later code from allocating buffers we don't need // * For large scale tile mode, some of the intended use cases expect // all frame headers to be identical. This breaks if global motion is // used, since global motion data is stored in the frame header. // eg, see test/lightfield_test.sh, which checks that all frame headers // are the same.
tool_cfg->enable_global_motion = extra_cfg->enable_global_motion &&
cfg->g_usage != AOM_USAGE_REALTIME &&
!cfg->large_scale_tile;
oxcf->speed = extra_cfg->cpu_used;
if (oxcf->mode == REALTIME) { #if CONFIG_REALTIME_ONLY
oxcf->speed = AOMMAX(oxcf->speed, 5); #endif // TODO(yunqingwang, any) In REALTIME mode, 1080p performance at speed 5 & 6 // is quite bad. Force to use speed 7 for now. Will investigate it when we // work on rd path optimization later.
if (AOMMIN(cfg->g_w, cfg->g_h) >= 1080 && oxcf->speed < 7) oxcf->speed = 7;
}
// Now, low complexity decode mode supports good-quality encoding (speed 1 to // 3) for vertical videos (608p to 1080p) and horizontal videos (720p to // 1080p). This can be further modified if needed. const int is_low_complexity_decode_mode_supported =
(cfg->g_usage == AOM_USAGE_GOOD_QUALITY) &&
(oxcf->speed >= 1 && oxcf->speed <= 3) &&
(AOMMIN(cfg->g_w, cfg->g_h) >= 608 && AOMMIN(cfg->g_w, cfg->g_h) <= 1080);
oxcf->enable_low_complexity_decode =
extra_cfg->enable_low_complexity_decode &&
is_low_complexity_decode_mode_supported;
// Set Color related configuration.
color_cfg->color_primaries = extra_cfg->color_primaries;
color_cfg->transfer_characteristics = extra_cfg->transfer_characteristics;
color_cfg->matrix_coefficients = extra_cfg->matrix_coefficients;
color_cfg->color_range = extra_cfg->color_range;
color_cfg->chroma_sample_position = extra_cfg->chroma_sample_position;
// Set Group of frames configuration. #if CONFIG_REALTIME_ONLY // When CONFIG_REALTIME_ONLY=1 and mode=REALTIME, then force lag_in_frames // = 0.
gf_cfg->lag_in_frames = (oxcf->mode == REALTIME)
? 0
: clamp(cfg->g_lag_in_frames, 0, MAX_LAG_BUFFERS); #else
gf_cfg->lag_in_frames = clamp(cfg->g_lag_in_frames, 0, MAX_LAG_BUFFERS); #endif
// Modify lag_in_frames slightly for better coding performance (e.g. better // temporal filtering result).
if (oxcf->mode == GOOD && gf_cfg->lag_in_frames >= 32 &&
gf_cfg->lag_in_frames < 39)
gf_cfg->lag_in_frames = AOMMIN(39, MAX_LAG_BUFFERS);
#if CONFIG_AV1_TEMPORAL_DENOISING // Temporal denoiser is for nonrd pickmode so disable it for speed < 7. // Also disable it for speed 7 for now since it needs to be modified for // the check_partition_merge_mode feature.
if (cfg->g_bit_depth == AOM_BITS_8 && oxcf->speed > 7) {
oxcf->noise_sensitivity = extra_cfg->noise_sensitivity;
} else {
oxcf->noise_sensitivity = 0;
} #endif // Set Tile related configuration.
tile_cfg->num_tile_groups = extra_cfg->num_tg; // In large-scale tile encoding mode, num_tile_groups is always 1.
if (cfg->large_scale_tile) tile_cfg->num_tile_groups = 1;
tile_cfg->mtu = extra_cfg->mtu_size;
tile_cfg->enable_large_scale_tile = cfg->large_scale_tile;
tile_cfg->enable_single_tile_decoding =
(tile_cfg->enable_large_scale_tile) ? extra_cfg->single_tile_decoding : 0;
if (extra_cfg->auto_tiles) {
set_auto_tiles(tile_cfg, cfg->g_w, cfg->g_h, cfg->g_threads);
extra_cfg->tile_columns = tile_cfg->tile_columns;
extra_cfg->tile_rows = tile_cfg->tile_rows;
} else {
tile_cfg->tile_columns = extra_cfg->tile_columns;
tile_cfg->tile_rows = extra_cfg->tile_rows;
}
tile_cfg->tile_width_count = AOMMIN(cfg->tile_width_count, MAX_TILE_COLS);
tile_cfg->tile_height_count = AOMMIN(cfg->tile_height_count, MAX_TILE_ROWS);
for (int i = 0; i < tile_cfg->tile_width_count; i++) {
tile_cfg->tile_widths[i] = cfg->tile_widths[i];
}
for (int i = 0; i < tile_cfg->tile_height_count; i++) {
tile_cfg->tile_heights[i] = cfg->tile_heights[i];
}
tile_cfg->enable_ext_tile_debug = extra_cfg->ext_tile_debug;
if (tile_cfg->enable_large_scale_tile) { // The superblock_size can only be AOM_SUPERBLOCK_SIZE_64X64 or // AOM_SUPERBLOCK_SIZE_128X128 while tile_cfg->enable_large_scale_tile = 1. // If superblock_size = AOM_SUPERBLOCK_SIZE_DYNAMIC, hard set it to // AOM_SUPERBLOCK_SIZE_64X64(default value in large_scale_tile).
if (extra_cfg->superblock_size != AOM_SUPERBLOCK_SIZE_64X64 &&
extra_cfg->superblock_size != AOM_SUPERBLOCK_SIZE_128X128)
tool_cfg->superblock_size = AOM_SUPERBLOCK_SIZE_64X64;
}
// Set reference frame related configuration.
oxcf->ref_frm_cfg.max_reference_frames = extra_cfg->max_reference_frames;
oxcf->ref_frm_cfg.enable_reduced_reference_set =
extra_cfg->enable_reduced_reference_set;
oxcf->ref_frm_cfg.enable_onesided_comp = extra_cfg->enable_onesided_comp;
if (input_cfg->limit == 1) { // still picture mode, display model and timing is meaningless
dec_model_cfg->display_model_info_present_flag = 0;
dec_model_cfg->timing_info_present = 0;
}
oxcf->save_as_annexb = cfg->save_as_annexb;
// Set unit test related configuration.
oxcf->unit_test_cfg.motion_vector_unit_test =
extra_cfg->motion_vector_unit_test;
oxcf->unit_test_cfg.sb_multipass_unit_test =
extra_cfg->sb_multipass_unit_test;
if (cfg->g_w != ctx->cfg.g_w || cfg->g_h != ctx->cfg.g_h) {
if (cfg->g_lag_in_frames > 1 || cfg->g_pass != AOM_RC_ONE_PASS)
ERROR("Cannot change width or height after initialization"); // Note: function encoder_set_config() is allowed to be called multiple // times. However, when the original frame width or height is less than two // times of the new frame width or height, a forced key frame should be // used (for the case of single spatial layer, since otherwise a previous // encoded frame at a lower layer may be the desired reference). To make // sure the correct detection of a forced key frame, we need // to update the frame width and height only when the actual encoding is // performed. cpi->last_coded_width and cpi->last_coded_height are used to // track the actual coded frame size.
if (ctx->ppi->cpi->svc.number_spatial_layers == 1 &&
ctx->ppi->cpi->last_coded_width && ctx->ppi->cpi->last_coded_height &&
(!valid_ref_frame_size(ctx->ppi->cpi->last_coded_width,
ctx->ppi->cpi->last_coded_height, cfg->g_w,
cfg->g_h) ||
((int)cfg->g_w > ctx->ppi->cpi->last_coded_width) ||
((int)cfg->g_h > ctx->ppi->cpi->last_coded_height))) {
force_key = 1;
}
}
if (ctx->monochrome_on_init && cfg->monochrome == 0) { // TODO(aomedia:3465): Allow this case to work without requiring re-init // of encoder.
ERROR("Cannot change to monochrome = 0 after init with monochrome");
}
// Prevent increasing lag_in_frames. This check is stricter than it needs // to be -- the limit is not increasing past the first lag_in_frames // value, but we don't track the initial config, only the last successful // config.
if (cfg->g_lag_in_frames > ctx->cfg.g_lag_in_frames)
ERROR("Cannot increase lag_in_frames"); // Prevent changing lag_in_frames if Lookahead Processing is enabled
if (cfg->g_lag_in_frames != ctx->cfg.g_lag_in_frames &&
ctx->num_lap_buffers > 0)
ERROR("Cannot change lag_in_frames if LAP is enabled");
res = validate_config(ctx, cfg, &ctx->extra_cfg);
if (res == AOM_CODEC_OK) {
ctx->cfg = *cfg;
set_encoder_config(&ctx->oxcf, &ctx->cfg, &ctx->extra_cfg); // On profile change, request a key frame
force_key |= ctx->ppi->seq_params.profile != ctx->oxcf.profile; bool is_sb_size_changed = false;
av1_change_config_seq(ctx->ppi, &ctx->oxcf, &is_sb_size_changed);
for (int i = 0; i < ctx->ppi->num_fp_contexts; i++) {
AV1_COMP *const cpi = ctx->ppi->parallel_cpi[i];
ENABLE_SETJMP(cpi);
av1_change_config(cpi, &ctx->oxcf, is_sb_size_changed);
DISABLE_SETJMP(cpi);
}
if (ctx->ppi->cpi_lap != NULL) {
AV1_COMP *const cpi = ctx->ppi->cpi_lap;
ENABLE_SETJMP(cpi);
av1_change_config(cpi, &ctx->oxcf, is_sb_size_changed);
DISABLE_SETJMP(cpi);
}
}
if (force_key) ctx->next_frame_flags |= AOM_EFLAG_FORCE_KF;
static aom_codec_err_t ctrl_set_tile_columns(aom_codec_alg_priv_t *ctx,
va_list args) { // If the control AUTO_TILES is used (set to 1) then don't override // the tile_columns set via the AUTO_TILES control.
if (ctx->extra_cfg.auto_tiles) {
ERROR("AUTO_TILES is set so AV1E_SET_TILE_COLUMNS should not be called.");
} struct av1_extracfg extra_cfg = ctx->extra_cfg; unsigned int tile_columns = CAST(AV1E_SET_TILE_COLUMNS, args);
if (tile_columns == extra_cfg.tile_columns) return AOM_CODEC_OK;
extra_cfg.tile_columns = tile_columns; return update_extra_cfg(ctx, &extra_cfg);
}
static aom_codec_err_t ctrl_set_tile_rows(aom_codec_alg_priv_t *ctx,
va_list args) { // If the control AUTO_TILES is used (set to 1) then don't override // the tile_rows set via the AUTO_TILES control.
if (ctx->extra_cfg.auto_tiles) {
ERROR("AUTO_TILES is set so AV1E_SET_TILE_ROWS should not be called.");
} struct av1_extracfg extra_cfg = ctx->extra_cfg; unsigned int tile_rows = CAST(AV1E_SET_TILE_ROWS, args);
if (tile_rows == extra_cfg.tile_rows) return AOM_CODEC_OK;
extra_cfg.tile_rows = tile_rows; return update_extra_cfg(ctx, &extra_cfg);
}
static aom_codec_err_t ctrl_set_enable_tpl_model(aom_codec_alg_priv_t *ctx,
va_list args) { struct av1_extracfg extra_cfg = ctx->extra_cfg; constunsigned int tpl_model_arg = CAST(AV1E_SET_ENABLE_TPL_MODEL, args); #if CONFIG_REALTIME_ONLY
if (tpl_model_arg) {
ERROR("TPL model can't be turned on in realtime only build.");
} #endif
extra_cfg.enable_tpl_model = tpl_model_arg; return update_extra_cfg(ctx, &extra_cfg);
}
static aom_codec_err_t handle_tuning(struct av1_extracfg *extra_cfg) {
if (extra_cfg->tuning == AOM_TUNE_IQ ||
extra_cfg->tuning == AOM_TUNE_SSIMULACRA2) { // Enable QMs as they've been found to be beneficial for images, when used // with alternative QM formulas: // - aom_get_qmlevel_allintra() // - aom_get_qmlevel_luma_ssimulacra2() // - aom_get_qmlevel_444_chroma()
extra_cfg->enable_qm = 1;
extra_cfg->qm_min = QM_FIRST_IQ_SSIMULACRA2;
extra_cfg->qm_max = QM_LAST_IQ_SSIMULACRA2; // Sharpness has been found to be beneficial for images (better perceptual // quality).
extra_cfg->sharpness = 7; // Using the QM-PSNR metric was found to be beneficial for images (over the // default PSNR metric), as it correlates better with subjective image // quality consistency and better SSIMULACRA 2 scores.
extra_cfg->dist_metric = AOM_DIST_METRIC_QM_PSNR; // CDEF_ALL has been found to blur images at medium and high quality // qindexes, so let's use a version that adapts CDEF strength on frame // qindexes. CDEF_ADAPTIVE strengths look like this for varying qindexes: // - CDEF off: 0 - 32 // - Reduced strength: 33 - 220 // - Full strength: 221 - 255
extra_cfg->enable_cdef = CDEF_ADAPTIVE; // Enable chroma deltaq so the encoder can factor in chroma subsampling and // adjust chroma quality when necessary.
extra_cfg->enable_chroma_deltaq = 1; // Enable "Variance Boost" deltaq mode, optimized for images.
extra_cfg->deltaq_mode = DELTA_Q_VARIANCE_BOOST; // Enable "anti-aliased text and graphics aware" screen detection mode.
extra_cfg->screen_detection_mode = AOM_SCREEN_DETECTION_ANTIALIASING_AWARE;
}
if (extra_cfg->tuning == AOM_TUNE_IQ) { // Enable adaptive sharpness to adjust loop filter levels according to QP. // Takes a small SSIMULACRA2 hit on the lower quality end, so enable it // just for tune IQ.
extra_cfg->enable_adaptive_sharpness = 1;
} return AOM_CODEC_OK;
}
// Skip AQ mode if using fixed QP for current frame.
if (ctx->ppi->cpi->rc.use_external_qp_one_pass) extra_cfg.aq_mode = 0;
return update_extra_cfg(ctx, &extra_cfg);
}
// Update the reference to the config structure to an internal copy.
assert(ctx->config.enc);
priv->cfg = *ctx->config.enc;
ctx->config.enc = &priv->cfg;
priv->extra_cfg = default_extra_cfg;
// Special handling:
// By default, if omitted: --enable-cdef=1, --screen-detection-mode=1,
// --qm-min=5, and --qm-max=9.
// Here we set its default values to --enable-cdef=0,
// --screen-detection-mode=2, --qm-min=4, and --qm-max=10 when --allintra
// is turned on.
// However, if users set --enable-cdef, --screen-detection-mode, --qm-min,
// or --qm-max, either from the command line or aom_codec_control(), the
// encoder still respects it.
if (priv->cfg.g_usage == AOM_USAGE_ALL_INTRA) {
// CDEF has been found to blur images, so it's disabled in all-intra mode
priv->extra_cfg.enable_cdef = 0;
// Enable "anti-aliased text and graphics aware" screen detection mode.
priv->extra_cfg.screen_detection_mode =
AOM_SCREEN_DETECTION_ANTIALIASING_AWARE;
// These QM min/max values have been found to be beneficial for images,
// when used with an alternative QM formula (see
// aom_get_qmlevel_allintra()).
// These values could also be beneficial for other usage modes, but
// further testing is required.
priv->extra_cfg.qm_min = DEFAULT_QM_FIRST_ALLINTRA;
priv->extra_cfg.qm_max = DEFAULT_QM_LAST_ALLINTRA;
}
av1_initialize_enc(priv->cfg.g_usage, priv->cfg.rc_end_usage);
res = validate_config(priv, &priv->cfg, &priv->extra_cfg);
if (res == AOM_CODEC_OK) {
int *num_lap_buffers = &priv->num_lap_buffers;
int lap_lag_in_frames = 0;
*num_lap_buffers = 0;
priv->timestamp_ratio.den = priv->cfg.g_timebase.den;
priv->timestamp_ratio.num =
(int64_t)priv->cfg.g_timebase.num * TICKS_PER_SEC;
reduce_ratio(&priv->timestamp_ratio);
set_encoder_config(&priv->oxcf, &priv->cfg, &priv->extra_cfg);
if (priv->oxcf.pass == AOM_RC_ONE_PASS) {
// Enable look ahead.
#if CONFIG_REALTIME_ONLY
// When CONFIG_REALTIME_ONLY=1 and mode=REALTIME, then force
// lag_in_frames = 0.
const int lag_in_frames =
(priv->oxcf.mode == REALTIME) ? 0 : (int)priv->cfg.g_lag_in_frames;
#else
const int lag_in_frames = (int)priv->cfg.g_lag_in_frames;
#endif
*num_lap_buffers =
AOMMIN(lag_in_frames,
AOMMIN(MAX_LAP_BUFFERS, priv->oxcf.kf_cfg.key_freq_max +
SCENE_CUT_KEY_TEST_INTERVAL));
if ((int)priv->cfg.g_lag_in_frames - (*num_lap_buffers) >=
LAP_LAG_IN_FRAMES) {
lap_lag_in_frames = LAP_LAG_IN_FRAMES;
}
}
priv->oxcf.use_highbitdepth =
(ctx->init_flags & AOM_CODEC_USE_HIGHBITDEPTH) ? 1 : 0;
priv->monochrome_on_init = priv->cfg.monochrome;
priv->ppi = av1_create_primary_compressor(&priv->pkt_list.head,
*num_lap_buffers, &priv->oxcf);
if (!priv->ppi) return AOM_CODEC_MEM_ERROR;
#if !CONFIG_REALTIME_ONLY
res = create_stats_buffer(&priv->frame_stats_buffer,
&priv->stats_buf_context, *num_lap_buffers);
if (res != AOM_CODEC_OK) return res;
assert(MAX_LAP_BUFFERS >= MAX_LAG_BUFFERS);
int size = get_stats_buf_size(*num_lap_buffers, MAX_LAG_BUFFERS);
for (int i = 0; i < size; i++)
priv->ppi->twopass.frame_stats_arr[i] = &priv->frame_stats_buffer[i];
// Due to the presence of no-show frames, the ctx->cx_data buffer holds
// compressed data corresponding to multiple frames. As no-show frames are
// not possible for all intra frame encoding with no forward key frames,
// the buffer is allocated with a smaller size in this case.
//
// For pseudo random input, the compressed frame size is seen to exceed
// the uncompressed frame size, but is less than 2 times the uncompressed
// frame size. Hence the size of the buffer is chosen as 2 times the
// uncompressed frame size.
int multiplier = 8;
if (ppi->cpi->oxcf.kf_cfg.key_freq_max == 0 &&
!ppi->cpi->oxcf.kf_cfg.fwd_kf_enabled)
multiplier = 2;
if (uncompressed_frame_sz > SIZE_MAX / multiplier)
return AOM_CODEC_MEM_ERROR;
size_t data_sz = uncompressed_frame_sz * multiplier;
if (data_sz < kMinCompressedSize) data_sz = kMinCompressedSize;
if (ctx->cx_data == NULL || ctx->cx_data_sz < data_sz) {
ctx->cx_data_sz = data_sz;
free(ctx->cx_data);
ctx->cx_data = (unsigned char *)malloc(ctx->cx_data_sz);
if (ctx->cx_data == NULL) {
ctx->cx_data_sz = 0;
return AOM_CODEC_MEM_ERROR;
}
}
for (int i = 0; i < ppi->num_fp_contexts - 1; i++) {
if (ppi->parallel_frames_data[i].cx_data == NULL ||
ppi->parallel_frames_data[i].cx_data_sz < data_sz) {
ppi->parallel_frames_data[i].cx_data_sz = data_sz;
free(ppi->parallel_frames_data[i].cx_data);
ppi->parallel_frames_data[i].frame_size = 0;
ppi->parallel_frames_data[i].cx_data =
(unsigned char *)malloc(ppi->parallel_frames_data[i].cx_data_sz);
if (ppi->parallel_frames_data[i].cx_data == NULL) {
ppi->parallel_frames_data[i].cx_data_sz = 0;
return AOM_CODEC_MEM_ERROR;
}
}
}
}
}
aom_codec_pkt_list_init(&ctx->pkt_list);
volatile aom_enc_frame_flags_t flags = enc_flags;
// The jmp_buf is valid only for the duration of the function that calls
// setjmp(). Therefore, this function must reset the 'setjmp' field to 0
// before it returns.
if (setjmp(ppi->error.jmp)) {
ppi->error.setjmp = 0;
res = update_error_state(ctx, &ppi->error);
return res;
}
ppi->error.setjmp = 1;
// Note(yunqing): While applying encoding flags, always start from enabling
// all, and then modifying according to the flags. Previous frame's flags are
// overwritten.
av1_apply_encoding_flags(ppi->cpi, flags);
if (cpi_lap != NULL) {
av1_apply_encoding_flags(cpi_lap, flags);
}
if (res == AOM_CODEC_OK) {
AV1_COMP *cpi = ppi->cpi;
// Per-frame PSNR is not supported when g_lag_in_frames is greater than 0.
if ((flags & AOM_EFLAG_CALCULATE_PSNR) && ctx->cfg.g_lag_in_frames != 0) {
aom_internal_error(
&ppi->error, AOM_CODEC_INCAPABLE,
"Cannot calculate per-frame PSNR when g_lag_in_frames is nonzero");
}
// Don't attempt to freeze internal state when lag is non-zero in order to
// minimize risk of state leaking when e.g. multi-pass and b-frames are
// used.
if ((flags & AOM_EFLAG_FREEZE_INTERNAL_STATE) &&
ctx->cfg.g_lag_in_frames != 0) {
aom_internal_error(
&ppi->error, AOM_CODEC_INCAPABLE,
"Cannot freeze internal state when g_lag_in_frames is nonzero");
}
if (img != NULL) {
if (!ctx->pts_offset_initialized) {
ctx->pts_offset = ptsvol;
ctx->pts_offset_initialized = 1;
}
if (ptsvol < ctx->pts_offset) {
aom_internal_error(&ppi->error, AOM_CODEC_INVALID_PARAM,
"pts is smaller than initial pts");
}
ptsvol -= ctx->pts_offset;
if (ptsvol > INT64_MAX / cpi_data.timestamp_ratio->num) {
aom_internal_error(
&ppi->error, AOM_CODEC_INVALID_PARAM,
"conversion of relative pts to ticks would overflow");
}
int64_t src_time_stamp =
timebase_units_to_ticks(cpi_data.timestamp_ratio, ptsvol);
#if ULONG_MAX > INT64_MAX
if (duration > INT64_MAX) {
aom_internal_error(&ppi->error, AOM_CODEC_INVALID_PARAM,
"duration is too big");
}
#endif
if (ptsvol > INT64_MAX - (int64_t)duration) {
aom_internal_error(&ppi->error, AOM_CODEC_INVALID_PARAM,
"relative pts + duration is too big");
}
aom_codec_pts_t pts_end = ptsvol + (int64_t)duration;
if (pts_end > INT64_MAX / cpi_data.timestamp_ratio->num) {
aom_internal_error(
&ppi->error, AOM_CODEC_INVALID_PARAM,
"conversion of relative pts + duration to ticks would overflow");
}
int64_t src_end_time_stamp =
timebase_units_to_ticks(cpi_data.timestamp_ratio, pts_end);
YV12_BUFFER_CONFIG sd;
res = image2yuvconfig(img, &sd);
// When generating a monochrome stream, make |sd| a monochrome image.
if (ctx->cfg.monochrome) {
sd.u_buffer = sd.v_buffer = NULL;
sd.uv_stride = 0;
sd.monochrome = 1;
}
int use_highbitdepth = (sd.flags & YV12_FLAG_HIGHBITDEPTH) != 0;
int subsampling_x = sd.subsampling_x;
int subsampling_y = sd.subsampling_y;
if (!ppi->lookahead) {
int lag_in_frames = cpi_lap != NULL ? cpi_lap->oxcf.gf_cfg.lag_in_frames
: cpi->oxcf.gf_cfg.lag_in_frames;
AV1EncoderConfig *oxcf = &cpi->oxcf;
const BLOCK_SIZE sb_size = av1_select_sb_size(
oxcf, oxcf->frm_dim_cfg.width, oxcf->frm_dim_cfg.height,
ppi->number_spatial_layers);
oxcf->border_in_pixels =
av1_get_enc_border_size(av1_is_resize_needed(oxcf),
oxcf->kf_cfg.key_freq_max == 0, sb_size);
for (int i = 0; i < ppi->num_fp_contexts; i++) {
ppi->parallel_cpi[i]->oxcf.border_in_pixels = oxcf->border_in_pixels;
}
const int src_border_in_pixels = get_src_border_in_pixels(cpi, sb_size);
ppi->lookahead = av1_lookahead_init(
cpi->oxcf.frm_dim_cfg.width, cpi->oxcf.frm_dim_cfg.height,
subsampling_x, subsampling_y, use_highbitdepth, lag_in_frames,
src_border_in_pixels, cpi->common.features.byte_alignment,
ctx->num_lap_buffers, (cpi->oxcf.kf_cfg.key_freq_max == 0),
cpi->alloc_pyramid);
}
if (!ppi->lookahead)
aom_internal_error(&ppi->error, AOM_CODEC_MEM_ERROR,
"Failed to allocate lag buffers");
for (int i = 0; i < ppi->num_fp_contexts; i++) {
aom_codec_err_t err =
av1_check_initial_width(ppi->parallel_cpi[i], use_highbitdepth,
subsampling_x, subsampling_y);
if (err != AOM_CODEC_OK) {
aom_internal_error(&ppi->error, err,
"av1_check_initial_width() failed");
}
}
if (cpi_lap != NULL) {
aom_codec_err_t err = av1_check_initial_width(
cpi_lap, use_highbitdepth, subsampling_x, subsampling_y);
if (err != AOM_CODEC_OK) {
aom_internal_error(&ppi->error, err,
"av1_check_initial_width() failed");
}
}
// Store the original flags in to the frame buffer. Will extract the
// key frame flag when we actually encode this frame.
if (av1_receive_raw_frame(cpi, flags | ctx->next_frame_flags, &sd,
src_time_stamp, src_end_time_stamp)) {
res = update_error_state(ctx, cpi->common.error);
}
ctx->next_frame_flags = 0;
}
/* Any pending invisible frames? */
if (ctx->pending_cx_data_sz) {
cpi_data.cx_data += ctx->pending_cx_data_sz;
cpi_data.cx_data_sz -= ctx->pending_cx_data_sz;
/* TODO: this is a minimal check, the underlying codec doesn't respect
* the buffer size anyway.
*/
if (cpi_data.cx_data_sz < ctx->cx_data_sz / 2) {
aom_internal_error(&ppi->error, AOM_CODEC_ERROR,
"Compressed data buffer too small");
}
}
int is_frame_visible = 0;
int has_no_show_keyframe = 0;
int num_workers = 0;
if (cpi->oxcf.pass == AOM_RC_FIRST_PASS) {
#if !CONFIG_REALTIME_ONLY
num_workers = ppi->p_mt_info.num_mod_workers[MOD_FP] =
av1_fp_compute_num_enc_workers(cpi);
#endif
} else {
av1_compute_num_workers_for_mt(cpi);
num_workers = av1_get_max_num_workers(cpi);
}
if (num_workers > 1 && ppi->p_mt_info.num_workers < num_workers) {
// Obtain the maximum no. of frames that can be supported in a parallel
// encode set.
if (is_stat_consumption_stage(cpi)) {
ppi->num_fp_contexts = av1_compute_num_fp_contexts(ppi, &cpi->oxcf);
}
if (ppi->p_mt_info.num_workers > 0) {
av1_terminate_workers(ppi);
free_thread_data(ppi);
aom_free(ppi->p_mt_info.tile_thr_data);
ppi->p_mt_info.tile_thr_data = NULL;
aom_free(ppi->p_mt_info.workers);
ppi->p_mt_info.workers = NULL;
ppi->p_mt_info.num_workers = 0;
for (int j = 0; j < ppi->num_fp_contexts; j++) {
aom_free(ppi->parallel_cpi[j]->td.tctx);
ppi->parallel_cpi[j]->td.tctx = NULL;
}
}
av1_create_workers(ppi, num_workers);
av1_init_tile_thread_data(ppi, cpi->oxcf.pass == AOM_RC_FIRST_PASS);
}
// Re-allocate thread data if workers for encoder multi-threading stage
// exceeds prev_num_enc_workers.
const int num_enc_workers =
av1_get_num_mod_workers_for_alloc(&ppi->p_mt_info, MOD_ENC);
if (ppi->p_mt_info.prev_num_enc_workers < num_enc_workers &&
num_enc_workers <= ppi->p_mt_info.num_workers) {
free_thread_data(ppi);
for (int j = 0; j < ppi->num_fp_contexts; j++) {
aom_free(ppi->parallel_cpi[j]->td.tctx);
ppi->parallel_cpi[j]->td.tctx = NULL;
}
av1_init_tile_thread_data(ppi, cpi->oxcf.pass == AOM_RC_FIRST_PASS);
}
for (int i = 0; i < ppi->num_fp_contexts; i++) {
av1_init_frame_mt(ppi, ppi->parallel_cpi[i]);
}
if (cpi_lap != NULL) {
av1_init_frame_mt(ppi, cpi_lap);
}
#if CONFIG_MULTITHREAD
if (ppi->p_mt_info.num_workers > 1) {
for (int i = 0; i < ppi->num_fp_contexts; i++) {
av1_init_mt_sync(ppi->parallel_cpi[i],
ppi->parallel_cpi[i]->oxcf.pass == AOM_RC_FIRST_PASS);
}
if (cpi_lap != NULL) {
av1_init_mt_sync(cpi_lap, 1);
}
}
#endif // CONFIG_MULTITHREAD
// Call for LAP stage
if (cpi_lap != NULL && !is_one_pass_rt_lag_params(cpi)) {
if (cpi_lap->ppi->b_freeze_internal_state) {
av1_save_all_coding_context(cpi_lap);
}
AV1_COMP_DATA cpi_lap_data = { 0 };
cpi_lap_data.flush = !img;
cpi_lap_data.timestamp_ratio = &ctx->timestamp_ratio;
const int status = av1_get_compressed_data(cpi_lap, &cpi_lap_data);
if (status > AOM_CODEC_OK) {
aom_internal_error_copy(&ppi->error, cpi_lap->common.error);
}
av1_post_encode_updates(cpi_lap, &cpi_lap_data);
if (cpi_lap->ppi->b_freeze_internal_state) {
restore_all_coding_context(cpi_lap);
}
}
// Recalculate the maximum number of frames that can be encoded in
// parallel at the beginning of sub gop.
if (is_stat_consumption_stage(cpi) && ppi->gf_group.size > 0 &&
cpi->gf_frame_index == ppi->gf_group.size) {
ppi->num_fp_contexts = av1_compute_num_fp_contexts(ppi, &cpi->oxcf);
}
// Get the next visible frame. Invisible frames get packed with the next
// visible frame.
while (cpi_data.cx_data_sz >= ctx->cx_data_sz / 2 && !is_frame_visible) {
int simulate_parallel_frame = 0;
int status = -1;
cpi->do_frame_data_update = true;
cpi->ref_idx_to_skip = INVALID_IDX;
cpi->ref_refresh_index = INVALID_IDX;
cpi->refresh_idx_available = false;
if (cpi->ppi->b_freeze_internal_state) {
av1_save_all_coding_context(cpi);
}
#if CONFIG_FPMT_TEST
simulate_parallel_frame =
cpi->ppi->fpmt_unit_test_cfg == PARALLEL_SIMULATION_ENCODE ? 1 : 0;
if (simulate_parallel_frame) {
if (ppi->num_fp_contexts > 1 && ppi->gf_group.size > 1) {
if (cpi->gf_frame_index < ppi->gf_group.size) {
calc_frame_data_update_flag(&ppi->gf_group, cpi->gf_frame_index,
&cpi->do_frame_data_update);
}
}
status = av1_get_compressed_data(cpi, &cpi_data);
}
#endif // CONFIG_FPMT_TEST
if (!simulate_parallel_frame) {
// Add a range check here to give an error for frame_parallel_level
// array out-of-bounds access.
if (cpi->gf_frame_index >= MAX_STATIC_GF_GROUP_LENGTH) {
aom_internal_error(&ppi->error, AOM_CODEC_ERROR,
"cpi->gf_frame_index is out of range");
}
// May need a better way for checking the frame's frame_parallel_level,
// especially for the first frame of the following gop.
if (ppi->gf_group.frame_parallel_level[cpi->gf_frame_index] == 0) {
status = av1_get_compressed_data(cpi, &cpi_data);
} else if (ppi->gf_group.frame_parallel_level[cpi->gf_frame_index] == 1) {
// In case of an error, longjmp() would be invoked and hence "status"
// is set to AOM_CODEC_OK here.
av1_compress_parallel_frames(ppi, &cpi_data);
status = AOM_CODEC_OK;
} else {
// No possibility of failures from this function and hence "status" is
// set to AOM_CODEC_OK here.
cpi = av1_get_parallel_frame_enc_data(ppi, &cpi_data);
status = AOM_CODEC_OK;
}
}
if (status == -1) break;
if (status != AOM_CODEC_OK) {
aom_internal_error_copy(&ppi->error, cpi->common.error);
}
if (ppi->num_fp_contexts > 0 && frame_is_intra_only(&cpi->common)) {
av1_init_sc_decisions(ppi);
}
has_no_show_keyframe |=
(!is_frame_visible &&
cpi->common.current_frame.frame_type == KEY_FRAME);
}
if (is_frame_visible) {
// Add the frame packet to the list of returned packets.
aom_codec_cx_pkt_t pkt;
pkt.data.frame.pts = ticks_to_timebase_units(cpi_data.timestamp_ratio,
cpi_data.ts_frame_start) +
ctx->pts_offset;
pkt.data.frame.flags = get_frame_pkt_flags(cpi, cpi_data.lib_flags);
if (has_no_show_keyframe) {
// If one of the invisible frames in the packet is a keyframe, set
// the delayed random access point flag.
pkt.data.frame.flags |= AOM_FRAME_IS_DELAYED_RANDOM_ACCESS_POINT;
}
const int64_t duration64 = ticks_to_timebase_units(
cpi_data.timestamp_ratio,
cpi_data.ts_frame_end - cpi_data.ts_frame_start);
if (duration64 > UINT32_MAX) {
aom_internal_error(&ppi->error, AOM_CODEC_ERROR, NULL);
}
pkt.data.frame.duration = (uint32_t)duration64;
if (mode) {
AV1EncoderConfig *const oxcf =
ctx->ppi->seq_params_locked ? &ctx->ppi->cpi->oxcf : &ctx->oxcf;
const int res =
av1_set_internal_size(oxcf, &ctx->ppi->cpi->resize_pending_params,
mode->h_scaling_mode, mode->v_scaling_mode);
if (res == 0) {
// update_encoder_cfg() is somewhat costly and this control may be called
// multiple times, so update_encoder_cfg() is only called to ensure frame
// and superblock sizes are updated before they're fixed by the first
// encode call.
if (ctx->ppi->seq_params_locked) {
av1_check_fpmt_config(ctx->ppi, &ctx->ppi->cpi->oxcf);
return AOM_CODEC_OK;
}
return update_encoder_cfg(ctx);
}
return AOM_CODEC_INVALID_PARAM;
} else {
return AOM_CODEC_INVALID_PARAM;
}
}
static aom_codec_err_t ctrl_set_number_spatial_layers(aom_codec_alg_priv_t *ctx,
va_list args) {
const int number_spatial_layers = va_arg(args, int);
// Note svc.use_flexible_mode is set by AV1E_SET_SVC_REF_FRAME_CONFIG. When
// it is false (the default) the actual limit is 3 for both spatial and
// temporal layers. Given the order of these calls are unpredictable the
// final check is deferred until encoder_encode() (av1_set_svc_fixed_mode()).
if (number_spatial_layers <= 0 ||
number_spatial_layers > MAX_NUM_SPATIAL_LAYERS)
return AOM_CODEC_INVALID_PARAM;
ctx->ppi->number_spatial_layers = number_spatial_layers;
// update_encoder_cfg() is somewhat costly and this control may be called
// multiple times, so update_encoder_cfg() is only called to ensure frame and
// superblock sizes are updated before they're fixed by the first encode
// call.
if (!ctx->ppi->seq_params_locked) {
return update_encoder_cfg(ctx);
}
return AOM_CODEC_OK;
}
static aom_codec_err_t ctrl_set_svc_params(aom_codec_alg_priv_t *ctx,
va_list args) {
AV1_PRIMARY *const ppi = ctx->ppi;
AV1_COMP *const cpi = ppi->cpi;
aom_svc_params_t *const params = va_arg(args, aom_svc_params_t *);
volatile int64_t target_bandwidth = 0;
// Note svc.use_flexible_mode is set by AV1E_SET_SVC_REF_FRAME_CONFIG. When
// it is false (the default) the actual limit is 3 for both spatial and
// temporal layers. Given the order of these calls are unpredictable the
// final check is deferred until encoder_encode() (av1_set_svc_fixed_mode()).
if (params->number_spatial_layers <= 0 ||
params->number_spatial_layers > MAX_NUM_SPATIAL_LAYERS ||
params->number_temporal_layers <= 0 ||
params->number_temporal_layers > MAX_NUM_TEMPORAL_LAYERS) {
return AOM_CODEC_INVALID_PARAM;
}
ppi->number_spatial_layers = params->number_spatial_layers;
ppi->number_temporal_layers = params->number_temporal_layers;
cpi->svc.number_spatial_layers = params->number_spatial_layers;
cpi->svc.number_temporal_layers = params->number_temporal_layers;
// Sequence parameters (operating_points_cnt_minus_1, operating_point_idc[])
// need to be updated if the number of layers have changed.
// Force a keyframe here and update the two relevant sequence parameters.
if (cpi->svc.prev_number_temporal_layers &&
cpi->svc.prev_number_spatial_layers &&
(cpi->svc.number_temporal_layers !=
cpi->svc.prev_number_temporal_layers ||
cpi->svc.number_spatial_layers != cpi->svc.prev_number_spatial_layers)) {
SequenceHeader *const seq_params = &ppi->seq_params;
seq_params->operating_points_cnt_minus_1 =
ppi->number_spatial_layers * ppi->number_temporal_layers - 1;
ctx->next_frame_flags |= AOM_EFLAG_FORCE_KF;
av1_set_svc_seq_params(ppi);
av1_free_svc_cyclic_refresh(cpi);
// Check for valid values for the spatial/temporal_layer_id here, since
// there has been a dynamic change in the number_spatial/temporal_layers,
// and if the ctrl_set_layer_id is not used after this call, the
// previous (last_encoded) values of spatial/temporal_layer_id will be used,
// which may be invalid.
cpi->svc.spatial_layer_id =
clamp(cpi->svc.spatial_layer_id, 0, cpi->svc.number_spatial_layers - 1);
cpi->svc.temporal_layer_id = clamp(cpi->svc.temporal_layer_id, 0,
cpi->svc.number_temporal_layers - 1);
cpi->common.spatial_layer_id = clamp(cpi->common.spatial_layer_id, 0,
cpi->svc.number_spatial_layers - 1);
cpi->common.temporal_layer_id = clamp(cpi->common.temporal_layer_id, 0,
cpi->svc.number_temporal_layers - 1);
}
if (ppi->number_spatial_layers > 1 || ppi->number_temporal_layers > 1) {
unsigned int sl, tl;
// Disable svc for lag_in_frames > 0.
if (cpi->oxcf.gf_cfg.lag_in_frames > 0) return AOM_CODEC_INVALID_PARAM;
const int num_layers =
ppi->number_spatial_layers * ppi->number_temporal_layers;
for (int layer = 0; layer < num_layers; ++layer) {
if (params->max_quantizers[layer] > 63 ||
params->min_quantizers[layer] < 0 ||
params->min_quantizers[layer] > params->max_quantizers[layer]) {
disable_svc(ppi, cpi);
return AOM_CODEC_INVALID_PARAM;
}
}
for (sl = 0; sl < ppi->number_spatial_layers; ++sl) {
// Check scaling factors: spatial scaling (scaling_factor_num[]/den[]) is
// always to a lower resolution, so den must be >= num.
if (params->scaling_factor_den[sl] < params->scaling_factor_num[sl]) {
disable_svc(ppi, cpi);
return AOM_CODEC_INVALID_PARAM;
}
}
if (!av1_alloc_layer_context(cpi, num_layers)) return AOM_CODEC_MEM_ERROR;
ppi->use_svc = 1;
if (ppi->seq_params_locked) {
AV1EncoderConfig *const oxcf = &cpi->oxcf;
// Keep ctx->oxcf in sync in case further codec controls are made prior
// to encoding.
ctx->oxcf.rc_cfg.target_bandwidth = oxcf->rc_cfg.target_bandwidth =
target_bandwidth;
set_primary_rc_buffer_sizes(oxcf, ppi);
ENABLE_SETJMP(cpi);
av1_update_layer_context_change_config(cpi, target_bandwidth);
DISABLE_SETJMP(cpi);
check_reset_rc_flag(cpi);
} else {
// Note av1_init_layer_context() relies on cpi->oxcf. The order of that
// call and the ones in the other half of this block (which
// update_encoder_cfg() transitively makes) is important. So we keep
// ctx->oxcf and cpi->oxcf in sync here as update_encoder_cfg() will
// overwrite cpi->oxcf with ctx->oxcf.
ctx->oxcf.rc_cfg.target_bandwidth = cpi->oxcf.rc_cfg.target_bandwidth =
target_bandwidth;
SequenceHeader *const seq_params = &ppi->seq_params;
seq_params->operating_points_cnt_minus_1 =
ppi->number_spatial_layers * ppi->number_temporal_layers - 1;
ENABLE_SETJMP(cpi);
av1_init_layer_context(cpi);
DISABLE_SETJMP(cpi); // update_encoder_cfg() is somewhat costly and this control may be called // multiple times, so update_encoder_cfg() is only called to ensure frame // and superblock sizes are updated before they're fixed by the first // encode call. return update_encoder_cfg(ctx);
}
} else if (!ppi->seq_params_locked) { // Ensure frame and superblock sizes are updated. return update_encoder_cfg(ctx);
}
av1_check_fpmt_config(ctx->ppi, &ctx->ppi->cpi->oxcf); return AOM_CODEC_OK;
}
static aom_codec_err_t ctrl_set_svc_ref_frame_config(aom_codec_alg_priv_t *ctx,
va_list args) {
AV1_COMP *const cpi = ctx->ppi->cpi;
aom_svc_ref_frame_config_t *const data =
va_arg(args, aom_svc_ref_frame_config_t *);
cpi->ppi->rtc_ref.set_ref_frame_config = 1;
for (unsigned int i = 0; i < INTER_REFS_PER_FRAME; ++i) {
if (data->reference[i] != 0 && data->reference[i] != 1) return AOM_CODEC_INVALID_PARAM;
if (data->ref_idx[i] > 7 || data->ref_idx[i] < 0) return AOM_CODEC_INVALID_PARAM;
cpi->ppi->rtc_ref.reference[i] = data->reference[i];
cpi->ppi->rtc_ref.ref_idx[i] = data->ref_idx[i];
}
for (unsigned int i = 0; i < REF_FRAMES; ++i) {
if (data->refresh[i] != 0 && data->refresh[i] != 1) return AOM_CODEC_INVALID_PARAM;
cpi->ppi->rtc_ref.refresh[i] = data->refresh[i];
}
cpi->svc.use_flexible_mode = 1;
cpi->svc.ksvc_fixed_mode = 0; return AOM_CODEC_OK;
}
static aom_codec_err_t encoder_set_option(aom_codec_alg_priv_t *ctx, const char *name, const char *value) {
if (ctx == NULL || name == NULL || value == NULL) return AOM_CODEC_INVALID_PARAM; struct av1_extracfg extra_cfg = ctx->extra_cfg; // Used to mock the argv with just one string "--{name}={value}"
char *argv[2] = { NULL, "" };
size_t len = strlen(name) + strlen(value) + 4;
char *const err_string = ctx->ppi->error.detail;
static_assert(sizeof(ctx->ppi->error.detail) >= ARG_ERR_MSG_MAX_LEN, "The size of the err_msg buffer for arg_match_helper must be " "at least ARG_ERR_MSG_MAX_LEN");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.