av1代码学习6---encode_frame_to_data_rate()

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