函数av1_encode_strategy会执行一些high-level的编码策略,包括选择帧的类型等。其中函数choose_frame_source,会根据当前帧是否为ARF or internal ARF,来对原始帧进行时域滤波,如果不是则用LAST代替或者从lookahead从得到。
show_existing_frame代表这一帧已经编码过.
tpl_model在第二次编码才会使用,是通过Look-ahead的方式记录inter和intra的cost, 通过类似MB-Tree的方法对实际编码的QP和lambda进行调整以提高性能。
av1_configure_buffer_updates,用于更新参考帧类型的数据,比如对于LAST_FRAME, 会被初始化为
case LF_UPDATE:
frame_params->refresh_last_frame = 1;
frame_params->refresh_golden_frame = 0;
frame_params->refresh_bwd_ref_frame = 0;
frame_params->refresh_alt2_ref_frame = 0;
frame_params->refresh_alt_ref_frame = 0;
而KEY_FRAME会把const int force_refresh_all设为1 ,结果就变成了
if (force_refresh_all) {
frame_params->refresh_last_frame = 1;
frame_params->refresh_golden_frame = 1;
frame_params->refresh_bwd_ref_frame = 1;
frame_params->refresh_alt2_ref_frame = 1;
frame_params->refresh_alt_ref_frame = 1;
}
函数的下一个入口是av1_encode
int av1_encode_strategy(AV1_COMP *const cpi, size_t *const size,
uint8_t *const dest, unsigned int *frame_flags,
int64_t *const time_stamp, int64_t *const time_end,
const aom_rational64_t *const timestamp_ratio,
int flush) {
const AV1EncoderConfig *const oxcf = &cpi->oxcf;
AV1_COMMON *const cm = &cpi->common;
EncodeFrameInput frame_input;
EncodeFrameParams frame_params;
EncodeFrameResults frame_results;
memset(&frame_input, 0, sizeof(frame_input));
memset(&frame_params, 0, sizeof(frame_params));
memset(&frame_results, 0, sizeof(frame_results));
if (oxcf->pass == 0 || oxcf->pass == 2) {
check_show_existing_frame(cpi, &frame_params);
frame_params.show_existing_frame &= allow_show_existing(cpi, *frame_flags);
} else {
frame_params.show_existing_frame = 0;
}
int temporal_filtered = 0;
struct lookahead_entry *source = NULL;
struct lookahead_entry *last_source = NULL;
FRAME_UPDATE_TYPE frame_update_type;
if (frame_params.show_existing_frame) {
source = av1_lookahead_pop(cpi->lookahead, flush);
frame_update_type = LF_UPDATE;
} else {
source = choose_frame_source(cpi, &temporal_filtered, &flush, &last_source,
&frame_update_type, &frame_params);//根据当前帧的类型来决定是否需要temporal filter
}
// In pass 2 we get the frame_update_type from gf_group
if (oxcf->pass == 2) {
frame_update_type = get_frame_update_type(cpi);
}
if (source == NULL) { // If no source was found, we can't encode a frame.
#if !CONFIG_REALTIME_ONLY
if (flush && oxcf->pass == 1 && !cpi->twopass.first_pass_done) {
av1_end_first_pass(cpi); /* get last stats packet */
cpi->twopass.first_pass_done = 1;
}
#endif
return -1;
}
frame_input.source = temporal_filtered ? &cpi->alt_ref_buffer : &source->img;
frame_input.last_source = last_source != NULL ? &last_source->img : NULL;
frame_input.ts_duration = source->ts_end - source->ts_start;
*time_stamp = source->ts_start;
*time_end = source->ts_end;
if (source->ts_start < cpi->first_time_stamp_ever) {
cpi->first_time_stamp_ever = source->ts_start;
cpi->last_end_time_stamp_seen = source->ts_start;
}
av1_apply_encoding_flags(cpi, source->flags);
if (!frame_params.show_existing_frame)
*frame_flags = (source->flags & AOM_EFLAG_FORCE_KF) ? FRAMEFLAGS_KEY : 0;
const int is_overlay = frame_params.show_existing_frame &&
(frame_update_type == OVERLAY_UPDATE ||
frame_update_type == INTNL_OVERLAY_UPDATE);
if (frame_params.show_frame || is_overlay) {
// Shown frames and arf-overlay frames need frame-rate considering
adjust_frame_rate(cpi, source);
}
if (frame_params.show_existing_frame) {
// show_existing_frame implies this frame is shown!
frame_params.show_frame = 1;
} else {
if (cpi->film_grain_table) {
cm->cur_frame->film_grain_params_present = aom_film_grain_table_lookup(
cpi->film_grain_table, *time_stamp, *time_end, 0 /* =erase */,
&cm->film_grain_params);
} else {
cm->cur_frame->film_grain_params_present =
cm->seq_params.film_grain_params_present;
}
// only one operating point supported now
const int64_t pts64 = ticks_to_timebase_units(timestamp_ratio, *time_stamp);
if (pts64 < 0 || pts64 > UINT32_MAX) return AOM_CODEC_ERROR;
cpi->common.frame_presentation_time = (uint32_t)pts64;
}
#if !CONFIG_REALTIME_ONLY
if (oxcf->pass == 2 && (!frame_params.show_existing_frame || is_overlay)) {
// GF_GROUP needs updating for arf overlays as well as non-show-existing
av1_get_second_pass_params(cpi, &frame_params, *frame_flags);
frame_update_type = get_frame_update_type(cpi);
}
#endif
if (frame_params.show_existing_frame &&
frame_params.frame_type != KEY_FRAME) {
// Force show-existing frames to be INTER, except forward keyframes
frame_params.frame_type = INTER_FRAME;
}
// TODO([email protected]): Move all the encode strategy
// (largely near av1_get_compressed_data) in here
// TODO([email protected]): Change all the encode strategy to
// modify frame_params instead of cm or cpi.
// Per-frame encode speed. In theory this can vary, but things may have been
// written assuming speed-level will not change within a sequence, so this
// parameter should be used with caution.
frame_params.speed = oxcf->speed;
if (!frame_params.show_existing_frame) {
cm->using_qmatrix = cpi->oxcf.using_qm;
cm->min_qmlevel = cpi->oxcf.qm_minlevel;
cm->max_qmlevel = cpi->oxcf.qm_maxlevel;
if (oxcf->pass == 2) {
if (cpi->twopass.gf_group.index == 1 && cpi->oxcf.enable_tpl_model) {
av1_configure_buffer_updates(cpi, &frame_params, frame_update_type, 0);
av1_set_frame_size(cpi, cm->width, cm->height);
av1_tpl_setup_stats(cpi, &frame_input);
assert(cpi->num_gf_group_show_frames == 1);
}
}
}
// Work out some encoding parameters specific to the pass:
if (oxcf->pass == 0) {
if (cpi->oxcf.rc_mode == AOM_CBR) {
av1_rc_get_one_pass_cbr_params(cpi, &frame_update_type, &frame_params,
*frame_flags);
} else {
av1_rc_get_one_pass_vbr_params(cpi, &frame_update_type, &frame_params,
*frame_flags);
}
} else if (oxcf->pass == 1) {
cpi->td.mb.e_mbd.lossless[0] = is_lossless_requested(&cpi->oxcf);
const int kf_requested = (cm->current_frame.frame_number == 0 ||
(*frame_flags & FRAMEFLAGS_KEY));
if (kf_requested && frame_update_type != OVERLAY_UPDATE &&
frame_update_type != INTNL_OVERLAY_UPDATE) {
frame_params.frame_type = KEY_FRAME;
} else {
frame_params.frame_type = INTER_FRAME;
}
} else if (oxcf->pass == 2) {
#if CONFIG_MISMATCH_DEBUG
mismatch_move_frame_idx_w();
#endif
#if TXCOEFF_COST_TIMER
cm->txcoeff_cost_timer = 0;
cm->txcoeff_cost_count = 0;
#endif
}
if (oxcf->pass == 0 || oxcf->pass == 2) set_ext_overrides(cpi, &frame_params);
// Shown keyframes and S frames refresh all reference buffers
const int force_refresh_all =
((frame_params.frame_type == KEY_FRAME && frame_params.show_frame) ||
frame_params.frame_type == S_FRAME) &&
!frame_params.show_existing_frame;
av1_configure_buffer_updates(cpi, &frame_params, frame_update_type,
force_refresh_all);
if (oxcf->pass == 0 || oxcf->pass == 2) {
// Work out which reference frame slots may be used.
frame_params.ref_frame_flags = get_ref_frame_flags(cpi);
frame_params.primary_ref_frame =
choose_primary_ref_frame(cpi, &frame_params);
frame_params.order_offset =
get_order_offset(&cpi->twopass.gf_group, &frame_params);
frame_params.refresh_frame_flags =
get_refresh_frame_flags(cpi, &frame_params, frame_update_type);
}
// The way frame_params->remapped_ref_idx is setup is a placeholder.
// Currently, reference buffer assignment is done by update_ref_frame_map()
// which is called by high-level strategy AFTER encoding a frame. It modifies
// cm->remapped_ref_idx. If you want to use an alternative method to
// determine reference buffer assignment, just put your assignments into
// frame_params->remapped_ref_idx here and they will be used when encoding
// this frame. If frame_params->remapped_ref_idx is setup independently of
// cm->remapped_ref_idx then update_ref_frame_map() will have no effect.
memcpy(frame_params.remapped_ref_idx, cm->remapped_ref_idx,
REF_FRAMES * sizeof(*cm->remapped_ref_idx));
if (av1_encode(cpi, dest, &frame_input, &frame_params, &frame_results) !=
AOM_CODEC_OK) {
return AOM_CODEC_ERROR;
}
if (oxcf->pass == 2) cpi->num_gf_group_show_frames += frame_params.show_frame;
if (oxcf->pass == 0 || oxcf->pass == 2) {
// First pass doesn't modify reference buffer assignment or produce frame
// flags
update_frame_flags(cpi, frame_flags);
update_ref_frame_map(cpi, frame_update_type);
}
#if !CONFIG_REALTIME_ONLY
if (oxcf->pass == 2) {
#if TXCOEFF_COST_TIMER
cm->cum_txcoeff_cost_timer += cm->txcoeff_cost_timer;
fprintf(stderr,
"\ntxb coeff cost block number: %ld, frame time: %ld, cum time %ld "
"in us\n",
cm->txcoeff_cost_count, cm->txcoeff_cost_timer,
cm->cum_txcoeff_cost_timer);
#endif
av1_twopass_postencode_update(cpi);
}
#endif // !CONFIG_REALTIME_ONLY
if (oxcf->pass == 0 || oxcf->pass == 2) {
update_fb_of_context_type(cpi, &frame_params, cpi->fb_of_context_type);
set_additional_frame_flags(cm, frame_flags);
update_rc_counts(cpi);
}
// Unpack frame_results:
*size = frame_results.size;
// Leave a signal for a higher level caller about if this frame is droppable
if (*size > 0) {
cpi->droppable = is_frame_droppable(cpi);
}
return AOM_CODEC_OK;
}