|  | #include <stdbool.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  |  | 
|  | #include "av1/common/reconinter.h" | 
|  | #include "av1/common/reconintra.h" | 
|  | #include "av1/encoder/interintra_ml_data_collect.h" | 
|  | #include "av1/encoder/reconinter_enc.h" | 
|  |  | 
|  | #define BORDER_SIZE 4  // Only generate data with a border of 4 pixels. | 
|  |  | 
|  | // Static references to the currently captured Y/U/V planes. Held in | 
|  | // memory until we can determine if this block is a skip-block (if so, | 
|  | // the data is not written out). | 
|  | static IIMLPlaneInfo *INFO_Y = NULL; | 
|  | static IIMLPlaneInfo *INFO_U = NULL; | 
|  | static IIMLPlaneInfo *INFO_V = NULL; | 
|  |  | 
|  | // Static reference to the file where the data is being stored. | 
|  | static FILE *FP = NULL; | 
|  |  | 
|  | // Clean-up function called on program exit. Closes file descriptor, | 
|  | // flushing writes. | 
|  | static void cleanup_fp() { | 
|  | if (FP != NULL) { | 
|  | int r = fclose(FP); | 
|  | assert(r == 0); | 
|  | (void)r; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Function called on every invocation of data collection. Initializes | 
|  | // the file output and registers the cleanup function (if not already done). | 
|  | // The output file name is "interintra_ml_data_collect.bin" by default. | 
|  | // The file name can be changed by setting INTERINTRA_ML_DATA_COLLECT | 
|  | // environment variable. | 
|  | static void init_first_run() { | 
|  | if (FP != NULL) { | 
|  | return; | 
|  | } | 
|  | const char *filename = getenv("INTERINTRA_ML_DATA_COLLECT"); | 
|  | if (filename == NULL) { | 
|  | filename = "interintra_ml_data_collect.bin"; | 
|  | } | 
|  | FP = fopen(filename, "wb"); | 
|  | assert(FP != NULL); | 
|  | int r = atexit(cleanup_fp); | 
|  | assert(r == 0); | 
|  | (void)r; | 
|  | } | 
|  |  | 
|  | // Copy the value from the source into the destination, at the given offsets. | 
|  | // If both source and destination are high-bitdepth, will copy. Otherwise, | 
|  | // if source is high-bitdepth but destination is lower, will only copy the | 
|  | // first 8 bits. | 
|  | static void copy_value(uint8_t *dst, int dst_offset, const uint8_t *src, | 
|  | int src_offset, int bitdepth, bool is_hbd) { | 
|  | if (bitdepth > 8) { | 
|  | assert(is_hbd);  // If the IIMLPlaneInfo is high-bitdepth, then so must the | 
|  | // AV1 data structures. | 
|  | uint16_t *dst16 = (uint16_t *)dst; | 
|  | uint16_t *src16 = CONVERT_TO_SHORTPTR(src); | 
|  | dst16[dst_offset] = src16[src_offset]; | 
|  | } else if (bitdepth == 8 && !is_hbd) { | 
|  | // A pure copy. | 
|  | dst[dst_offset] = src[src_offset]; | 
|  | } else { | 
|  | // There is no case when bitdepth > 8 and !is_hbd. | 
|  | assert(bitdepth == 8 && is_hbd); | 
|  | uint16_t *src16 = CONVERT_TO_SHORTPTR(src); | 
|  | assert(src16[src_offset] <= 255); | 
|  | dst[dst_offset] = (uint8_t)src16[src_offset]; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void copy_source_image(IIMLPlaneInfo *info, MACROBLOCK *const x) { | 
|  | // Overallocate, memory is not an issue. | 
|  | info->source_image = malloc(info->width * info->height * sizeof(uint16_t)); | 
|  | assert(info->source_image != NULL); | 
|  |  | 
|  | const struct buf_2d *ref = &x->plane[info->plane].src; | 
|  | const int stride = ref->stride; | 
|  | const uint8_t *buf = ref->buf0 + info->y * stride + info->x; | 
|  | const bool is_hbd = is_cur_buf_hbd(&x->e_mbd); | 
|  |  | 
|  | for (int j = 0; j < info->height; ++j) { | 
|  | for (int i = 0; i < info->width; ++i) { | 
|  | copy_value(info->source_image, j * info->width + i, buf, j * stride + i, | 
|  | info->bitdepth, is_hbd); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void copy_intrapred_lshape(IIMLPlaneInfo *info, MACROBLOCK *const x) { | 
|  | info->intrapred_lshape = | 
|  | malloc(sizeof(uint16_t) * ((info->width + info->border) * info->border + | 
|  | info->border * info->height)); | 
|  | assert(info->intrapred_lshape != NULL); | 
|  | MACROBLOCKD *const xd = &x->e_mbd; | 
|  | const struct macroblockd_plane *const pd = &xd->plane[info->plane]; | 
|  | const struct buf_2d *ref = &pd->dst; | 
|  | const int stride = ref->stride; | 
|  | uint8_t *buf = ref->buf0 + info->y * stride + info->x; | 
|  | av1_interintra_ml_data_collect_copy_intrapred_lshape( | 
|  | info->intrapred_lshape, buf, stride, info->width, info->height, | 
|  | info->border, info->bitdepth, is_cur_buf_hbd(xd)); | 
|  | } | 
|  |  | 
|  | void av1_interintra_ml_data_collect_copy_intrapred_lshape( | 
|  | uint8_t *dst, const uint8_t *src, int src_stride, int width, int height, | 
|  | int border, int bitdepth, bool is_src_hbd) { | 
|  | // Point back at the start of the L-region. | 
|  | src -= (border * src_stride + border); | 
|  | int info_i = 0; | 
|  | // Copy over the top part. | 
|  | for (int j = 0; j < border; ++j) { | 
|  | for (int i = 0; i < border + width; ++i) { | 
|  | copy_value(dst, info_i++, src, j * src_stride + i, bitdepth, is_src_hbd); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Copy over the side pixels. | 
|  | for (int j = border; j < border + height; ++j) { | 
|  | for (int i = 0; i < border; ++i) { | 
|  | copy_value(dst, info_i++, src, j * src_stride + i, bitdepth, is_src_hbd); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void copy_interpred(IIMLPlaneInfo *info, const AV1_COMP *const cpi, | 
|  | MACROBLOCK *const x) { | 
|  | info->interpred = malloc(sizeof(uint16_t) * (info->width + info->border) * | 
|  | (info->height + info->border)); | 
|  |  | 
|  | const AV1_COMMON *const cm = &cpi->common; | 
|  | MACROBLOCKD *const xd = &x->e_mbd; | 
|  |  | 
|  | uint8_t *orig_buf = xd->plane[info->plane].dst.buf; | 
|  | int orig_stride = xd->plane[info->plane].dst.stride; | 
|  |  | 
|  | uint8_t *interpred; | 
|  | int interpred_stride; | 
|  | const int border = | 
|  | av1_calc_border(xd, AOM_PLANE_Y, false /* build for obmc */); | 
|  | assert(border >= BORDER_SIZE); | 
|  | av1_alloc_buf_with_border(&interpred, &interpred_stride, border, | 
|  | is_cur_buf_hbd(xd)); | 
|  |  | 
|  | xd->plane[info->plane].dst.buf = interpred; | 
|  | xd->plane[info->plane].dst.stride = interpred_stride; | 
|  |  | 
|  | av1_enc_build_border_only_inter_predictor(cm, xd, xd->mi_row, xd->mi_col, | 
|  | info->plane); | 
|  |  | 
|  | int info_i = 0; | 
|  | for (int j = -1 * info->border; j < info->height; ++j) { | 
|  | for (int i = -1 * info->border; i < info->width; ++i) { | 
|  | copy_value(info->interpred, info_i++, interpred, j * interpred_stride + i, | 
|  | info->bitdepth, is_cur_buf_hbd(xd)); | 
|  | } | 
|  | } | 
|  | xd->plane[info->plane].dst.buf = orig_buf; | 
|  | xd->plane[info->plane].dst.stride = orig_stride; | 
|  | av1_free_buf_with_border(interpred, interpred_stride, border, | 
|  | is_cur_buf_hbd(xd)); | 
|  | } | 
|  |  | 
|  | // Initializes the IIMLPlaneInfo structure. | 
|  | static IIMLPlaneInfo *init_plane_info(const AV1_COMP *const cpi, | 
|  | MACROBLOCK *const x, BLOCK_SIZE bsize, | 
|  | int plane) { | 
|  | MACROBLOCKD *const xd = &x->e_mbd; | 
|  | MB_MODE_INFO *mbmi = xd->mi[0]; | 
|  | (void)mbmi;  // Used for asserts. | 
|  | const AV1_COMMON *const cm = &cpi->common; | 
|  | const struct macroblockd_plane *const pd = &xd->plane[plane]; | 
|  | const BLOCK_SIZE plane_bsize = | 
|  | get_plane_block_size(bsize, pd->subsampling_x, pd->subsampling_y); | 
|  |  | 
|  | assert(is_inter_block(mbmi)); | 
|  | assert(!has_second_ref(mbmi)); | 
|  | assert(!is_intrabc_block(mbmi)); | 
|  | // Data is packed differently for smaller block sizes. Not supported. | 
|  | assert(block_size_wide[plane_bsize] >= 4); | 
|  | assert(block_size_high[plane_bsize] >= 4); | 
|  |  | 
|  | IIMLPlaneInfo *info = malloc(sizeof(IIMLPlaneInfo)); | 
|  | assert(info != NULL); | 
|  |  | 
|  | info->width = block_size_wide[plane_bsize]; | 
|  | info->height = block_size_high[plane_bsize]; | 
|  | info->plane = plane; | 
|  | info->bitdepth = (uint8_t)xd->bd; | 
|  | info->border = BORDER_SIZE; | 
|  | info->x = (xd->mi_col * MI_SIZE) >> pd->subsampling_x; | 
|  | info->y = (xd->mi_row * MI_SIZE) >> pd->subsampling_y; | 
|  | info->frame_order_hint = (int)cm->current_frame.order_hint; | 
|  |  | 
|  | RefCntBuffer *refbuf = get_ref_frame_buf(cm, xd->mi[0]->ref_frame[0]); | 
|  | assert(refbuf != NULL); | 
|  | info->ref_q = refbuf->base_qindex; | 
|  | info->base_q = cm->base_qindex; | 
|  | info->lambda = av1_compute_rd_mult_based_on_qindex(cpi, cm->base_qindex); | 
|  | assert(info->lambda > 0); | 
|  | assert(info->lambda < 10 * 1000 * 1000); | 
|  |  | 
|  | copy_source_image(info, x); | 
|  | copy_intrapred_lshape(info, x); | 
|  | copy_interpred(info, cpi, x); | 
|  | return info; | 
|  | } | 
|  |  | 
|  | void write_uint8_and_advance(uint8_t **buf, uint8_t b) { | 
|  | **buf = b; | 
|  | (*buf)++; | 
|  | } | 
|  |  | 
|  | void write_int32_and_advance(uint8_t **buf, int32_t value) { | 
|  | assert(value >= 0); | 
|  | for (size_t i = 0; i < sizeof(value); ++i) { | 
|  | // Little-endian order. | 
|  | uint8_t byte = 0xff & value; | 
|  | write_uint8_and_advance(buf, byte); | 
|  | value >>= 8; | 
|  | } | 
|  | } | 
|  |  | 
|  | void write_buffer_and_advance(uint8_t **buf, uint8_t *src, size_t size, | 
|  | int bitdepth) { | 
|  | if (bitdepth == 8) { | 
|  | for (size_t i = 0; i < size; ++i) { | 
|  | write_uint8_and_advance(buf, src[i]); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | uint16_t *src16 = (uint16_t *)src; | 
|  | for (size_t i = 0; i < size; ++i) { | 
|  | // Little-endian order. | 
|  | uint8_t b1 = (uint8_t)(0xff & src16[i]); | 
|  | uint8_t b2 = (uint8_t)(0xff & (src16[i] >> 8)); | 
|  | write_uint8_and_advance(buf, b1); | 
|  | write_uint8_and_advance(buf, b2); | 
|  | } | 
|  | } | 
|  |  | 
|  | void av1_interintra_ml_data_collect_serialize(IIMLPlaneInfo *info, | 
|  | uint8_t **buf, size_t *buf_size) { | 
|  | const int bytes_per_pixel = info->bitdepth == 8 ? 1 : 2; | 
|  | const size_t source_size = info->width * info->height; | 
|  | const size_t intrapred_lshape_size = | 
|  | ((info->border + info->width) * info->border + | 
|  | info->border * info->height); | 
|  | const size_t interpred_size = | 
|  | ((info->border + info->width) * (info->border + info->height)); | 
|  | *buf_size = 1 /* width */ + 1 /* height */ + 1 /* plane */ + | 
|  | 1 /* bitdepth */ + 1 /* border */ + sizeof(int32_t) /* x */ + | 
|  | sizeof(int32_t) /* y */ + sizeof(int32_t) /* frame order hint */ + | 
|  | sizeof(int32_t) /* lambda */ + 1 /* ref_q */ + 1 /* base_q */ + | 
|  | bytes_per_pixel * source_size + | 
|  | bytes_per_pixel * intrapred_lshape_size + | 
|  | bytes_per_pixel * interpred_size; | 
|  | *buf = malloc(*buf_size); | 
|  | assert(*buf != NULL); | 
|  | uint8_t *start = *buf; | 
|  |  | 
|  | write_uint8_and_advance(buf, info->width); | 
|  | write_uint8_and_advance(buf, info->height); | 
|  | write_uint8_and_advance(buf, info->plane); | 
|  | write_uint8_and_advance(buf, info->bitdepth); | 
|  | write_uint8_and_advance(buf, info->border); | 
|  | write_int32_and_advance(buf, info->x); | 
|  | write_int32_and_advance(buf, info->y); | 
|  | write_int32_and_advance(buf, info->frame_order_hint); | 
|  | write_int32_and_advance(buf, info->lambda); | 
|  | write_uint8_and_advance(buf, info->ref_q); | 
|  | write_uint8_and_advance(buf, info->base_q); | 
|  | write_buffer_and_advance(buf, info->source_image, source_size, | 
|  | info->bitdepth); | 
|  | write_buffer_and_advance(buf, info->intrapred_lshape, intrapred_lshape_size, | 
|  | info->bitdepth); | 
|  | write_buffer_and_advance(buf, info->interpred, interpred_size, | 
|  | info->bitdepth); | 
|  |  | 
|  | assert(start + *buf_size == *buf); | 
|  | *buf = start; | 
|  | } | 
|  |  | 
|  | void av1_interintra_ml_data_collect(const AV1_COMP *const cpi, | 
|  | MACROBLOCK *const x, BLOCK_SIZE bsize) { | 
|  | init_first_run(); | 
|  | assert(INFO_Y == NULL); | 
|  | assert(INFO_U == NULL); | 
|  | assert(INFO_V == NULL); | 
|  | INFO_Y = init_plane_info(cpi, x, bsize, AOM_PLANE_Y); | 
|  | assert(INFO_Y != NULL); | 
|  |  | 
|  | // Sometimes the chroma is derived from the luma. Check if chroma is | 
|  | // available. | 
|  | MACROBLOCKD *const xd = &x->e_mbd; | 
|  | if (xd->mi[0]->chroma_ref_info.is_chroma_ref) { | 
|  | INFO_U = init_plane_info(cpi, x, bsize, AOM_PLANE_U); | 
|  | INFO_V = init_plane_info(cpi, x, bsize, AOM_PLANE_V); | 
|  | assert(INFO_U != NULL); | 
|  | assert(INFO_V != NULL); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Write out the three planes to disk. | 
|  | void av1_interintra_ml_data_collect_finalize() { | 
|  | uint8_t *buf; | 
|  | size_t buf_size; | 
|  |  | 
|  | // If luma is not present, then neither is chroma. | 
|  | if (INFO_Y == NULL) { | 
|  | assert(INFO_U == NULL); | 
|  | assert(INFO_V == NULL); | 
|  | return; | 
|  | } | 
|  | av1_interintra_ml_data_collect_serialize(INFO_Y, &buf, &buf_size); | 
|  | size_t written = fwrite(buf, sizeof(*buf), buf_size, FP); | 
|  | assert(written == buf_size); | 
|  | (void)written; | 
|  | free(buf); | 
|  |  | 
|  | if (INFO_U != NULL) { | 
|  | av1_interintra_ml_data_collect_serialize(INFO_U, &buf, &buf_size); | 
|  | written = fwrite(buf, sizeof(*buf), buf_size, FP); | 
|  | assert(written == buf_size); | 
|  | free(buf); | 
|  | } | 
|  |  | 
|  | if (INFO_V != NULL) { | 
|  | av1_interintra_ml_data_collect_serialize(INFO_V, &buf, &buf_size); | 
|  | written = fwrite(buf, sizeof(*buf), buf_size, FP); | 
|  | assert(written == buf_size); | 
|  | free(buf); | 
|  | } | 
|  |  | 
|  | av1_interintra_ml_data_collect_abandon(); | 
|  | assert(INFO_Y == NULL); | 
|  | assert(INFO_U == NULL); | 
|  | assert(INFO_V == NULL); | 
|  | } | 
|  |  | 
|  | static void destroy_info(IIMLPlaneInfo *info) { | 
|  | if (info != NULL) { | 
|  | free(info->source_image); | 
|  | free(info->intrapred_lshape); | 
|  | free(info->interpred); | 
|  | free(info); | 
|  | } | 
|  | } | 
|  |  | 
|  | void av1_interintra_ml_data_collect_abandon() { | 
|  | if (INFO_Y == NULL) { | 
|  | assert(INFO_U == NULL); | 
|  | assert(INFO_V == NULL); | 
|  | return; | 
|  | } | 
|  | destroy_info(INFO_Y); | 
|  | destroy_info(INFO_U); | 
|  | destroy_info(INFO_V); | 
|  | INFO_Y = NULL; | 
|  | INFO_U = NULL; | 
|  | INFO_V = NULL; | 
|  | } | 
|  |  | 
|  | static int calc_intra_border(MACROBLOCK *const x, BLOCK_SIZE bsize) { | 
|  | MACROBLOCKD *const xd = &x->e_mbd; | 
|  | int border = BORDER_SIZE; | 
|  | // For each plane, check how much is available on the top and left, | 
|  | // shrinking the border if needed. | 
|  | for (int plane = AOM_PLANE_Y; plane <= AOM_PLANE_V; ++plane) { | 
|  | border = AOMMIN(border, av1_intra_top_available(xd, plane)); | 
|  | border = AOMMIN(border, av1_intra_left_available(xd, plane)); | 
|  |  | 
|  | // If either the bottom or the right side of the block is partially | 
|  | // unavailable, set the entire border to 0 -- we cannot extract the | 
|  | // full border. | 
|  | const struct macroblockd_plane *const pd = &xd->plane[plane]; | 
|  | const BLOCK_SIZE plane_bsize = | 
|  | get_plane_block_size(bsize, pd->subsampling_x, pd->subsampling_y); | 
|  | const TX_SIZE tx_size = max_txsize_rect_lookup[plane_bsize]; | 
|  | if (av1_intra_bottom_unavailable(xd, plane, tx_size) || | 
|  | av1_intra_right_unavailable(xd, plane, tx_size)) { | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | return border; | 
|  | } | 
|  |  | 
|  | bool av1_interintra_ml_data_collect_valid(MACROBLOCK *const x, | 
|  | BLOCK_SIZE bsize) { | 
|  | MACROBLOCKD *const xd = &x->e_mbd; | 
|  | const int inter_border = | 
|  | av1_calc_border(xd, AOM_PLANE_Y, false /* build for obmc */); | 
|  | const int intra_border = calc_intra_border(x, bsize); | 
|  | // Only process 8x8 or larger blocks -- if a dimension is 4, then the | 
|  | // chroma dimension can be 2, which has a different packing structure. | 
|  | return inter_border >= BORDER_SIZE && intra_border >= BORDER_SIZE && | 
|  | !x->skip && block_size_wide[bsize] >= 8 && block_size_high[bsize] >= 8; | 
|  | } |