1,函数功能
顾名思义,encode_with_recode_loop编码完成后将打包到码流,函数首先进行配置文件中编码必须的属性设置,编码完还有个重要的流程,就是loopfilter_frame, loopfilter_frame涉及到编码器内的图像重建,具体内容请看代码注释
2,代码学习
static int encode_frame_to_data_rate(AV1_COMP *cpi, size_t *size,
uint8_t *dest) {
AV1_COMMON *const cm = &cpi->common;
SequenceHeader *const seq_params = &cm->seq_params;
CurrentFrame *const current_frame = &cm->current_frame;
const AV1EncoderConfig *const oxcf = &cpi->oxcf;
struct segmentation *const seg = &cm->seg;
#if CONFIG_COLLECT_COMPONENT_TIMING
start_timing(cpi, encode_frame_to_data_rate_time);
#endif
// frame type has been decided outside of this function call
// 帧类型已在此函数上一层确定
cm->cur_frame->frame_type = current_frame->frame_type;
cm->large_scale_tile = cpi->oxcf.large_scale_tile;
cm->single_tile_decoding = cpi->oxcf.single_tile_decoding;
cm->allow_ref_frame_mvs &= frame_might_allow_ref_frame_mvs(cm);
// cm->allow_ref_frame_mvs needs to be written into the frame header while
// cm->large_scale_tile is 1, therefore, "cm->large_scale_tile=1" case is
// separated from frame_might_allow_ref_frame_mvs().
/*运动相关*/
/*cm->large_scale_tile=1其实被写入帧头的frame_might_allow_ref_frame_mvs()分开的,目前还不明白这里*/
cm->allow_ref_frame_mvs &= !cm->large_scale_tile;
/*运动相关*/
cm->allow_warped_motion =
cpi->oxcf.allow_warped_motion && frame_might_allow_warped_motion(cm);
cm->last_frame_type = current_frame->frame_type;//当前帧类型
if (cpi->oxcf.pass == 2 && cpi->sf.adaptive_interp_filter_search) /*允许跳过某些过滤器类型。*/
cpi->sf.interp_filter_search_mask = setup_interp_filter_search_mask(cpi);
if (encode_show_existing_frame(cm)) {//检查cm->show_existing_frame是不是与容错帧重合
restore_coding_context(cpi);
finalize_encoded_frame(cpi);
// Build the bitstream,建立bit流奥
int largest_tile_id = 0; // Output from bitstream: unused here
if (av1_pack_bitstream(cpi, dest, size, &largest_tile_id) != AOM_CODEC_OK)
return AOM_CODEC_ERROR;
if (seq_params->frame_id_numbers_present_flag &&
current_frame->frame_type == KEY_FRAME) {//关键帧并且存在前向帧
// Displaying a forward key-frame, so reset the ref buffer IDs
/*显示前向关键帧,因此重置ref缓冲区id*/
int display_frame_id = cm->ref_frame_id[cpi->existing_fb_idx_to_show];
for (int i = 0; i < REF_FRAMES; i++)
cm->ref_frame_id[i] = display_frame_id;
}
cpi->seq_params_locked = 1;
#if DUMP_RECON_FRAMES == 1
// NOTE(zoeliu): For debug - Output the filtered reconstructed video.
dump_filtered_recon_frames(cpi);
#endif // DUMP_RECON_FRAMES
// NOTE: Save the new show frame buffer index for --test-code=warn, i.e.,
// for the purpose to verify no mismatch between encoder and decoder.
if (cm->show_frame) cpi->last_show_frame_buf = cm->cur_frame;
refresh_reference_frames(cpi); //刷新参考帧列表
// Since we allocate a spot for the OVERLAY frame in the gf group, we need
// to do post-encoding update accordingly.
/*因为我们在黄金帧组中为覆盖帧分配了一个位置,所以我们需要进行相应的后期编码更新*/
if (cpi->rc.is_src_frame_alt_ref) {
av1_set_target_rate(cpi, cm->width, cm->height);
av1_rc_postencode_update(cpi, *size);
}
++current_frame->frame_number;
return AOM_CODEC_OK;
}
// Work out whether to force_integer_mv this frame
// 确定这一帧是否要force_integer_mv,貌似是强制将运动量化成整数
if (oxcf->pass != 1 && cpi->common.allow_screen_content_tools &&
!frame_is_intra_only(cm)) {
if (cpi->common.seq_params.force_integer_mv == 2) {
// Adaptive mode: see what previous frame encoded did
if (cpi->unscaled_last_source != NULL) {//上一帧不为空
cm->cur_frame_force_integer_mv =
is_integer_mv(cpi, cpi->source, cpi->unscaled_last_source,
cpi->previous_hash_table);
} else {
cpi->common.cur_frame_force_integer_mv = 0;
}
} else {
cpi->common.cur_frame_force_integer_mv =
cpi->common.seq_params.force_integer_mv;
}
} else {
cpi->common.cur_frame_force_integer_mv = 0;
}
// Set default state for segment based loop filter update flags.
// 为loop filter update flags设置设置默认状态。
cm->lf.mode_ref_delta_update = 0;
// Set various flags etc to special state if it is a key frame.
// 关键帧的话就设置为特殊的状态
if (frame_is_intra_only(cm) || frame_is_sframe(cm)) {
// Reset the loop filter deltas and segmentation map.
//重新设置loop filter三部分和分段的映射
av1_reset_segment_features(cm);
// If segmentation is enabled force a map update for key frames.
//如果分段,可以强制为关键帧更新映射
if (seg->enabled) {
seg->update_map = 1;
seg->update_data = 1;
}
// The alternate reference frame cannot be active for a key frame.
//可选参考帧不能被激活作为关键帧
cpi->rc.source_alt_ref_active = 0;
}
if (cpi->oxcf.mtu == 0) {
cm->num_tg = cpi->oxcf.num_tile_groups;
} else {
// Use a default value for the purposes of weighting costs in probability
// updates
cm->num_tg = DEFAULT_MAX_NUM_TG;
}
// For 1 pass CBR, check if we are dropping this frame.
// Never drop on key frame.
if (oxcf->pass == 0 && oxcf->rc_mode == AOM_CBR &&
current_frame->frame_type != KEY_FRAME) {
if (av1_rc_drop_frame(cpi)) {//丢弃帧
av1_rc_postencode_update_drop_frame(cpi);
release_scaled_references(cpi);
return AOM_CODEC_OK;
}
}
if (oxcf->tuning == AOM_TUNE_SSIM) set_mb_ssim_rdmult_scaling(cpi);
aom_clear_system_state();
#if CONFIG_INTERNAL_STATS
memset(cpi->mode_chosen_counts, 0,
MAX_MODES * sizeof(*cpi->mode_chosen_counts));
#endif
if (seq_params->frame_id_numbers_present_flag) {
/* Non-normative definition of current_frame_id ("frame counter" with
* wraparound) */
if (cm->current_frame_id == -1) {
int lsb, msb;
/* quasi-random initialization of current_frame_id for a key frame */
if (cpi->source->flags & YV12_FLAG_HIGHBITDEPTH) {
lsb = CONVERT_TO_SHORTPTR(cpi->source->y_buffer)[0] & 0xff;
msb = CONVERT_TO_SHORTPTR(cpi->source->y_buffer)[1] & 0xff;
} else {
lsb = cpi->source->y_buffer[0] & 0xff;
msb = cpi->source->y_buffer[1] & 0xff;
}
cm->current_frame_id =
((msb << 8) + lsb) % (1 << seq_params->frame_id_length);
// S_frame is meant for stitching different streams of different
// resolutions together, so current_frame_id must be the
// same across different streams of the same content current_frame_id
// should be the same and not random. 0x37 is a chosen number as start
// point
//s帧是用来缝合不同分辨率的不同流,因此当前帧id必须在相同内容的不同流中相同当前帧id应该相同
if (cpi->oxcf.sframe_enabled) cm->current_frame_id = 0x37;
} else {
cm->current_frame_id =
(cm->current_frame_id + 1 + (1 << seq_params->frame_id_length)) %
(1 << seq_params->frame_id_length);
}
}
switch (cpi->oxcf.cdf_update_mode) {
case 0: // No CDF update for any frames(4~6% compression loss).
cm->disable_cdf_update = 1;
break;
case 1: // Enable CDF update for all frames.
cm->disable_cdf_update = 0;
break;
case 2:
// Strategically determine at which frames to do CDF update.
// Currently only enable CDF update for all-intra and no-show frames(1.5%
// compression loss).
// TODO(huisu@google.com): design schemes for various trade-offs between
// compression quality and decoding speed.
cm->disable_cdf_update =
(frame_is_intra_only(cm) || !cm->show_frame) ? 0 : 1;
break;
}
cm->timing_info_present &= !seq_params->reduced_still_picture_hdr;
#if CONFIG_COLLECT_COMPONENT_TIMING
start_timing(cpi, encode_with_recode_loop_time);
#endif
if (cpi->oxcf.pass == 2 && cpi->oxcf.enable_tpl_model == 2 &&
current_frame->frame_type == INTER_FRAME) {
if (!cm->show_frame) {
assert(cpi->tpl_model_pass == 0);
cpi->tpl_model_pass = 1;
}
}
if (encode_with_recode_loop(cpi, size, dest) != AOM_CODEC_OK) //编码入口
return AOM_CODEC_ERROR;
#if CONFIG_COLLECT_COMPONENT_TIMING
end_timing(cpi, encode_with_recode_loop_time);
#endif
#ifdef OUTPUT_YUV_SKINMAP
if (cpi->common.current_frame.frame_number > 1) {
av1_compute_skin_map(cpi, yuv_skinmap_file);
}
#endif // OUTPUT_YUV_SKINMAP
// Special case code to reduce pulsing when key frames are forced at a
// fixed interval. Note the reconstruction error if it is the frame before
// the force key frame
if (cpi->rc.next_key_frame_forced && cpi->rc.frames_to_key == 1) {
if (seq_params->use_highbitdepth) {
cpi->ambient_err = aom_highbd_get_y_sse(cpi->source, &cm->cur_frame->buf);
} else {
cpi->ambient_err = aom_get_y_sse(cpi->source, &cm->cur_frame->buf);
}
}
cm->cur_frame->buf.color_primaries = seq_params->color_primaries;
cm->cur_frame->buf.transfer_characteristics =
seq_params->transfer_characteristics;
cm->cur_frame->buf.matrix_coefficients = seq_params->matrix_coefficients;
cm->cur_frame->buf.monochrome = seq_params->monochrome;
cm->cur_frame->buf.chroma_sample_position =
seq_params->chroma_sample_position;
cm->cur_frame->buf.color_range = seq_params->color_range;
cm->cur_frame->buf.render_width = cm->render_width;
cm->cur_frame->buf.render_height = cm->render_height;
// TODO(zoeliu): For non-ref frames, loop filtering may need to be turned
// off.
// Pick the loop filter level for the frame.
if (!cm->allow_intrabc) {
loopfilter_frame(cpi, cm);
} else {
cm->lf.filter_level[0] = 0;
cm->lf.filter_level[1] = 0;
cm->cdef_info.cdef_bits = 0;
cm->cdef_info.cdef_strengths[0] = 0;
cm->cdef_info.nb_cdef_strengths = 1;
cm->cdef_info.cdef_uv_strengths[0] = 0;
cm->rst_info[0].frame_restoration_type = RESTORE_NONE;
cm->rst_info[1].frame_restoration_type = RESTORE_NONE;
cm->rst_info[2].frame_restoration_type = RESTORE_NONE;
#if CONFIG_CNN_RESTORATION && !CONFIG_LOOP_RESTORE_CNN
cm->use_cnn = 0;
#endif // CONFIG_CNN_RESTORATION && !CONFIG_LOOP_RESTORE_CNN
#if CRLC_LF
cm->use_cnn = 0;
#endif
}
// TODO(debargha): Fix mv search range on encoder side
// aom_extend_frame_inner_borders(&cm->cur_frame->buf, av1_num_planes(cm));
aom_extend_frame_borders(&cm->cur_frame->buf, av1_num_planes(cm));
#ifdef OUTPUT_YUV_REC
aom_write_one_yuv_frame(cm, &cm->cur_frame->buf);
#endif
finalize_encoded_frame(cpi);
// Build the bitstream
int largest_tile_id = 0; // Output from pack_bitstream
#if CONFIG_COLLECT_COMPONENT_TIMING
start_timing(cpi, av1_pack_bitstream_final_time);
#endif
if (av1_pack_bitstream(cpi, dest, size, &largest_tile_id) !=
AOM_CODEC_OK) //码流打包好
return AOM_CODEC_ERROR;
#if CONFIG_COLLECT_COMPONENT_TIMING
end_timing(cpi, av1_pack_bitstream_final_time);
#endif
cpi->seq_params_locked = 1;
// Update reference frame ids for reference frames this frame will overwrite
//更新参考帧的参考帧ID,此帧将覆盖
if (seq_params->frame_id_numbers_present_flag) {
for (int i = 0; i < REF_FRAMES; i++) {
if ((current_frame->refresh_frame_flags >> i) & 1) {
cm->ref_frame_id[i] = cm->current_frame_id;
}
}
}
#if DUMP_RECON_FRAMES == 1
// NOTE(zoeliu): For debug - Output the filtered reconstructed video.
dump_filtered_recon_frames(cpi);
#endif // DUMP_RECON_FRAMES
//分块的话更新分块的信息映射
if (cm->seg.enabled) {
if (cm->seg.update_map) {
update_reference_segmentation_map(cpi);
} else if (cm->last_frame_seg_map) {
memcpy(cm->cur_frame->seg_map, cm->last_frame_seg_map,
cm->mi_cols * cm->mi_rows * sizeof(uint8_t));
}
}
//只帧内编码的话不用参考帧
if (frame_is_intra_only(cm) == 0) {
release_scaled_references(cpi);
}
// NOTE: Save the new show frame buffer index for --test-code=warn, i.e.,
// for the purpose to verify no mismatch between encoder and decoder.
//注意:将新的显示帧缓冲区索引保存为--test code=warn,即。,
//以验证编码器和解码器之间没有不匹配。
if (cm->show_frame) cpi->last_show_frame_buf = cm->cur_frame;
refresh_reference_frames(cpi);
#if CONFIG_ENTROPY_STATS
av1_accumulate_frame_counts(&aggregate_fc, &cpi->counts);
#endif // CONFIG_ENTROPY_STATS
//解码末尾要更新的帧内容?
if (cm->refresh_frame_context == REFRESH_FRAME_CONTEXT_BACKWARD) {
*cm->fc = cpi->tile_data[largest_tile_id].tctx;
av1_reset_cdf_symbol_counters(cm->fc);
}
if (!cm->large_scale_tile) {
cm->cur_frame->frame_context = *cm->fc;
}
#define EXT_TILE_DEBUG 0
#if EXT_TILE_DEBUG
if (cm->large_scale_tile && oxcf->pass == 2) {
char fn[20] = "./fc";
fn[4] = current_frame->frame_number / 100 + '0';
fn[5] = (current_frame->frame_number % 100) / 10 + '0';
fn[6] = (current_frame->frame_number % 10) + '0';
fn[7] = '\0';
av1_print_frame_contexts(cm->fc, fn);
}
#endif // EXT_TILE_DEBUG
#undef EXT_TILE_DEBUG
#if CONFIG_COLLECT_COMPONENT_TIMING
end_timing(cpi, encode_frame_to_data_rate_time);
// Print out timing information.
int i;
fprintf(stderr, "\n Frame number: %d, Frame type: %s, Show Frame: %d\n",
cm->current_frame.frame_number,
get_frame_type_enum(cm->current_frame.frame_type), cm->show_frame);
for (i = 0; i < kTimingComponents; i++) {
cpi->component_time[i] += cpi->frame_component_time[i];
fprintf(stderr, " %s: %" PRId64 " us (total: %" PRId64 " us)\n",
get_component_name(i), cpi->frame_component_time[i],
cpi->component_time[i]);
cpi->frame_component_time[i] = 0;
}
#endif
cm->last_frame_type = current_frame->frame_type;
av1_rc_postencode_update(cpi, *size);
// Store encoded frame's hash table for is_integer_mv() next time
if (oxcf->pass != 1 && cpi->common.allow_screen_content_tools) {
cpi->previous_hash_table = &cm->cur_frame->hash_table;
}
// Clear the one shot update flags for segmentation map and mode/ref loop
// filter deltas.
cm->seg.update_map = 0;
cm->seg.update_data = 0;
cm->lf.mode_ref_delta_update = 0;
// A droppable frame might not be shown but it always
// takes a space in the gf group. Therefore, even when
// it is not shown, we still need update the count down.
if (cm->show_frame) {
// TODO(zoeliu): We may only swamp mi and prev_mi for those frames that
// are
// being used as reference.
swap_mi_and_prev_mi(cm);
// Don't increment frame counters if this was an altref buffer
// update not a real frame
++current_frame->frame_number;
}
return AOM_CODEC_OK;
}
网友评论