Move adaptive int_mv to encode_frame_to_data_rate is_integer_mv() is used to determine whether to force integer MVs in this frame. In adaptive mode the previous frame is taken into account. This patch moves this decision from av1_get_compressed_data() to encode_frame_to_data_rate(), as this is an internal frame encoding decision rather than high-level strategy This forms part of wider restructuring and refactoring in order to achieve a clean API separation at the entry to the low-level encoder. BUG=aomedia:2244 Change-Id: I1972af5e8d478d1f30661c06837d411a7d6f7600
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c index 78e07da..774efef 100644 --- a/av1/encoder/encoder.c +++ b/av1/encoder/encoder.c
@@ -5085,6 +5085,139 @@ return mask; } +static int is_integer_mv(AV1_COMP *cpi, const YV12_BUFFER_CONFIG *cur_picture, + const YV12_BUFFER_CONFIG *last_picture, + hash_table *last_hash_table) { + aom_clear_system_state(); + // check use hash ME + int k; + uint32_t hash_value_1; + uint32_t hash_value_2; + + const int block_size = 8; + const double threshold_current = 0.8; + const double threshold_average = 0.95; + const int max_history_size = 32; + int T = 0; // total block + int C = 0; // match with collocated block + int S = 0; // smooth region but not match with collocated block + int M = 0; // match with other block + + const int pic_width = cur_picture->y_width; + const int pic_height = cur_picture->y_height; + for (int i = 0; i + block_size <= pic_height; i += block_size) { + for (int j = 0; j + block_size <= pic_width; j += block_size) { + const int x_pos = j; + const int y_pos = i; + int match = 1; + T++; + + // check whether collocated block match with current + uint8_t *p_cur = cur_picture->y_buffer; + uint8_t *p_ref = last_picture->y_buffer; + int stride_cur = cur_picture->y_stride; + int stride_ref = last_picture->y_stride; + p_cur += (y_pos * stride_cur + x_pos); + p_ref += (y_pos * stride_ref + x_pos); + + if (cur_picture->flags & YV12_FLAG_HIGHBITDEPTH) { + uint16_t *p16_cur = CONVERT_TO_SHORTPTR(p_cur); + uint16_t *p16_ref = CONVERT_TO_SHORTPTR(p_ref); + for (int tmpY = 0; tmpY < block_size && match; tmpY++) { + for (int tmpX = 0; tmpX < block_size && match; tmpX++) { + if (p16_cur[tmpX] != p16_ref[tmpX]) { + match = 0; + } + } + p16_cur += stride_cur; + p16_ref += stride_ref; + } + } else { + for (int tmpY = 0; tmpY < block_size && match; tmpY++) { + for (int tmpX = 0; tmpX < block_size && match; tmpX++) { + if (p_cur[tmpX] != p_ref[tmpX]) { + match = 0; + } + } + p_cur += stride_cur; + p_ref += stride_ref; + } + } + + if (match) { + C++; + continue; + } + + if (av1_hash_is_horizontal_perfect(cur_picture, block_size, x_pos, + y_pos) || + av1_hash_is_vertical_perfect(cur_picture, block_size, x_pos, y_pos)) { + S++; + continue; + } + + av1_get_block_hash_value( + cur_picture->y_buffer + y_pos * stride_cur + x_pos, stride_cur, + block_size, &hash_value_1, &hash_value_2, + (cur_picture->flags & YV12_FLAG_HIGHBITDEPTH), &cpi->td.mb); + // Hashing does not work for highbitdepth currently. + // TODO(Roger): Make it work for highbitdepth. + if (av1_use_hash_me(&cpi->common)) { + if (av1_has_exact_match(last_hash_table, hash_value_1, hash_value_2)) { + M++; + } + } + } + } + + assert(T > 0); + double csm_rate = ((double)(C + S + M)) / ((double)(T)); + double m_rate = ((double)(M)) / ((double)(T)); + + cpi->csm_rate_array[cpi->rate_index] = csm_rate; + cpi->m_rate_array[cpi->rate_index] = m_rate; + + cpi->rate_index = (cpi->rate_index + 1) % max_history_size; + cpi->rate_size++; + cpi->rate_size = AOMMIN(cpi->rate_size, max_history_size); + + if (csm_rate < threshold_current) { + return 0; + } + + if (C == T) { + return 1; + } + + double csm_average = 0.0; + double m_average = 0.0; + + for (k = 0; k < cpi->rate_size; k++) { + csm_average += cpi->csm_rate_array[k]; + m_average += cpi->m_rate_array[k]; + } + csm_average /= cpi->rate_size; + m_average /= cpi->rate_size; + + if (csm_average < threshold_average) { + return 0; + } + + if (M > (T - C - S) / 3) { + return 1; + } + + if (csm_rate > 0.99 && m_rate > 0.01) { + return 1; + } + + if (csm_average + m_average > 1.01) { + return 1; + } + + return 0; +} + static int encode_frame_to_data_rate(AV1_COMP *cpi, size_t *size, uint8_t *dest, unsigned int *frame_flags) { AV1_COMMON *const cm = &cpi->common; @@ -5093,8 +5226,6 @@ const AV1EncoderConfig *const oxcf = &cpi->oxcf; struct segmentation *const seg = &cm->seg; - aom_clear_system_state(); - // frame type has been decided outside of this function call cm->cur_frame->frame_type = current_frame->frame_type; @@ -5188,6 +5319,26 @@ return AOM_CODEC_OK; } + // Work out whether to force_integer_mv this frame + 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. cm->lf.mode_ref_delta_update = 0; @@ -5412,6 +5563,11 @@ *frame_flags = cpi->frame_flags & ~FRAMEFLAGS_KEY; } + // 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; @@ -5873,139 +6029,6 @@ } #endif // CONFIG_INTERNAL_STATS -static int is_integer_mv(AV1_COMP *cpi, const YV12_BUFFER_CONFIG *cur_picture, - const YV12_BUFFER_CONFIG *last_picture, - hash_table *last_hash_table) { - aom_clear_system_state(); - // check use hash ME - int k; - uint32_t hash_value_1; - uint32_t hash_value_2; - - const int block_size = 8; - const double threshold_current = 0.8; - const double threshold_average = 0.95; - const int max_history_size = 32; - int T = 0; // total block - int C = 0; // match with collocated block - int S = 0; // smooth region but not match with collocated block - int M = 0; // match with other block - - const int pic_width = cur_picture->y_width; - const int pic_height = cur_picture->y_height; - for (int i = 0; i + block_size <= pic_height; i += block_size) { - for (int j = 0; j + block_size <= pic_width; j += block_size) { - const int x_pos = j; - const int y_pos = i; - int match = 1; - T++; - - // check whether collocated block match with current - uint8_t *p_cur = cur_picture->y_buffer; - uint8_t *p_ref = last_picture->y_buffer; - int stride_cur = cur_picture->y_stride; - int stride_ref = last_picture->y_stride; - p_cur += (y_pos * stride_cur + x_pos); - p_ref += (y_pos * stride_ref + x_pos); - - if (cur_picture->flags & YV12_FLAG_HIGHBITDEPTH) { - uint16_t *p16_cur = CONVERT_TO_SHORTPTR(p_cur); - uint16_t *p16_ref = CONVERT_TO_SHORTPTR(p_ref); - for (int tmpY = 0; tmpY < block_size && match; tmpY++) { - for (int tmpX = 0; tmpX < block_size && match; tmpX++) { - if (p16_cur[tmpX] != p16_ref[tmpX]) { - match = 0; - } - } - p16_cur += stride_cur; - p16_ref += stride_ref; - } - } else { - for (int tmpY = 0; tmpY < block_size && match; tmpY++) { - for (int tmpX = 0; tmpX < block_size && match; tmpX++) { - if (p_cur[tmpX] != p_ref[tmpX]) { - match = 0; - } - } - p_cur += stride_cur; - p_ref += stride_ref; - } - } - - if (match) { - C++; - continue; - } - - if (av1_hash_is_horizontal_perfect(cur_picture, block_size, x_pos, - y_pos) || - av1_hash_is_vertical_perfect(cur_picture, block_size, x_pos, y_pos)) { - S++; - continue; - } - - av1_get_block_hash_value( - cur_picture->y_buffer + y_pos * stride_cur + x_pos, stride_cur, - block_size, &hash_value_1, &hash_value_2, - (cur_picture->flags & YV12_FLAG_HIGHBITDEPTH), &cpi->td.mb); - // Hashing does not work for highbitdepth currently. - // TODO(Roger): Make it work for highbitdepth. - if (av1_use_hash_me(&cpi->common)) { - if (av1_has_exact_match(last_hash_table, hash_value_1, hash_value_2)) { - M++; - } - } - } - } - - assert(T > 0); - double csm_rate = ((double)(C + S + M)) / ((double)(T)); - double m_rate = ((double)(M)) / ((double)(T)); - - cpi->csm_rate_array[cpi->rate_index] = csm_rate; - cpi->m_rate_array[cpi->rate_index] = m_rate; - - cpi->rate_index = (cpi->rate_index + 1) % max_history_size; - cpi->rate_size++; - cpi->rate_size = AOMMIN(cpi->rate_size, max_history_size); - - if (csm_rate < threshold_current) { - return 0; - } - - if (C == T) { - return 1; - } - - double csm_average = 0.0; - double m_average = 0.0; - - for (k = 0; k < cpi->rate_size; k++) { - csm_average += cpi->csm_rate_array[k]; - m_average += cpi->m_rate_array[k]; - } - csm_average /= cpi->rate_size; - m_average /= cpi->rate_size; - - if (csm_average < threshold_average) { - return 0; - } - - if (M > (T - C - S) / 3) { - return 1; - } - - if (csm_rate > 0.99 && m_rate > 0.01) { - return 1; - } - - if (csm_average + m_average > 1.01) { - return 1; - } - - return 0; -} - // Code for temporal dependency model typedef struct GF_PICTURE { YV12_BUFFER_CONFIG *frame; @@ -6891,24 +6914,6 @@ cpi->common.current_frame_id = -1; } - if (oxcf->pass != 1 && cpi->common.allow_screen_content_tools && - !frame_is_intra_only(cm)) { - if (cpi->common.seq_params.force_integer_mv == 2) { - struct lookahead_entry *previous_entry = - av1_lookahead_peek(cpi->lookahead, cpi->previous_index); - if (!previous_entry) - cpi->common.cur_frame_force_integer_mv = 0; - else - cpi->common.cur_frame_force_integer_mv = is_integer_mv( - cpi, cpi->source, &previous_entry->img, cpi->previous_hash_table); - } else { - cpi->common.cur_frame_force_integer_mv = - cpi->common.seq_params.force_integer_mv; - } - } else { - cpi->common.cur_frame_force_integer_mv = 0; - } - if (cpi->twopass.gf_group.index == 1 && cpi->oxcf.enable_tpl_model) { set_frame_size(cpi, cm->width, cm->height); setup_tpl_stats(cpi); @@ -6925,23 +6930,6 @@ if (Pass0Encode(cpi, size, dest, frame_flags) != AOM_CODEC_OK) return AOM_CODEC_ERROR; } - if (oxcf->pass != 1 && cpi->common.allow_screen_content_tools) { - cpi->previous_hash_table = &cm->cur_frame->hash_table; - { - int l; - for (l = -MAX_PRE_FRAMES; l < cpi->lookahead->max_sz; l++) { - if ((cpi->lookahead->buf + l) == source) { - cpi->previous_index = l; - break; - } - } - - if (l == cpi->lookahead->max_sz) { - aom_internal_error(&cm->error, AOM_CODEC_MEM_ERROR, - "Failed to find last frame original buffer"); - } - } - } if (!cm->large_scale_tile) { cm->cur_frame->frame_context = *cm->fc;