chromium/third_party/libaom/source/libaom/av1/encoder/encoder_utils.c

/*
 * Copyright (c) 2020, Alliance for Open Media. All rights reserved.
 *
 * This source code is subject to the terms of the BSD 2 Clause License and
 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
 * was not distributed with this source code in the LICENSE file, you can
 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
 * Media Patent License 1.0 was not distributed with this source code in the
 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
 */

#include <string.h>

#include "aom/aomcx.h"

#include "av1/common/av1_common_int.h"
#include "av1/encoder/bitstream.h"
#include "av1/encoder/encodeframe.h"
#include "av1/encoder/encoder.h"
#include "av1/encoder/encoder_alloc.h"
#include "av1/encoder/encodetxb.h"
#include "av1/encoder/encoder_utils.h"
#include "av1/encoder/grain_test_vectors.h"
#include "av1/encoder/mv_prec.h"
#include "av1/encoder/rc_utils.h"
#include "av1/encoder/rdopt.h"
#include "av1/encoder/segmentation.h"
#include "av1/encoder/superres_scale.h"
#include "av1/encoder/tpl_model.h"
#include "av1/encoder/var_based_part.h"

#if CONFIG_TUNE_VMAF
#include "av1/encoder/tune_vmaf.h"
#endif

#define MIN_BOOST_COMBINE_FACTOR
#define MAX_BOOST_COMBINE_FACTOR

const int default_tx_type_probs[FRAME_UPDATE_TYPES][TX_SIZES_ALL][TX_TYPES] =;

const int default_obmc_probs[FRAME_UPDATE_TYPES][BLOCK_SIZES_ALL] =;

const int default_warped_probs[FRAME_UPDATE_TYPES] =;

// TODO(yunqing): the default probs can be trained later from better
// performance.
const int default_switchable_interp_probs[FRAME_UPDATE_TYPES]
                                         [SWITCHABLE_FILTER_CONTEXTS]
                                         [SWITCHABLE_FILTERS] =;

static void configure_static_seg_features(AV1_COMP *cpi) {}

void av1_apply_active_map(AV1_COMP *cpi) {}

#if !CONFIG_REALTIME_ONLY
static void process_tpl_stats_frame(AV1_COMP *cpi) {
  const GF_GROUP *const gf_group = &cpi->ppi->gf_group;
  AV1_COMMON *const cm = &cpi->common;

  assert(IMPLIES(gf_group->size > 0, cpi->gf_frame_index < gf_group->size));

  const int tpl_idx = cpi->gf_frame_index;
  TplParams *const tpl_data = &cpi->ppi->tpl_data;
  TplDepFrame *tpl_frame = &tpl_data->tpl_frame[tpl_idx];
  TplDepStats *tpl_stats = tpl_frame->tpl_stats_ptr;

  if (tpl_frame->is_valid) {
    int tpl_stride = tpl_frame->stride;
    double intra_cost_base = 0;
    double mc_dep_cost_base = 0;
    double cbcmp_base = 1;
    const int step = 1 << tpl_data->tpl_stats_block_mis_log2;
    const int row_step = step;
    const int col_step_sr =
        coded_to_superres_mi(step, cm->superres_scale_denominator);
    const int mi_cols_sr = av1_pixels_to_mi(cm->superres_upscaled_width);

    for (int row = 0; row < cm->mi_params.mi_rows; row += row_step) {
      for (int col = 0; col < mi_cols_sr; col += col_step_sr) {
        TplDepStats *this_stats = &tpl_stats[av1_tpl_ptr_pos(
            row, col, tpl_stride, tpl_data->tpl_stats_block_mis_log2)];
        double cbcmp = (double)(this_stats->srcrf_dist);
        int64_t mc_dep_delta =
            RDCOST(tpl_frame->base_rdmult, this_stats->mc_dep_rate,
                   this_stats->mc_dep_dist);
        double dist_scaled = (double)(this_stats->recrf_dist << RDDIV_BITS);
        intra_cost_base += log(dist_scaled) * cbcmp;
        mc_dep_cost_base += log(dist_scaled + mc_dep_delta) * cbcmp;
        cbcmp_base += cbcmp;
      }
    }

    if (mc_dep_cost_base == 0) {
      tpl_frame->is_valid = 0;
    } else {
      cpi->rd.r0 = exp((intra_cost_base - mc_dep_cost_base) / cbcmp_base);
      if (is_frame_tpl_eligible(gf_group, cpi->gf_frame_index)) {
        if (cpi->ppi->lap_enabled) {
          double min_boost_factor = sqrt(cpi->ppi->p_rc.baseline_gf_interval);
          const int gfu_boost = get_gfu_boost_from_r0_lap(
              min_boost_factor, MAX_GFUBOOST_FACTOR, cpi->rd.r0,
              cpi->ppi->p_rc.num_stats_required_for_gfu_boost);
          // printf("old boost %d new boost %d\n", cpi->rc.gfu_boost,
          //        gfu_boost);
          cpi->ppi->p_rc.gfu_boost = combine_prior_with_tpl_boost(
              min_boost_factor, MAX_BOOST_COMBINE_FACTOR,
              cpi->ppi->p_rc.gfu_boost, gfu_boost,
              cpi->ppi->p_rc.num_stats_used_for_gfu_boost);
        } else {
          // TPL may only look at a subset of frame in the gf group when the
          // speed feature 'reduce_num_frames' is on, which affects the r0
          // calcuation. Thus, to compensate for TPL not using all frames a
          // factor to adjust r0 is used.
          const int gfu_boost =
              (int)(200.0 * cpi->ppi->tpl_data.r0_adjust_factor / cpi->rd.r0);
          cpi->ppi->p_rc.gfu_boost = combine_prior_with_tpl_boost(
              MIN_BOOST_COMBINE_FACTOR, MAX_BOOST_COMBINE_FACTOR,
              cpi->ppi->p_rc.gfu_boost, gfu_boost, cpi->rc.frames_to_key);
        }
      }
    }
  }
}
#endif  // !CONFIG_REALTIME_ONLY

void av1_set_size_dependent_vars(AV1_COMP *cpi, int *q, int *bottom_index,
                                 int *top_index) {}

static void reset_film_grain_chroma_params(aom_film_grain_t *pars) {}

void av1_update_film_grain_parameters_seq(struct AV1_PRIMARY *ppi,
                                          const AV1EncoderConfig *oxcf) {}

void av1_update_film_grain_parameters(struct AV1_COMP *cpi,
                                      const AV1EncoderConfig *oxcf) {}

void av1_scale_references(AV1_COMP *cpi, const InterpFilter filter,
                          const int phase, const int use_optimized_scaler) {}

BLOCK_SIZE av1_select_sb_size(const AV1EncoderConfig *const oxcf, int width,
                              int height, int number_spatial_layers) {}

void av1_setup_frame(AV1_COMP *cpi) {}

#if !CONFIG_REALTIME_ONLY
static int get_interp_filter_selected(const AV1_COMMON *const cm,
                                      MV_REFERENCE_FRAME ref,
                                      InterpFilter ifilter) {
  const RefCntBuffer *const buf = get_ref_frame_buf(cm, ref);
  if (buf == NULL) return 0;
  return buf->interp_filter_selected[ifilter];
}

uint16_t av1_setup_interp_filter_search_mask(AV1_COMP *cpi) {
  const AV1_COMMON *const cm = &cpi->common;
  int ref_total[REF_FRAMES] = { 0 };
  uint16_t mask = ALLOW_ALL_INTERP_FILT_MASK;

  if (cpi->last_frame_type == KEY_FRAME || cpi->refresh_frame.alt_ref_frame)
    return mask;

  for (MV_REFERENCE_FRAME ref = LAST_FRAME; ref <= ALTREF_FRAME; ++ref) {
    for (InterpFilter ifilter = EIGHTTAP_REGULAR; ifilter <= MULTITAP_SHARP;
         ++ifilter) {
      ref_total[ref] += get_interp_filter_selected(cm, ref, ifilter);
    }
  }
  int ref_total_total = (ref_total[LAST2_FRAME] + ref_total[LAST3_FRAME] +
                         ref_total[GOLDEN_FRAME] + ref_total[BWDREF_FRAME] +
                         ref_total[ALTREF2_FRAME] + ref_total[ALTREF_FRAME]);

  for (InterpFilter ifilter = EIGHTTAP_REGULAR; ifilter <= MULTITAP_SHARP;
       ++ifilter) {
    int last_score = get_interp_filter_selected(cm, LAST_FRAME, ifilter) * 30;
    if (ref_total[LAST_FRAME] && last_score <= ref_total[LAST_FRAME]) {
      int filter_score =
          get_interp_filter_selected(cm, LAST2_FRAME, ifilter) * 20 +
          get_interp_filter_selected(cm, LAST3_FRAME, ifilter) * 20 +
          get_interp_filter_selected(cm, GOLDEN_FRAME, ifilter) * 20 +
          get_interp_filter_selected(cm, BWDREF_FRAME, ifilter) * 10 +
          get_interp_filter_selected(cm, ALTREF2_FRAME, ifilter) * 10 +
          get_interp_filter_selected(cm, ALTREF_FRAME, ifilter) * 10;
      if (filter_score < ref_total_total) {
        DUAL_FILTER_TYPE filt_type = ifilter + SWITCHABLE_FILTERS * ifilter;
        reset_interp_filter_allowed_mask(&mask, filt_type);
      }
    }
  }
  return mask;
}

#define STRICT_PSNR_DIFF_THRESH
// Encode key frame with/without screen content tools to determine whether
// screen content tools should be enabled for this key frame group or not.
// The first encoding is without screen content tools.
// The second encoding is with screen content tools.
// We compare the psnr and frame size to make the decision.
static void screen_content_tools_determination(
    AV1_COMP *cpi, const int allow_screen_content_tools_orig_decision,
    const int allow_intrabc_orig_decision,
    const int use_screen_content_tools_orig_decision,
    const int is_screen_content_type_orig_decision, const int pass,
    int *projected_size_pass, PSNR_STATS *psnr) {
  AV1_COMMON *const cm = &cpi->common;
  FeatureFlags *const features = &cm->features;

#if CONFIG_FPMT_TEST
  projected_size_pass[pass] =
      ((cpi->ppi->gf_group.frame_parallel_level[cpi->gf_frame_index] > 0) &&
       (cpi->ppi->fpmt_unit_test_cfg == PARALLEL_SIMULATION_ENCODE))
          ? cpi->ppi->p_rc.temp_projected_frame_size
          : cpi->rc.projected_frame_size;
#else
  projected_size_pass[pass] = cpi->rc.projected_frame_size;
#endif

#if CONFIG_AV1_HIGHBITDEPTH
  const uint32_t in_bit_depth = cpi->oxcf.input_cfg.input_bit_depth;
  const uint32_t bit_depth = cpi->td.mb.e_mbd.bd;
  aom_calc_highbd_psnr(cpi->source, &cpi->common.cur_frame->buf, &psnr[pass],
                       bit_depth, in_bit_depth);
#else
  aom_calc_psnr(cpi->source, &cpi->common.cur_frame->buf, &psnr[pass]);
#endif
  if (pass != 1) return;

  const double psnr_diff = psnr[1].psnr[0] - psnr[0].psnr[0];
  // Calculate % of palette mode to be chosen in a frame from mode decision.
  const double palette_ratio =
      (double)cpi->palette_pixel_num / (double)(cm->height * cm->width);
  const int psnr_diff_is_large = (psnr_diff > STRICT_PSNR_DIFF_THRESH);
  const int ratio_is_large =
      ((palette_ratio >= 0.0001) && ((psnr_diff / palette_ratio) > 4));
  const int is_sc_encoding_much_better = (psnr_diff_is_large || ratio_is_large);
  if (is_sc_encoding_much_better) {
    // Use screen content tools, if we get coding gain.
    features->allow_screen_content_tools = 1;
    features->allow_intrabc = cpi->intrabc_used;
    cpi->use_screen_content_tools = 1;
    cpi->is_screen_content_type = 1;
  } else {
    // Use original screen content decision.
    features->allow_screen_content_tools =
        allow_screen_content_tools_orig_decision;
    features->allow_intrabc = allow_intrabc_orig_decision;
    cpi->use_screen_content_tools = use_screen_content_tools_orig_decision;
    cpi->is_screen_content_type = is_screen_content_type_orig_decision;
  }
}

// Set some encoding parameters to make the encoding process fast.
// A fixed block partition size, and a large q is used.
static void set_encoding_params_for_screen_content(AV1_COMP *cpi,
                                                   const int pass) {
  AV1_COMMON *const cm = &cpi->common;
  if (pass == 0) {
    // In the first pass, encode without screen content tools.
    // Use a high q, and a fixed block size for fast encoding.
    cm->features.allow_screen_content_tools = 0;
    cm->features.allow_intrabc = 0;
    cpi->use_screen_content_tools = 0;
    cpi->sf.part_sf.partition_search_type = FIXED_PARTITION;
    cpi->sf.part_sf.fixed_partition_size = BLOCK_32X32;
    return;
  }
  assert(pass == 1);
  // In the second pass, encode with screen content tools.
  // Use a high q, and a fixed block size for fast encoding.
  cm->features.allow_screen_content_tools = 1;
  // TODO(chengchen): turn intrabc on could lead to data race issue.
  // cm->allow_intrabc = 1;
  cpi->use_screen_content_tools = 1;
  cpi->sf.part_sf.partition_search_type = FIXED_PARTITION;
  cpi->sf.part_sf.fixed_partition_size = BLOCK_32X32;
}

// Determines whether to use screen content tools for the key frame group.
// This function modifies "cm->features.allow_screen_content_tools",
// "cm->features.allow_intrabc" and "cpi->use_screen_content_tools".
void av1_determine_sc_tools_with_encoding(AV1_COMP *cpi, const int q_orig) {
  AV1_COMMON *const cm = &cpi->common;
  const AV1EncoderConfig *const oxcf = &cpi->oxcf;
  const QuantizationCfg *const q_cfg = &oxcf->q_cfg;
  // Variables to help determine if we should allow screen content tools.
  int projected_size_pass[3] = { 0 };
  PSNR_STATS psnr[3];
  const int is_key_frame = cm->current_frame.frame_type == KEY_FRAME;
  const int allow_screen_content_tools_orig_decision =
      cm->features.allow_screen_content_tools;
  const int allow_intrabc_orig_decision = cm->features.allow_intrabc;
  const int use_screen_content_tools_orig_decision =
      cpi->use_screen_content_tools;
  const int is_screen_content_type_orig_decision = cpi->is_screen_content_type;
  // Turn off the encoding trial for forward key frame and superres.
  if (cpi->sf.rt_sf.use_nonrd_pick_mode || oxcf->kf_cfg.fwd_kf_enabled ||
      cpi->superres_mode != AOM_SUPERRES_NONE || oxcf->mode == REALTIME ||
      use_screen_content_tools_orig_decision || !is_key_frame) {
    return;
  }

  // TODO(chengchen): multiple encoding for the lossless mode is time consuming.
  // Find a better way to determine whether screen content tools should be used
  // for lossless coding.
  // Use a high q and a fixed partition to do quick encoding.
  const int q_for_screen_content_quick_run =
      is_lossless_requested(&oxcf->rc_cfg) ? q_orig : AOMMAX(q_orig, 244);
  const int partition_search_type_orig = cpi->sf.part_sf.partition_search_type;
  const BLOCK_SIZE fixed_partition_block_size_orig =
      cpi->sf.part_sf.fixed_partition_size;

  // Setup necessary params for encoding, including frame source, etc.

  cpi->source = av1_realloc_and_scale_if_required(
      cm, cpi->unscaled_source, &cpi->scaled_source, cm->features.interp_filter,
      0, false, false, cpi->oxcf.border_in_pixels, cpi->alloc_pyramid);
  if (cpi->unscaled_last_source != NULL) {
    cpi->last_source = av1_realloc_and_scale_if_required(
        cm, cpi->unscaled_last_source, &cpi->scaled_last_source,
        cm->features.interp_filter, 0, false, false, cpi->oxcf.border_in_pixels,
        cpi->alloc_pyramid);
  }

  av1_setup_frame(cpi);

  if (cm->seg.enabled) {
    if (!cm->seg.update_data && cm->prev_frame) {
      segfeatures_copy(&cm->seg, &cm->prev_frame->seg);
      cm->seg.enabled = cm->prev_frame->seg.enabled;
    } else {
      av1_calculate_segdata(&cm->seg);
    }
  } else {
    memset(&cm->seg, 0, sizeof(cm->seg));
  }
  segfeatures_copy(&cm->cur_frame->seg, &cm->seg);
  cm->cur_frame->seg.enabled = cm->seg.enabled;

  // The two encoding passes aim to help determine whether to use screen
  // content tools, with a high q and fixed partition.
  for (int pass = 0; pass < 2; ++pass) {
    set_encoding_params_for_screen_content(cpi, pass);
    av1_set_quantizer(cm, q_cfg->qm_minlevel, q_cfg->qm_maxlevel,
                      q_for_screen_content_quick_run,
                      q_cfg->enable_chroma_deltaq, q_cfg->enable_hdr_deltaq);
    av1_set_speed_features_qindex_dependent(cpi, oxcf->speed);
    av1_init_quantizer(&cpi->enc_quant_dequant_params, &cm->quant_params,
                       cm->seq_params->bit_depth);

    av1_set_variance_partition_thresholds(cpi, q_for_screen_content_quick_run,
                                          0);
    // transform / motion compensation build reconstruction frame
    av1_encode_frame(cpi);
    // Screen content decision
    screen_content_tools_determination(
        cpi, allow_screen_content_tools_orig_decision,
        allow_intrabc_orig_decision, use_screen_content_tools_orig_decision,
        is_screen_content_type_orig_decision, pass, projected_size_pass, psnr);
  }

  // Set partition speed feature back.
  cpi->sf.part_sf.partition_search_type = partition_search_type_orig;
  cpi->sf.part_sf.fixed_partition_size = fixed_partition_block_size_orig;

  // Free token related info if screen content coding tools are not enabled.
  if (!cm->features.allow_screen_content_tools)
    free_token_info(&cpi->token_info);
}
#endif  // CONFIG_REALTIME_ONLY

static void fix_interp_filter(InterpFilter *const interp_filter,
                              const FRAME_COUNTS *const counts) {}

void av1_finalize_encoded_frame(AV1_COMP *const cpi) {}

int av1_is_integer_mv(const YV12_BUFFER_CONFIG *cur_picture,
                      const YV12_BUFFER_CONFIG *last_picture,
                      ForceIntegerMVInfo *const force_intpel_info) {}

void av1_set_mb_ssim_rdmult_scaling(AV1_COMP *cpi) {}

// Coding context that only needs to be saved when recode loop includes
// filtering (deblocking, CDEF, superres post-encode upscale and/or loop
// restoraton).
static void save_extra_coding_context(AV1_COMP *cpi) {}

void av1_save_all_coding_context(AV1_COMP *cpi) {}

#if DUMP_RECON_FRAMES == 1

// NOTE(zoeliu): For debug - Output the filtered reconstructed video.
void av1_dump_filtered_recon_frames(AV1_COMP *cpi) {
  AV1_COMMON *const cm = &cpi->common;
  const CurrentFrame *const current_frame = &cm->current_frame;
  const YV12_BUFFER_CONFIG *recon_buf = &cm->cur_frame->buf;

  if (recon_buf == NULL) {
    printf("Frame %d is not ready.\n", current_frame->frame_number);
    return;
  }

  static const int flag_list[REF_FRAMES] = { 0,
                                             AOM_LAST_FLAG,
                                             AOM_LAST2_FLAG,
                                             AOM_LAST3_FLAG,
                                             AOM_GOLD_FLAG,
                                             AOM_BWD_FLAG,
                                             AOM_ALT2_FLAG,
                                             AOM_ALT_FLAG };
  printf(
      "\n***Frame=%d (frame_offset=%d, show_frame=%d, "
      "show_existing_frame=%d) "
      "[LAST LAST2 LAST3 GOLDEN BWD ALT2 ALT]=[",
      current_frame->frame_number, current_frame->order_hint, cm->show_frame,
      cm->show_existing_frame);
  for (int ref_frame = LAST_FRAME; ref_frame <= ALTREF_FRAME; ++ref_frame) {
    const RefCntBuffer *const buf = get_ref_frame_buf(cm, ref_frame);
    const int ref_offset = buf != NULL ? (int)buf->order_hint : -1;
    printf(" %d(%c)", ref_offset,
           (cpi->ref_frame_flags & flag_list[ref_frame]) ? 'Y' : 'N');
  }
  printf(" ]\n");

  if (!cm->show_frame) {
    printf("Frame %d is a no show frame, so no image dump.\n",
           current_frame->frame_number);
    return;
  }

  int h;
  char file_name[256] = "/tmp/enc_filtered_recon.yuv";
  FILE *f_recon = NULL;

  if (current_frame->frame_number == 0) {
    if ((f_recon = fopen(file_name, "wb")) == NULL) {
      printf("Unable to open file %s to write.\n", file_name);
      return;
    }
  } else {
    if ((f_recon = fopen(file_name, "ab")) == NULL) {
      printf("Unable to open file %s to append.\n", file_name);
      return;
    }
  }
  printf(
      "\nFrame=%5d, encode_update_type[%5d]=%1d, frame_offset=%d, "
      "show_frame=%d, show_existing_frame=%d, source_alt_ref_active=%d, "
      "refresh_alt_ref_frame=%d, "
      "y_stride=%4d, uv_stride=%4d, cm->width=%4d, cm->height=%4d\n\n",
      current_frame->frame_number, cpi->gf_frame_index,
      cpi->ppi->gf_group.update_type[cpi->gf_frame_index],
      current_frame->order_hint, cm->show_frame, cm->show_existing_frame,
      cpi->rc.source_alt_ref_active, cpi->refresh_frame.alt_ref_frame,
      recon_buf->y_stride, recon_buf->uv_stride, cm->width, cm->height);
#if 0
  int ref_frame;
  printf("get_ref_frame_map_idx: [");
  for (ref_frame = LAST_FRAME; ref_frame <= ALTREF_FRAME; ++ref_frame)
    printf(" %d", get_ref_frame_map_idx(cm, ref_frame));
  printf(" ]\n");
#endif  // 0

  // --- Y ---
  for (h = 0; h < cm->height; ++h) {
    fwrite(&recon_buf->y_buffer[h * recon_buf->y_stride], 1, cm->width,
           f_recon);
  }
  // --- U ---
  for (h = 0; h < (cm->height >> 1); ++h) {
    fwrite(&recon_buf->u_buffer[h * recon_buf->uv_stride], 1, (cm->width >> 1),
           f_recon);
  }
  // --- V ---
  for (h = 0; h < (cm->height >> 1); ++h) {
    fwrite(&recon_buf->v_buffer[h * recon_buf->uv_stride], 1, (cm->width >> 1),
           f_recon);
  }

  fclose(f_recon);
}
#endif  // DUMP_RECON_FRAMES