1.av1_encode_strategy函数

av1_encode_strategy函数将实现high-level 编码策略,选择帧类型、帧位置等。它用这些决定的结果填充EncodeFrameParams结构,然后调用av1_encode函数(或者是denoise_and_encode函数)。

该函数基本流程是:

  1. 初始化
  2. 获取下一个要编码的源缓冲区(在第一次编码过程中,没有真正进行编码,所有source为NULL)
  3. 根据第一次编码过程的数据,获取当前帧的编码参数(av1_get_second_pass_params),设置当前帧类型
  4. 获取参考帧列表
  5. 调用denoise_and_encode或者av1_encode函数对当前帧进行编码
  6. 更新相应信息

代码和注释如下:

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;GF_GROUP *gf_group = &cpi->gf_group;ExternalFlags *const ext_flags = &cpi->ext_flags; //由帧级外部接口发出信号的标志。EncodeFrameInput frame_input;// EncodeFrameParams包含由av1_encode_strategy决定并传递给av1_encode的每帧编码参数EncodeFrameParams frame_params;// EncodeFrameResults包含有关对单个帧进行编码的结果的信息EncodeFrameResults frame_results;memset(&frame_input, 0, sizeof(frame_input));memset(&frame_params, 0, sizeof(frame_params));memset(&frame_results, 0, sizeof(frame_results));// TODO(sarahparker) finish bit allocation for one pass pyramidif (has_no_stats_stage(cpi) && oxcf->rc_mode != AOM_Q) {cpi->oxcf.gf_max_pyr_height =AOMMIN(cpi->oxcf.gf_max_pyr_height, USE_ALTREF_FOR_ONE_PASS);cpi->oxcf.gf_min_pyr_height =AOMMIN(cpi->oxcf.gf_min_pyr_height, cpi->oxcf.gf_max_pyr_height);}if (!is_stat_generation_stage(cpi)) {// 如果不是统计信息的阶段// If this is a forward keyframe, mark as a show_existing_frame// 如果这是一个向前的关键帧,标记为show_existing_frameif (cpi->oxcf.fwd_kf_enabled && (gf_group->index == gf_group->size) &&gf_group->update_type[1] == ARF_UPDATE && cpi->rc.frames_to_key == 0) {frame_params.show_existing_frame = 1;} else {frame_params.show_existing_frame =((oxcf->enable_overlay == 0 || cpi->sf.hl_sf.disable_overlay_frames ||cpi->show_existing_alt_ref) &&gf_group->update_type[gf_group->index] == OVERLAY_UPDATE) ||gf_group->update_type[gf_group->index] == INTNL_OVERLAY_UPDATE;}frame_params.show_existing_frame &= allow_show_existing(cpi, *frame_flags);// Reset show_existing_alt_ref decision to 0 after it is used.if (gf_group->update_type[gf_group->index] == OVERLAY_UPDATE) {cpi->show_existing_alt_ref = 0;}} else {frame_params.show_existing_frame = 0;}int code_arf = 0;struct lookahead_entry *source = NULL;struct lookahead_entry *last_source = NULL;if (frame_params.show_existing_frame) {source = av1_lookahead_pop(cpi->lookahead, flush, cpi->compressor_stage);frame_params.show_frame = 1;} else {int show_existing_alt_ref = 0;//检查我们是应该编码一个ARF还是internal ARF。如果没有,试试LAST//执行与所选源关联的某些设置//输出的是temporal_filtered, flush和frame_update_type类型。//返回帧源,如果找不到,则返回NULLsource = choose_frame_source(cpi, &code_arf, &flush, &last_source,&frame_params, &show_existing_alt_ref);if (gf_group->update_type[gf_group->index] == ARF_UPDATE)cpi->show_existing_alt_ref = show_existing_alt_ref;//告诉OVERLAY帧是否显示现有的alt_ref帧。}if (source == NULL) {  // If no source was found, we can't encode a frame.// 在第一次编码过程中,没有真正进行编码,所有source为NULL
#if !CONFIG_REALTIME_ONLYif (flush && oxcf->pass == 1 && !cpi->twopass.first_pass_done) {// 第一次编码过程结束时,会执行该函数,用来输出第一次编码过程的统计数据av1_end_first_pass(cpi); /* get last stats packet */cpi->twopass.first_pass_done = 1;}
#endifreturn -1;}frame_input.source = code_arf ? &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;// Save unfiltered source. It is used in av1_get_second_pass_params().cpi->unfiltered_source = frame_input.source; //保存为滤波的原图像*time_stamp = source->ts_start;*time_end = source->ts_end;if (source->ts_start < cpi->time_stamps.first_ever) {cpi->time_stamps.first_ever = source->ts_start;cpi->time_stamps.prev_end_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;// Shown frames and arf-overlay frames need frame-rate consideringif (frame_params.show_frame)adjust_frame_rate(cpi, source->ts_start, source->ts_end);if (!frame_params.show_existing_frame) {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 nowconst int64_t pts64 = ticks_to_timebase_units(timestamp_ratio, *time_stamp);if (pts64 < 0 || pts64 > UINT32_MAX) return AOM_CODEC_ERROR;cm->frame_presentation_time = (uint32_t)pts64;}#if CONFIG_REALTIME_ONLYav1_get_one_pass_rt_params(cpi, &frame_params, *frame_flags);
#elseif (has_no_stats_stage(cpi) && oxcf->mode == REALTIME &&oxcf->lag_in_frames == 0)av1_get_one_pass_rt_params(cpi, &frame_params, *frame_flags);else if (!is_stat_generation_stage(cpi))//获取第二次编码过程的当前帧的编码参数(根据第一次编码过程的数据)av1_get_second_pass_params(cpi, &frame_params, &frame_input, *frame_flags);
#endifFRAME_UPDATE_TYPE frame_update_type = get_frame_update_type(gf_group);// 设置当前帧的帧类型if (frame_params.show_existing_frame &&frame_params.frame_type != KEY_FRAME) {// Force show-existing frames to be INTER, except forward keyframesframe_params.frame_type = INTER_FRAME;}// TODO(david.turner@argondesign.com): Move all the encode strategy// (largely near av1_get_compressed_data) in here// TODO(david.turner@argondesign.com): 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;// Work out some encoding parameters specific to the pass:// 计算出一些特定于过程的编码参数:if (has_no_stats_stage(cpi) && cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ) {av1_cyclic_refresh_update_parameters(cpi);} else if (is_stat_generation_stage(cpi)) {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 (is_stat_consumption_stage(cpi)) {
#if CONFIG_MISMATCH_DEBUGmismatch_move_frame_idx_w();
#endif
#if TXCOEFF_COST_TIMERcm->txcoeff_cost_timer = 0;cm->txcoeff_cost_count = 0;
#endif}if (!is_stat_generation_stage(cpi))set_ext_overrides(cm, &frame_params, ext_flags);// Shown keyframes and S frames refresh all reference buffers// 显示的关键帧和S帧刷新所有引用缓冲区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 (!is_stat_generation_stage(cpi)) {const RefCntBuffer *ref_frames[INTER_REFS_PER_FRAME];//参考帧const YV12_BUFFER_CONFIG *ref_frame_buf[INTER_REFS_PER_FRAME];if (!ext_flags->refresh_frame_flags_pending) {av1_get_ref_frames(cpi, &cpi->ref_buffer_stack);} else if (cpi->svc.external_ref_frame_config) {for (unsigned int i = 0; i < INTER_REFS_PER_FRAME; i++)cm->remapped_ref_idx[i] = cpi->svc.ref_idx[i];}// Get the reference frames 获取参考帧for (int i = 0; i < INTER_REFS_PER_FRAME; ++i) {ref_frames[i] = get_ref_frame_buf(cm, ref_frame_priority_order[i]);ref_frame_buf[i] = ref_frames[i] != NULL ? &ref_frames[i]->buf : NULL;}// Work out which reference frame slots may be used. 找出可以使用的参考帧槽。frame_params.ref_frame_flags = get_ref_frame_flags(&cpi->sf, ref_frame_buf, ext_flags->ref_frame_flags);frame_params.primary_ref_frame =choose_primary_ref_frame(cpi, &frame_params);frame_params.order_offset = get_order_offset(&cpi->gf_group, &frame_params);frame_params.refresh_frame_flags = av1_get_refresh_frame_flags(cpi, &frame_params, frame_update_type, &cpi->ref_buffer_stack);frame_params.existing_fb_idx_to_show =frame_params.show_existing_frame? (frame_update_type == INTNL_OVERLAY_UPDATE? get_ref_frame_map_idx(cm, BWDREF_FRAME): get_ref_frame_map_idx(cm, ALTREF_FRAME)): INVALID_IDX;}// 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));cpi->td.mb.e_mbd.delta_qindex = 0;if (!frame_params.show_existing_frame) {cm->quant_params.using_qmatrix = cpi->oxcf.using_qm;
#if !CONFIG_REALTIME_ONLYif (oxcf->lag_in_frames > 0 && !is_stat_generation_stage(cpi)) {if (cpi->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, 0, &frame_params, &frame_input);assert(cpi->num_gf_group_show_frames == 1);}}
#endif}#if CONFIG_REALTIME_ONLYif (av1_encode(cpi, dest, &frame_input, &frame_params, &frame_results) !=AOM_CODEC_OK) {return AOM_CODEC_ERROR;}
#else// 对当前帧进行编码// 对关键帧应用时间滤波并对过滤后的帧进行编码。// 如果当前帧不是关键帧,则此函数与av1_encode相同。if (denoise_and_encode(cpi, dest, &frame_input, &frame_params,&frame_results) != AOM_CODEC_OK) {return AOM_CODEC_ERROR;}
#endif  // CONFIG_REALTIME_ONLYif (!is_stat_generation_stage(cpi))cpi->num_gf_group_show_frames += frame_params.show_frame;if (!is_stat_generation_stage(cpi)) {// First pass doesn't modify reference buffer assignment or produce frame// flags 第一个过程不修改引用缓冲区分配或生成帧标志update_frame_flags(cpi, frame_flags);if (!ext_flags->refresh_frame_flags_pending) {int ref_map_index =av1_get_refresh_ref_frame_map(cm->current_frame.refresh_frame_flags);av1_update_ref_frame_map(cpi, frame_update_type, cm->show_existing_frame,ref_map_index, &cpi->ref_buffer_stack);}}#if !CONFIG_REALTIME_ONLYif (!is_stat_generation_stage(cpi)) {
#if TXCOEFF_COST_TIMERcm->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);
#endifav1_twopass_postencode_update(cpi);}
#endif  // !CONFIG_REALTIME_ONLYif (!is_stat_generation_stage(cpi)) {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 droppableif (*size > 0) {cpi->droppable = is_frame_droppable(&cpi->svc, ext_flags);}if (cpi->use_svc) av1_save_layer_context(cpi);return AOM_CODEC_OK;
}

2.denoise_and_encode函数

该函数对当前帧进行编码,分为以下两种情况:

  • 如果当前帧是关键帧应用时域滤波并对滤波后的帧进行编码。
  • 如果当前帧不是关键帧,则此函数与av1_encode相同。
static int denoise_and_encode(AV1_COMP *const cpi, uint8_t *const dest,EncodeFrameInput *const frame_input,EncodeFrameParams *const frame_params,EncodeFrameResults *const frame_results) {const AV1EncoderConfig *const oxcf = &cpi->oxcf;AV1_COMMON *const cm = &cpi->common;// Decide whether to apply temporal filtering to the source frame.// 决定是否对源帧应用时域滤波。int apply_filtering =frame_params->frame_type == KEY_FRAME &&oxcf->enable_keyframe_filtering && !is_stat_generation_stage(cpi) &&!frame_params->show_existing_frame &&cpi->rc.frames_to_key > TF_NUM_FILTERING_FRAMES_FOR_KEY_FRAME &&!is_lossless_requested(oxcf) && oxcf->arnr_max_frames > 0;if (apply_filtering) {const double y_noise_level = av1_estimate_noise_from_single_plane(frame_input->source, 0, cm->seq_params.bit_depth);apply_filtering = y_noise_level > 0;}// Save the pointer to the original source image.YV12_BUFFER_CONFIG *source_kf_buffer = frame_input->source;// Apply filtering to key frame.对关键帧应用过滤。if (apply_filtering) {// Initialization for frame motion estimation.MACROBLOCKD *const xd = &cpi->td.mb.e_mbd;av1_init_mi_buffers(&cm->mi_params);setup_mi(cpi, frame_input->source);av1_init_macroblockd(cm, xd, NULL);memset(cpi->mbmi_ext_info.frame_base, 0,cpi->mbmi_ext_info.alloc_size * sizeof(*cpi->mbmi_ext_info.frame_base));av1_set_speed_features_framesize_independent(cpi, oxcf->speed);av1_set_speed_features_framesize_dependent(cpi, oxcf->speed);av1_set_rd_speed_thresholds(cpi);av1_setup_frame_buf_refs(cm);av1_setup_frame_sign_bias(cm);av1_frame_init_quantizer(cpi);av1_setup_past_independence(cm);if (!frame_params->show_frame) {int arf_src_index = get_arf_src_index(&cpi->gf_group, cpi->oxcf.pass);av1_temporal_filter(cpi, -1 * arf_src_index, NULL);} else {av1_temporal_filter(cpi, -1, NULL);}aom_extend_frame_borders(&cpi->alt_ref_buffer, av1_num_planes(cm));// Use the filtered frame for encoding.frame_input->source = &cpi->alt_ref_buffer;// Copy metadata info to alt-ref buffer.aom_remove_metadata_from_frame_buffer(frame_input->source);aom_copy_metadata_to_frame_buffer(frame_input->source,source_kf_buffer->metadata);if (oxcf->enable_tpl_model && oxcf->lag_in_frames > 0 &&frame_params->show_frame) {av1_tpl_setup_stats(cpi, 0, frame_params, frame_input);}}if (av1_encode(cpi, dest, frame_input, frame_params, frame_results) !=AOM_CODEC_OK) {return AOM_CODEC_ERROR;}// Set frame_input source to true source for psnr calculation.if (apply_filtering) {cpi->source = source_kf_buffer;cpi->unscaled_source = source_kf_buffer;}return AOM_CODEC_OK;
}

3.av1_encode函数

av1_encode函数将av1_encode_strategy中更新的参数(EncodeFrameInput和EncodeFrameParams)来更新AV1_COMP和AV1_COMMON中相对应的参数。如果是第一次编码,则进入av1_first_pass函数;否则进入encode_frame_to_data_rate函数进行编码。

int av1_encode(AV1_COMP *const cpi, uint8_t *const dest,const EncodeFrameInput *const frame_input,const EncodeFrameParams *const frame_params,EncodeFrameResults *const frame_results) {AV1_COMMON *const cm = &cpi->common;CurrentFrame *const current_frame = &cm->current_frame;cpi->unscaled_source = frame_input->source;cpi->source = frame_input->source;cpi->unscaled_last_source = frame_input->last_source;current_frame->refresh_frame_flags = frame_params->refresh_frame_flags;cm->features.error_resilient_mode = frame_params->error_resilient_mode;cm->features.primary_ref_frame = frame_params->primary_ref_frame;cm->current_frame.frame_type = frame_params->frame_type;cm->show_frame = frame_params->show_frame;cpi->ref_frame_flags = frame_params->ref_frame_flags;cpi->speed = frame_params->speed;cm->show_existing_frame = frame_params->show_existing_frame;cpi->existing_fb_idx_to_show = frame_params->existing_fb_idx_to_show;memcpy(cm->remapped_ref_idx, frame_params->remapped_ref_idx,REF_FRAMES * sizeof(*cm->remapped_ref_idx));cpi->refresh_golden_frame = frame_params->refresh_golden_frame;cpi->refresh_bwd_ref_frame = frame_params->refresh_bwd_ref_frame;cpi->refresh_alt_ref_frame = frame_params->refresh_alt_ref_frame;if (current_frame->frame_type == KEY_FRAME && cm->show_frame)current_frame->frame_number = 0;current_frame->order_hint =current_frame->frame_number + frame_params->order_offset;current_frame->display_order_hint = current_frame->order_hint;current_frame->order_hint %=(1 << (cm->seq_params.order_hint_info.order_hint_bits_minus_1 + 1));if (is_stat_generation_stage(cpi)) {
#if !CONFIG_REALTIME_ONLYav1_first_pass(cpi, frame_input->ts_duration);
#endif} else if (cpi->oxcf.pass == 0 || cpi->oxcf.pass == 2) {if (encode_frame_to_data_rate(cpi, &frame_results->size, dest) !=AOM_CODEC_OK) {return AOM_CODEC_ERROR;}} else {return AOM_CODEC_ERROR;}return AOM_CODEC_OK;
}

AV1代码学习:av1_encode_strategy、denoise_and_encode、av1_encode函数相关推荐

  1. HEVC代码学习42:estIntraPredLumaQT函数

    在之前的 HEVC代码学习37:帧内预测代码整体学习 中已经提到,estIntraPredLumaQT是亮度帧内预测的入口函数,下面将对该函数进行详细学习. estIntraPredLumaQT中完成 ...

  2. AV1代码学习6:函数av1_encode和 av1_first_pass

    av1_encode没什么特别好说的,会把在av1_encode_strategy的参数(EncodeFrameInput和EncodeFrameParams)赋给结构体AV1_COMP和AV1_CO ...

  3. AV1代码学习:av1_foreach_transformed_block_in_plane函数

    在AV1中,进行预测变换都是基于Transform Block(变换块)进行的,变换块一共19种尺寸,并且其尺寸通常是小于或者等于编码块尺寸的,如下代码所示. enum {TX_4X4, // 4x4 ...

  4. AV1代码学习3:函数aom_codec_encode

    函数aom_codec_encode主要就是根据命令行参数--cpu-used来决定num_enc, 通常情况下,为了通测方便,--cpu-used都是设置为1. 提高--cpu-used的数值会加快 ...

  5. AV1代码学习:函数encode_frame和aom_codec_encode

    1.encode_frame函数 在编码端aomenc.c的main函数中,在进入编码过程循环后,循环读取视频的每一帧数据,然后通过encode_frame函数对每一帧进行编码. encode_fra ...

  6. AV1代码学习6:tpl_model之一

    AV1的tpl_model是AV1的一个c文件,包含了一系列函数,其主要目的是为了利用lookahead design记录每个块的一些数据,包括失真等,在实际编码时利用这些数据建立模型,调整QP或者l ...

  7. HEVC代码学习13:predInterSearch函数

    在上一章的xCheckRDCostInter学习中,我们知道了,进行帧间搜索的入口实际是predInterSearch,今天我们就来对他进行学习. 推荐看大神博客 http://blog.csdn.n ...

  8. HEVC代码学习27:calcRdCost函数

    之前在率失真代价TComRdCost类中提到,计算率失真代价的函数为:calcRdCost,下面就来学习一下. calcRdCost工作流程如下: 1.根据输入的失真类型eDFunc来设置λ. 2.根 ...

  9. HEVC代码学习35:xEncodeCU函数

    xEncodeCU是由encodeCtu调用,其作用是从CTU开始迭代对每个CU进行编码.注意,xEncodeCU是在最优分块已经划分完成后进行编码时使用的,在xCompressCU中没有使用. xE ...

  10. AV1代码学习6:tpl_model之二

    mode_estimation字面意思就是模式估计,实质上是对帧内和帧间的模式进行遍历.帧内预测选取了13种模式,主要是DC模式.角度模式和新加入的PAETH模式.帧间对7个参考帧进行遍历,寻找COS ...

最新文章

  1. 新一代人工智能白皮书(2020年) ——产业智能化升级
  2. maven仓库阿里云镜像配置
  3. 安卓之页面跳转与传值和按钮事件
  4. python PyQt5 QtWidgets.QWidget.setLayout()(将布局中的小部件重新父级化,以将窗口作为父级)
  5. 输入正方形对角线两个端点坐标,求中点坐标
  6. 【计算机组成原理】各种码表示的数
  7. ERP开放平台定制化远程高效协作秘笈
  8. 数据结构与算法-- 二叉树中和为某一值的路径
  9. java参数后面跟三个点是什么意思
  10. BugkuCTF-WEB题本地管理员
  11. 双向链表中插入结点并输出
  12. 选择题:JAVA的类和对象
  13. 第二届蓝桥杯省赛---夺冠概率
  14. 属于自己的MES(二)必备的主数据
  15. Python的程序结构[2] - 类/Class[1] - 基类与继承
  16. K8S coreDNS部署及简单验证
  17. opencv 模板匹配形状匹配
  18. easyui事件方法onChange()、onSelect()、 onLoadSuccess()
  19. URAL 1099 Work Scheduling
  20. 【转载】FreeIPA中间CA证书已过期

热门文章

  1. python中角度变弧度_Python中转换角度为弧度的radians()方法
  2. 常见泰勒展开公式及复杂泰勒展开求法
  3. c语言 获取中文首字母,C语言获取汉字拼音首字母
  4. 浏览器打开就是360导航(浏览器被360劫持)
  5. 《数据库原理与应用》作业(1-0.5)
  6. trunk与access的区别
  7. 分数乘法计算机题,分数乘法计算题100道测试卷(无答案)
  8. bootstraptable冻结列无效_Bootstrap Table 冻结列功能详解
  9. oracle 还原imp,Oracle学习笔记——imp还原数据库
  10. android 汉字拼音转换工具