美文网首页
av1代码学习6---encode_frame_to_data_

av1代码学习6---encode_frame_to_data_

作者: 青吟乐 | 来源:发表于2020-06-21 13:17 被阅读0次

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

    相关文章

      网友评论

          本文标题:av1代码学习6---encode_frame_to_data_

          本文链接:https://www.haomeiwen.com/subject/rvooxktx.html