| /* | 
 |  *  Copyright (c) 2019 The WebM project authors. All Rights Reserved. | 
 |  * | 
 |  *  Use of this source code is governed by a BSD-style license | 
 |  *  that can be found in the LICENSE file in the root of the source | 
 |  *  tree. An additional intellectual property rights grant can be found | 
 |  *  in the file PATENTS.  All contributing project authors may | 
 |  *  be found in the AUTHORS file in the root of the source tree. | 
 |  */ | 
 |  | 
 | #include <memory> | 
 | #include <vector> | 
 | #include "./ivfenc.h" | 
 | #include "vp9/common/vp9_entropymode.h" | 
 | #include "vp9/common/vp9_enums.h" | 
 | #include "vp9/common/vp9_onyxc_int.h" | 
 | #include "vp9/vp9_iface_common.h" | 
 | #include "vp9/encoder/vp9_encoder.h" | 
 | #include "vp9/encoder/vp9_firstpass.h" | 
 | #include "vp9/simple_encode.h" | 
 | #include "vp9/vp9_cx_iface.h" | 
 |  | 
 | namespace vp9 { | 
 |  | 
 | static int get_plane_height(vpx_img_fmt_t img_fmt, int frame_height, | 
 |                             int plane) { | 
 |   assert(plane < 3); | 
 |   if (plane == 0) { | 
 |     return frame_height; | 
 |   } | 
 |   switch (img_fmt) { | 
 |     case VPX_IMG_FMT_I420: | 
 |     case VPX_IMG_FMT_I440: | 
 |     case VPX_IMG_FMT_YV12: | 
 |     case VPX_IMG_FMT_I42016: | 
 |     case VPX_IMG_FMT_I44016: return (frame_height + 1) >> 1; | 
 |     default: return frame_height; | 
 |   } | 
 | } | 
 |  | 
 | static int get_plane_width(vpx_img_fmt_t img_fmt, int frame_width, int plane) { | 
 |   assert(plane < 3); | 
 |   if (plane == 0) { | 
 |     return frame_width; | 
 |   } | 
 |   switch (img_fmt) { | 
 |     case VPX_IMG_FMT_I420: | 
 |     case VPX_IMG_FMT_YV12: | 
 |     case VPX_IMG_FMT_I422: | 
 |     case VPX_IMG_FMT_I42016: | 
 |     case VPX_IMG_FMT_I42216: return (frame_width + 1) >> 1; | 
 |     default: return frame_width; | 
 |   } | 
 | } | 
 |  | 
 | // TODO(angiebird): Merge this function with vpx_img_plane_width() | 
 | static int img_plane_width(const vpx_image_t *img, int plane) { | 
 |   if (plane > 0 && img->x_chroma_shift > 0) | 
 |     return (img->d_w + 1) >> img->x_chroma_shift; | 
 |   else | 
 |     return img->d_w; | 
 | } | 
 |  | 
 | // TODO(angiebird): Merge this function with vpx_img_plane_height() | 
 | static int img_plane_height(const vpx_image_t *img, int plane) { | 
 |   if (plane > 0 && img->y_chroma_shift > 0) | 
 |     return (img->d_h + 1) >> img->y_chroma_shift; | 
 |   else | 
 |     return img->d_h; | 
 | } | 
 |  | 
 | // TODO(angiebird): Merge this function with vpx_img_read() | 
 | static int img_read(vpx_image_t *img, FILE *file) { | 
 |   int plane; | 
 |  | 
 |   for (plane = 0; plane < 3; ++plane) { | 
 |     unsigned char *buf = img->planes[plane]; | 
 |     const int stride = img->stride[plane]; | 
 |     const int w = img_plane_width(img, plane) * | 
 |                   ((img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1); | 
 |     const int h = img_plane_height(img, plane); | 
 |     int y; | 
 |  | 
 |     for (y = 0; y < h; ++y) { | 
 |       if (fread(buf, 1, w, file) != (size_t)w) return 0; | 
 |       buf += stride; | 
 |     } | 
 |   } | 
 |  | 
 |   return 1; | 
 | } | 
 |  | 
 | class SimpleEncode::EncodeImpl { | 
 |  public: | 
 |   VP9_COMP *cpi; | 
 |   vpx_img_fmt_t img_fmt; | 
 |   vpx_image_t tmp_img; | 
 |   std::vector<FIRSTPASS_STATS> first_pass_stats; | 
 | }; | 
 |  | 
 | static VP9_COMP *init_encoder(const VP9EncoderConfig *oxcf, | 
 |                               vpx_img_fmt_t img_fmt) { | 
 |   VP9_COMP *cpi; | 
 |   BufferPool *buffer_pool = (BufferPool *)vpx_calloc(1, sizeof(*buffer_pool)); | 
 |   vp9_initialize_enc(); | 
 |   cpi = vp9_create_compressor(oxcf, buffer_pool); | 
 |   vp9_update_compressor_with_img_fmt(cpi, img_fmt); | 
 |   return cpi; | 
 | } | 
 |  | 
 | static void free_encoder(VP9_COMP *cpi) { | 
 |   BufferPool *buffer_pool = cpi->common.buffer_pool; | 
 |   vp9_remove_compressor(cpi); | 
 |   // buffer_pool needs to be free after cpi because buffer_pool contains | 
 |   // allocated buffers that will be free in vp9_remove_compressor() | 
 |   vpx_free(buffer_pool); | 
 | } | 
 |  | 
 | static INLINE vpx_rational_t make_vpx_rational(int num, int den) { | 
 |   vpx_rational_t v; | 
 |   v.num = num; | 
 |   v.den = den; | 
 |   return v; | 
 | } | 
 |  | 
 | static INLINE FrameType | 
 | get_frame_type_from_update_type(FRAME_UPDATE_TYPE update_type) { | 
 |   switch (update_type) { | 
 |     case KF_UPDATE: return kFrameTypeKey; | 
 |     case ARF_UPDATE: return kFrameTypeAltRef; | 
 |     case GF_UPDATE: return kFrameTypeGolden; | 
 |     case OVERLAY_UPDATE: return kFrameTypeOverlay; | 
 |     case LF_UPDATE: return kFrameTypeInter; | 
 |     default: | 
 |       fprintf(stderr, "Unsupported update_type %d\n", update_type); | 
 |       abort(); | 
 |       return kFrameTypeInter; | 
 |   } | 
 | } | 
 |  | 
 | static void update_partition_info(const PARTITION_INFO *input_partition_info, | 
 |                                   const int num_rows_4x4, | 
 |                                   const int num_cols_4x4, | 
 |                                   PartitionInfo *output_partition_info) { | 
 |   const int num_units_4x4 = num_rows_4x4 * num_cols_4x4; | 
 |   for (int i = 0; i < num_units_4x4; ++i) { | 
 |     output_partition_info[i].row = input_partition_info[i].row; | 
 |     output_partition_info[i].column = input_partition_info[i].column; | 
 |     output_partition_info[i].row_start = input_partition_info[i].row_start; | 
 |     output_partition_info[i].column_start = | 
 |         input_partition_info[i].column_start; | 
 |     output_partition_info[i].width = input_partition_info[i].width; | 
 |     output_partition_info[i].height = input_partition_info[i].height; | 
 |   } | 
 | } | 
 |  | 
 | // translate MV_REFERENCE_FRAME to RefFrameType | 
 | static RefFrameType mv_ref_frame_to_ref_frame_type( | 
 |     MV_REFERENCE_FRAME mv_ref_frame) { | 
 |   switch (mv_ref_frame) { | 
 |     case LAST_FRAME: return kRefFrameTypeLast; | 
 |     case GOLDEN_FRAME: return kRefFrameTypePast; | 
 |     case ALTREF_FRAME: return kRefFrameTypeFuture; | 
 |     default: return kRefFrameTypeNone; | 
 |   } | 
 | } | 
 |  | 
 | static void update_motion_vector_info( | 
 |     const MOTION_VECTOR_INFO *input_motion_vector_info, const int num_rows_4x4, | 
 |     const int num_cols_4x4, MotionVectorInfo *output_motion_vector_info) { | 
 |   const int num_units_4x4 = num_rows_4x4 * num_cols_4x4; | 
 |   for (int i = 0; i < num_units_4x4; ++i) { | 
 |     const MV_REFERENCE_FRAME *in_ref_frame = | 
 |         input_motion_vector_info[i].ref_frame; | 
 |     output_motion_vector_info[i].mv_count = | 
 |         (in_ref_frame[0] == INTRA_FRAME) ? 0 | 
 |                                          : ((in_ref_frame[1] == NONE) ? 1 : 2); | 
 |     if (in_ref_frame[0] == NONE) { | 
 |       fprintf(stderr, "in_ref_frame[0] shouldn't be NONE\n"); | 
 |       abort(); | 
 |     } | 
 |     output_motion_vector_info[i].ref_frame[0] = | 
 |         mv_ref_frame_to_ref_frame_type(in_ref_frame[0]); | 
 |     output_motion_vector_info[i].ref_frame[1] = | 
 |         mv_ref_frame_to_ref_frame_type(in_ref_frame[1]); | 
 |     output_motion_vector_info[i].mv_row[0] = | 
 |         (double)input_motion_vector_info[i].mv[0].as_mv.row / | 
 |         kMotionVectorPrecision; | 
 |     output_motion_vector_info[i].mv_column[0] = | 
 |         (double)input_motion_vector_info[i].mv[0].as_mv.col / | 
 |         kMotionVectorPrecision; | 
 |     output_motion_vector_info[i].mv_row[1] = | 
 |         (double)input_motion_vector_info[i].mv[1].as_mv.row / | 
 |         kMotionVectorPrecision; | 
 |     output_motion_vector_info[i].mv_column[1] = | 
 |         (double)input_motion_vector_info[i].mv[1].as_mv.col / | 
 |         kMotionVectorPrecision; | 
 |   } | 
 | } | 
 |  | 
 | static void update_frame_counts(const FRAME_COUNTS *input_counts, | 
 |                                 FrameCounts *output_counts) { | 
 |   // Init array sizes. | 
 |   output_counts->y_mode.resize(BLOCK_SIZE_GROUPS); | 
 |   for (int i = 0; i < BLOCK_SIZE_GROUPS; ++i) { | 
 |     output_counts->y_mode[i].resize(INTRA_MODES); | 
 |   } | 
 |  | 
 |   output_counts->uv_mode.resize(INTRA_MODES); | 
 |   for (int i = 0; i < INTRA_MODES; ++i) { | 
 |     output_counts->uv_mode[i].resize(INTRA_MODES); | 
 |   } | 
 |  | 
 |   output_counts->partition.resize(PARTITION_CONTEXTS); | 
 |   for (int i = 0; i < PARTITION_CONTEXTS; ++i) { | 
 |     output_counts->partition[i].resize(PARTITION_TYPES); | 
 |   } | 
 |  | 
 |   output_counts->coef.resize(TX_SIZES); | 
 |   output_counts->eob_branch.resize(TX_SIZES); | 
 |   for (int i = 0; i < TX_SIZES; ++i) { | 
 |     output_counts->coef[i].resize(PLANE_TYPES); | 
 |     output_counts->eob_branch[i].resize(PLANE_TYPES); | 
 |     for (int j = 0; j < PLANE_TYPES; ++j) { | 
 |       output_counts->coef[i][j].resize(REF_TYPES); | 
 |       output_counts->eob_branch[i][j].resize(REF_TYPES); | 
 |       for (int k = 0; k < REF_TYPES; ++k) { | 
 |         output_counts->coef[i][j][k].resize(COEF_BANDS); | 
 |         output_counts->eob_branch[i][j][k].resize(COEF_BANDS); | 
 |         for (int l = 0; l < COEF_BANDS; ++l) { | 
 |           output_counts->coef[i][j][k][l].resize(COEFF_CONTEXTS); | 
 |           output_counts->eob_branch[i][j][k][l].resize(COEFF_CONTEXTS); | 
 |           for (int m = 0; m < COEFF_CONTEXTS; ++m) { | 
 |             output_counts->coef[i][j][k][l][m].resize(UNCONSTRAINED_NODES + 1); | 
 |           } | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   output_counts->switchable_interp.resize(SWITCHABLE_FILTER_CONTEXTS); | 
 |   for (int i = 0; i < SWITCHABLE_FILTER_CONTEXTS; ++i) { | 
 |     output_counts->switchable_interp[i].resize(SWITCHABLE_FILTERS); | 
 |   } | 
 |  | 
 |   output_counts->inter_mode.resize(INTER_MODE_CONTEXTS); | 
 |   for (int i = 0; i < INTER_MODE_CONTEXTS; ++i) { | 
 |     output_counts->inter_mode[i].resize(INTER_MODES); | 
 |   } | 
 |  | 
 |   output_counts->intra_inter.resize(INTRA_INTER_CONTEXTS); | 
 |   for (int i = 0; i < INTRA_INTER_CONTEXTS; ++i) { | 
 |     output_counts->intra_inter[i].resize(2); | 
 |   } | 
 |  | 
 |   output_counts->comp_inter.resize(COMP_INTER_CONTEXTS); | 
 |   for (int i = 0; i < COMP_INTER_CONTEXTS; ++i) { | 
 |     output_counts->comp_inter[i].resize(2); | 
 |   } | 
 |  | 
 |   output_counts->single_ref.resize(REF_CONTEXTS); | 
 |   for (int i = 0; i < REF_CONTEXTS; ++i) { | 
 |     output_counts->single_ref[i].resize(2); | 
 |     for (int j = 0; j < 2; ++j) { | 
 |       output_counts->single_ref[i][j].resize(2); | 
 |     } | 
 |   } | 
 |  | 
 |   output_counts->comp_ref.resize(REF_CONTEXTS); | 
 |   for (int i = 0; i < REF_CONTEXTS; ++i) { | 
 |     output_counts->comp_ref[i].resize(2); | 
 |   } | 
 |  | 
 |   output_counts->skip.resize(SKIP_CONTEXTS); | 
 |   for (int i = 0; i < SKIP_CONTEXTS; ++i) { | 
 |     output_counts->skip[i].resize(2); | 
 |   } | 
 |  | 
 |   output_counts->tx.p32x32.resize(TX_SIZE_CONTEXTS); | 
 |   output_counts->tx.p16x16.resize(TX_SIZE_CONTEXTS); | 
 |   output_counts->tx.p8x8.resize(TX_SIZE_CONTEXTS); | 
 |   for (int i = 0; i < TX_SIZE_CONTEXTS; i++) { | 
 |     output_counts->tx.p32x32[i].resize(TX_SIZES); | 
 |     output_counts->tx.p16x16[i].resize(TX_SIZES - 1); | 
 |     output_counts->tx.p8x8[i].resize(TX_SIZES - 2); | 
 |   } | 
 |   output_counts->tx.tx_totals.resize(TX_SIZES); | 
 |  | 
 |   output_counts->mv.joints.resize(MV_JOINTS); | 
 |   output_counts->mv.comps.resize(2); | 
 |   for (int i = 0; i < 2; ++i) { | 
 |     output_counts->mv.comps[i].sign.resize(2); | 
 |     output_counts->mv.comps[i].classes.resize(MV_CLASSES); | 
 |     output_counts->mv.comps[i].class0.resize(CLASS0_SIZE); | 
 |     output_counts->mv.comps[i].bits.resize(MV_OFFSET_BITS); | 
 |     for (int j = 0; j < MV_OFFSET_BITS; ++j) { | 
 |       output_counts->mv.comps[i].bits[j].resize(2); | 
 |     } | 
 |     output_counts->mv.comps[i].class0_fp.resize(CLASS0_SIZE); | 
 |     for (int j = 0; j < CLASS0_SIZE; ++j) { | 
 |       output_counts->mv.comps[i].class0_fp[j].resize(MV_FP_SIZE); | 
 |     } | 
 |     output_counts->mv.comps[i].fp.resize(MV_FP_SIZE); | 
 |     output_counts->mv.comps[i].class0_hp.resize(2); | 
 |     output_counts->mv.comps[i].hp.resize(2); | 
 |   } | 
 |  | 
 |   // Populate counts. | 
 |   for (int i = 0; i < BLOCK_SIZE_GROUPS; ++i) { | 
 |     for (int j = 0; j < INTRA_MODES; ++j) { | 
 |       output_counts->y_mode[i][j] = input_counts->y_mode[i][j]; | 
 |     } | 
 |   } | 
 |   for (int i = 0; i < INTRA_MODES; ++i) { | 
 |     for (int j = 0; j < INTRA_MODES; ++j) { | 
 |       output_counts->uv_mode[i][j] = input_counts->uv_mode[i][j]; | 
 |     } | 
 |   } | 
 |   for (int i = 0; i < PARTITION_CONTEXTS; ++i) { | 
 |     for (int j = 0; j < PARTITION_TYPES; ++j) { | 
 |       output_counts->partition[i][j] = input_counts->partition[i][j]; | 
 |     } | 
 |   } | 
 |   for (int i = 0; i < TX_SIZES; ++i) { | 
 |     for (int j = 0; j < PLANE_TYPES; ++j) { | 
 |       for (int k = 0; k < REF_TYPES; ++k) { | 
 |         for (int l = 0; l < COEF_BANDS; ++l) { | 
 |           for (int m = 0; m < COEFF_CONTEXTS; ++m) { | 
 |             output_counts->eob_branch[i][j][k][l][m] = | 
 |                 input_counts->eob_branch[i][j][k][l][m]; | 
 |             for (int n = 0; n < UNCONSTRAINED_NODES + 1; n++) { | 
 |               output_counts->coef[i][j][k][l][m][n] = | 
 |                   input_counts->coef[i][j][k][l][m][n]; | 
 |             } | 
 |           } | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |   for (int i = 0; i < SWITCHABLE_FILTER_CONTEXTS; ++i) { | 
 |     for (int j = 0; j < SWITCHABLE_FILTERS; ++j) { | 
 |       output_counts->switchable_interp[i][j] = | 
 |           input_counts->switchable_interp[i][j]; | 
 |     } | 
 |   } | 
 |   for (int i = 0; i < INTER_MODE_CONTEXTS; ++i) { | 
 |     for (int j = 0; j < INTER_MODES; ++j) { | 
 |       output_counts->inter_mode[i][j] = input_counts->inter_mode[i][j]; | 
 |     } | 
 |   } | 
 |   for (int i = 0; i < INTRA_INTER_CONTEXTS; ++i) { | 
 |     for (int j = 0; j < 2; ++j) { | 
 |       output_counts->intra_inter[i][j] = input_counts->intra_inter[i][j]; | 
 |     } | 
 |   } | 
 |   for (int i = 0; i < COMP_INTER_CONTEXTS; ++i) { | 
 |     for (int j = 0; j < 2; ++j) { | 
 |       output_counts->comp_inter[i][j] = input_counts->comp_inter[i][j]; | 
 |     } | 
 |   } | 
 |   for (int i = 0; i < REF_CONTEXTS; ++i) { | 
 |     for (int j = 0; j < 2; ++j) { | 
 |       for (int k = 0; k < 2; ++k) { | 
 |         output_counts->single_ref[i][j][k] = input_counts->single_ref[i][j][k]; | 
 |       } | 
 |     } | 
 |   } | 
 |   for (int i = 0; i < REF_CONTEXTS; ++i) { | 
 |     for (int j = 0; j < 2; ++j) { | 
 |       output_counts->comp_ref[i][j] = input_counts->comp_ref[i][j]; | 
 |     } | 
 |   } | 
 |   for (int i = 0; i < SKIP_CONTEXTS; ++i) { | 
 |     for (int j = 0; j < 2; ++j) { | 
 |       output_counts->skip[i][j] = input_counts->skip[i][j]; | 
 |     } | 
 |   } | 
 |   for (int i = 0; i < TX_SIZE_CONTEXTS; i++) { | 
 |     for (int j = 0; j < TX_SIZES; j++) { | 
 |       output_counts->tx.p32x32[i][j] = input_counts->tx.p32x32[i][j]; | 
 |     } | 
 |     for (int j = 0; j < TX_SIZES - 1; j++) { | 
 |       output_counts->tx.p16x16[i][j] = input_counts->tx.p16x16[i][j]; | 
 |     } | 
 |     for (int j = 0; j < TX_SIZES - 2; j++) { | 
 |       output_counts->tx.p8x8[i][j] = input_counts->tx.p8x8[i][j]; | 
 |     } | 
 |   } | 
 |   for (int i = 0; i < TX_SIZES; i++) { | 
 |     output_counts->tx.tx_totals[i] = input_counts->tx.tx_totals[i]; | 
 |   } | 
 |   for (int i = 0; i < MV_JOINTS; i++) { | 
 |     output_counts->mv.joints[i] = input_counts->mv.joints[i]; | 
 |   } | 
 |   for (int k = 0; k < 2; k++) { | 
 |     const nmv_component_counts *const comps_t = &input_counts->mv.comps[k]; | 
 |     for (int i = 0; i < 2; i++) { | 
 |       output_counts->mv.comps[k].sign[i] = comps_t->sign[i]; | 
 |       output_counts->mv.comps[k].class0_hp[i] = comps_t->class0_hp[i]; | 
 |       output_counts->mv.comps[k].hp[i] = comps_t->hp[i]; | 
 |     } | 
 |     for (int i = 0; i < MV_CLASSES; i++) { | 
 |       output_counts->mv.comps[k].classes[i] = comps_t->classes[i]; | 
 |     } | 
 |     for (int i = 0; i < CLASS0_SIZE; i++) { | 
 |       output_counts->mv.comps[k].class0[i] = comps_t->class0[i]; | 
 |       for (int j = 0; j < MV_FP_SIZE; j++) { | 
 |         output_counts->mv.comps[k].class0_fp[i][j] = comps_t->class0_fp[i][j]; | 
 |       } | 
 |     } | 
 |     for (int i = 0; i < MV_OFFSET_BITS; i++) { | 
 |       for (int j = 0; j < 2; j++) { | 
 |         output_counts->mv.comps[k].bits[i][j] = comps_t->bits[i][j]; | 
 |       } | 
 |     } | 
 |     for (int i = 0; i < MV_FP_SIZE; i++) { | 
 |       output_counts->mv.comps[k].fp[i] = comps_t->fp[i]; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void output_image_buffer(const ImageBuffer &image_buffer, std::FILE *out_file) { | 
 |   for (int plane = 0; plane < 3; ++plane) { | 
 |     const int w = image_buffer.plane_width[plane]; | 
 |     const int h = image_buffer.plane_height[plane]; | 
 |     const uint8_t *buf = image_buffer.plane_buffer[plane].get(); | 
 |     fprintf(out_file, "%d %d\n", h, w); | 
 |     for (int i = 0; i < w * h; ++i) { | 
 |       fprintf(out_file, "%d ", (int)buf[i]); | 
 |     } | 
 |     fprintf(out_file, "\n"); | 
 |   } | 
 | } | 
 |  | 
 | static bool init_image_buffer(ImageBuffer *image_buffer, int frame_width, | 
 |                               int frame_height, vpx_img_fmt_t img_fmt) { | 
 |   for (int plane = 0; plane < 3; ++plane) { | 
 |     const int w = get_plane_width(img_fmt, frame_width, plane); | 
 |     const int h = get_plane_height(img_fmt, frame_height, plane); | 
 |     image_buffer->plane_width[plane] = w; | 
 |     image_buffer->plane_height[plane] = h; | 
 |     image_buffer->plane_buffer[plane].reset(new (std::nothrow) uint8_t[w * h]); | 
 |     if (image_buffer->plane_buffer[plane].get() == nullptr) { | 
 |       return false; | 
 |     } | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | static void ImageBuffer_to_IMAGE_BUFFER(const ImageBuffer &image_buffer, | 
 |                                         IMAGE_BUFFER *image_buffer_c) { | 
 |   image_buffer_c->allocated = 1; | 
 |   for (int plane = 0; plane < 3; ++plane) { | 
 |     image_buffer_c->plane_width[plane] = image_buffer.plane_width[plane]; | 
 |     image_buffer_c->plane_height[plane] = image_buffer.plane_height[plane]; | 
 |     image_buffer_c->plane_buffer[plane] = | 
 |         image_buffer.plane_buffer[plane].get(); | 
 |   } | 
 | } | 
 |  | 
 | static size_t get_max_coding_data_byte_size(int frame_width, int frame_height) { | 
 |   return frame_width * frame_height * 3; | 
 | } | 
 |  | 
 | static bool init_encode_frame_result(EncodeFrameResult *encode_frame_result, | 
 |                                      int frame_width, int frame_height, | 
 |                                      vpx_img_fmt_t img_fmt) { | 
 |   const size_t max_coding_data_byte_size = | 
 |       get_max_coding_data_byte_size(frame_width, frame_height); | 
 |  | 
 |   encode_frame_result->coding_data.reset( | 
 |       new (std::nothrow) uint8_t[max_coding_data_byte_size]); | 
 |  | 
 |   encode_frame_result->num_rows_4x4 = get_num_unit_4x4(frame_height); | 
 |   encode_frame_result->num_cols_4x4 = get_num_unit_4x4(frame_width); | 
 |   encode_frame_result->partition_info.resize(encode_frame_result->num_rows_4x4 * | 
 |                                              encode_frame_result->num_cols_4x4); | 
 |   encode_frame_result->motion_vector_info.resize( | 
 |       encode_frame_result->num_rows_4x4 * encode_frame_result->num_cols_4x4); | 
 |  | 
 |   if (encode_frame_result->coding_data.get() == nullptr) { | 
 |     return false; | 
 |   } | 
 |   return init_image_buffer(&encode_frame_result->coded_frame, frame_width, | 
 |                            frame_height, img_fmt); | 
 | } | 
 |  | 
 | static void update_encode_frame_result( | 
 |     EncodeFrameResult *encode_frame_result, | 
 |     const ENCODE_FRAME_RESULT *encode_frame_info) { | 
 |   encode_frame_result->coding_data_bit_size = | 
 |       encode_frame_result->coding_data_byte_size * 8; | 
 |   encode_frame_result->show_idx = encode_frame_info->show_idx; | 
 |   encode_frame_result->coding_idx = encode_frame_info->frame_coding_index; | 
 |   assert(kRefFrameTypeMax == MAX_INTER_REF_FRAMES); | 
 |   for (int i = 0; i < kRefFrameTypeMax; ++i) { | 
 |     encode_frame_result->ref_frame_info.coding_indexes[i] = | 
 |         encode_frame_info->ref_frame_coding_indexes[i]; | 
 |     encode_frame_result->ref_frame_info.valid_list[i] = | 
 |         encode_frame_info->ref_frame_valid_list[i]; | 
 |   } | 
 |   encode_frame_result->frame_type = | 
 |       get_frame_type_from_update_type(encode_frame_info->update_type); | 
 |   encode_frame_result->psnr = encode_frame_info->psnr; | 
 |   encode_frame_result->sse = encode_frame_info->sse; | 
 |   encode_frame_result->quantize_index = encode_frame_info->quantize_index; | 
 |   update_partition_info(encode_frame_info->partition_info, | 
 |                         encode_frame_result->num_rows_4x4, | 
 |                         encode_frame_result->num_cols_4x4, | 
 |                         &encode_frame_result->partition_info[0]); | 
 |   update_motion_vector_info(encode_frame_info->motion_vector_info, | 
 |                             encode_frame_result->num_rows_4x4, | 
 |                             encode_frame_result->num_cols_4x4, | 
 |                             &encode_frame_result->motion_vector_info[0]); | 
 |   update_frame_counts(&encode_frame_info->frame_counts, | 
 |                       &encode_frame_result->frame_counts); | 
 | } | 
 |  | 
 | static void IncreaseGroupOfPictureIndex(GroupOfPicture *group_of_picture) { | 
 |   ++group_of_picture->next_encode_frame_index; | 
 | } | 
 |  | 
 | static int IsGroupOfPictureFinished(const GroupOfPicture &group_of_picture) { | 
 |   return static_cast<size_t>(group_of_picture.next_encode_frame_index) == | 
 |          group_of_picture.encode_frame_list.size(); | 
 | } | 
 |  | 
 | bool operator==(const RefFrameInfo &a, const RefFrameInfo &b) { | 
 |   bool match = true; | 
 |   for (int i = 0; i < kRefFrameTypeMax; ++i) { | 
 |     match &= a.coding_indexes[i] == b.coding_indexes[i]; | 
 |     match &= a.valid_list[i] == b.valid_list[i]; | 
 |   } | 
 |   return match; | 
 | } | 
 |  | 
 | static void InitRefFrameInfo(RefFrameInfo *ref_frame_info) { | 
 |   for (int i = 0; i < kRefFrameTypeMax; ++i) { | 
 |     ref_frame_info->coding_indexes[i] = -1; | 
 |     ref_frame_info->valid_list[i] = 0; | 
 |   } | 
 | } | 
 |  | 
 | // After finishing coding a frame, this function will update the coded frame | 
 | // into the ref_frame_info based on the frame_type and the coding_index. | 
 | static void PostUpdateRefFrameInfo(FrameType frame_type, int frame_coding_index, | 
 |                                    RefFrameInfo *ref_frame_info) { | 
 |   // This part is written based on the logics in vp9_configure_buffer_updates() | 
 |   // and update_ref_frames() | 
 |   int *ref_frame_coding_indexes = ref_frame_info->coding_indexes; | 
 |   switch (frame_type) { | 
 |     case kFrameTypeKey: | 
 |       ref_frame_coding_indexes[kRefFrameTypeLast] = frame_coding_index; | 
 |       ref_frame_coding_indexes[kRefFrameTypePast] = frame_coding_index; | 
 |       ref_frame_coding_indexes[kRefFrameTypeFuture] = frame_coding_index; | 
 |       break; | 
 |     case kFrameTypeInter: | 
 |       ref_frame_coding_indexes[kRefFrameTypeLast] = frame_coding_index; | 
 |       break; | 
 |     case kFrameTypeAltRef: | 
 |       ref_frame_coding_indexes[kRefFrameTypeFuture] = frame_coding_index; | 
 |       break; | 
 |     case kFrameTypeOverlay: | 
 |       // Reserve the past coding_index in the future slot. This logic is from | 
 |       // update_ref_frames() with condition vp9_preserve_existing_gf() == 1 | 
 |       // TODO(angiebird): Invetegate why we need this. | 
 |       ref_frame_coding_indexes[kRefFrameTypeFuture] = | 
 |           ref_frame_coding_indexes[kRefFrameTypePast]; | 
 |       ref_frame_coding_indexes[kRefFrameTypePast] = frame_coding_index; | 
 |       break; | 
 |     case kFrameTypeGolden: | 
 |       ref_frame_coding_indexes[kRefFrameTypePast] = frame_coding_index; | 
 |       ref_frame_coding_indexes[kRefFrameTypeLast] = frame_coding_index; | 
 |       break; | 
 |   } | 
 |  | 
 |   //  This part is written based on the logics in get_ref_frame_flags() but we | 
 |   //  rename the flags alt, golden to future, past respectively. Mark | 
 |   //  non-duplicated reference frames as valid. The priorities are | 
 |   //  kRefFrameTypeLast > kRefFrameTypePast > kRefFrameTypeFuture. | 
 |   const int last_index = ref_frame_coding_indexes[kRefFrameTypeLast]; | 
 |   const int past_index = ref_frame_coding_indexes[kRefFrameTypePast]; | 
 |   const int future_index = ref_frame_coding_indexes[kRefFrameTypeFuture]; | 
 |  | 
 |   int *ref_frame_valid_list = ref_frame_info->valid_list; | 
 |   for (int ref_frame_idx = 0; ref_frame_idx < kRefFrameTypeMax; | 
 |        ++ref_frame_idx) { | 
 |     ref_frame_valid_list[ref_frame_idx] = 1; | 
 |   } | 
 |  | 
 |   if (past_index == last_index) { | 
 |     ref_frame_valid_list[kRefFrameTypePast] = 0; | 
 |   } | 
 |  | 
 |   if (future_index == last_index) { | 
 |     ref_frame_valid_list[kRefFrameTypeFuture] = 0; | 
 |   } | 
 |  | 
 |   if (future_index == past_index) { | 
 |     ref_frame_valid_list[kRefFrameTypeFuture] = 0; | 
 |   } | 
 | } | 
 |  | 
 | static void SetGroupOfPicture(int first_is_key_frame, int use_alt_ref, | 
 |                               int coding_frame_count, int first_show_idx, | 
 |                               int last_gop_use_alt_ref, int start_coding_index, | 
 |                               const RefFrameInfo &start_ref_frame_info, | 
 |                               GroupOfPicture *group_of_picture) { | 
 |   // Clean up the state of previous group of picture. | 
 |   group_of_picture->encode_frame_list.clear(); | 
 |   group_of_picture->next_encode_frame_index = 0; | 
 |   group_of_picture->show_frame_count = coding_frame_count - use_alt_ref; | 
 |   group_of_picture->start_show_index = first_show_idx; | 
 |   group_of_picture->start_coding_index = start_coding_index; | 
 |   group_of_picture->first_is_key_frame = first_is_key_frame; | 
 |   group_of_picture->use_alt_ref = use_alt_ref; | 
 |   group_of_picture->last_gop_use_alt_ref = last_gop_use_alt_ref; | 
 |  | 
 |   // We need to make a copy of start reference frame info because we | 
 |   // use it to simulate the ref frame update. | 
 |   RefFrameInfo ref_frame_info = start_ref_frame_info; | 
 |  | 
 |   { | 
 |     // First frame in the group of pictures. It's either key frame or show inter | 
 |     // frame. | 
 |     EncodeFrameInfo encode_frame_info; | 
 |     // Set frame_type | 
 |     if (first_is_key_frame) { | 
 |       encode_frame_info.frame_type = kFrameTypeKey; | 
 |     } else { | 
 |       if (last_gop_use_alt_ref) { | 
 |         encode_frame_info.frame_type = kFrameTypeOverlay; | 
 |       } else { | 
 |         encode_frame_info.frame_type = kFrameTypeGolden; | 
 |       } | 
 |     } | 
 |  | 
 |     encode_frame_info.show_idx = first_show_idx; | 
 |     encode_frame_info.coding_index = start_coding_index; | 
 |  | 
 |     encode_frame_info.ref_frame_info = ref_frame_info; | 
 |     PostUpdateRefFrameInfo(encode_frame_info.frame_type, | 
 |                            encode_frame_info.coding_index, &ref_frame_info); | 
 |  | 
 |     group_of_picture->encode_frame_list.push_back(encode_frame_info); | 
 |   } | 
 |  | 
 |   const int show_frame_count = coding_frame_count - use_alt_ref; | 
 |   if (use_alt_ref) { | 
 |     // If there is alternate reference, it is always coded at the second place. | 
 |     // Its show index (or timestamp) is at the last of this group | 
 |     EncodeFrameInfo encode_frame_info; | 
 |     encode_frame_info.frame_type = kFrameTypeAltRef; | 
 |     encode_frame_info.show_idx = first_show_idx + show_frame_count; | 
 |     encode_frame_info.coding_index = start_coding_index + 1; | 
 |  | 
 |     encode_frame_info.ref_frame_info = ref_frame_info; | 
 |     PostUpdateRefFrameInfo(encode_frame_info.frame_type, | 
 |                            encode_frame_info.coding_index, &ref_frame_info); | 
 |  | 
 |     group_of_picture->encode_frame_list.push_back(encode_frame_info); | 
 |   } | 
 |  | 
 |   // Encode the rest show inter frames. | 
 |   for (int i = 1; i < show_frame_count; ++i) { | 
 |     EncodeFrameInfo encode_frame_info; | 
 |     encode_frame_info.frame_type = kFrameTypeInter; | 
 |     encode_frame_info.show_idx = first_show_idx + i; | 
 |     encode_frame_info.coding_index = start_coding_index + use_alt_ref + i; | 
 |  | 
 |     encode_frame_info.ref_frame_info = ref_frame_info; | 
 |     PostUpdateRefFrameInfo(encode_frame_info.frame_type, | 
 |                            encode_frame_info.coding_index, &ref_frame_info); | 
 |  | 
 |     group_of_picture->encode_frame_list.push_back(encode_frame_info); | 
 |   } | 
 | } | 
 |  | 
 | // Gets group of picture information from VP9's decision, and update | 
 | // |group_of_picture| accordingly. | 
 | // This is called at the starting of encoding of each group of picture. | 
 | static void UpdateGroupOfPicture(const VP9_COMP *cpi, int start_coding_index, | 
 |                                  const RefFrameInfo &start_ref_frame_info, | 
 |                                  GroupOfPicture *group_of_picture) { | 
 |   int first_is_key_frame; | 
 |   int use_alt_ref; | 
 |   int coding_frame_count; | 
 |   int first_show_idx; | 
 |   int last_gop_use_alt_ref; | 
 |   vp9_get_next_group_of_picture(cpi, &first_is_key_frame, &use_alt_ref, | 
 |                                 &coding_frame_count, &first_show_idx, | 
 |                                 &last_gop_use_alt_ref); | 
 |   SetGroupOfPicture(first_is_key_frame, use_alt_ref, coding_frame_count, | 
 |                     first_show_idx, last_gop_use_alt_ref, start_coding_index, | 
 |                     start_ref_frame_info, group_of_picture); | 
 | } | 
 |  | 
 | SimpleEncode::SimpleEncode(int frame_width, int frame_height, | 
 |                            int frame_rate_num, int frame_rate_den, | 
 |                            int target_bitrate, int num_frames, | 
 |                            const char *infile_path, const char *outfile_path) { | 
 |   impl_ptr_ = std::unique_ptr<EncodeImpl>(new EncodeImpl()); | 
 |   frame_width_ = frame_width; | 
 |   frame_height_ = frame_height; | 
 |   frame_rate_num_ = frame_rate_num; | 
 |   frame_rate_den_ = frame_rate_den; | 
 |   target_bitrate_ = target_bitrate; | 
 |   num_frames_ = num_frames; | 
 |   encode_speed_ = 0; | 
 |  | 
 |   frame_coding_index_ = 0; | 
 |   show_frame_count_ = 0; | 
 |  | 
 |   key_frame_group_index_ = 0; | 
 |   key_frame_group_size_ = 0; | 
 |  | 
 |   // TODO(angirbid): Should we keep a file pointer here or keep the file_path? | 
 |   assert(infile_path != nullptr); | 
 |   in_file_ = fopen(infile_path, "r"); | 
 |   if (outfile_path != nullptr) { | 
 |     out_file_ = fopen(outfile_path, "w"); | 
 |   } else { | 
 |     out_file_ = nullptr; | 
 |   } | 
 |   impl_ptr_->cpi = nullptr; | 
 |   impl_ptr_->img_fmt = VPX_IMG_FMT_I420; | 
 |  | 
 |   InitRefFrameInfo(&ref_frame_info_); | 
 | } | 
 |  | 
 | void SimpleEncode::SetEncodeSpeed(int encode_speed) { | 
 |   encode_speed_ = encode_speed; | 
 | } | 
 |  | 
 | void SimpleEncode::ComputeFirstPassStats() { | 
 |   vpx_rational_t frame_rate = | 
 |       make_vpx_rational(frame_rate_num_, frame_rate_den_); | 
 |   const VP9EncoderConfig oxcf = | 
 |       vp9_get_encoder_config(frame_width_, frame_height_, frame_rate, | 
 |                              target_bitrate_, encode_speed_, VPX_RC_FIRST_PASS); | 
 |   VP9_COMP *cpi = init_encoder(&oxcf, impl_ptr_->img_fmt); | 
 |   struct lookahead_ctx *lookahead = cpi->lookahead; | 
 |   int i; | 
 |   int use_highbitdepth = 0; | 
 |   const int num_rows_16x16 = get_num_unit_16x16(frame_height_); | 
 |   const int num_cols_16x16 = get_num_unit_16x16(frame_width_); | 
 | #if CONFIG_VP9_HIGHBITDEPTH | 
 |   use_highbitdepth = cpi->common.use_highbitdepth; | 
 | #endif | 
 |   vpx_image_t img; | 
 |   vpx_img_alloc(&img, impl_ptr_->img_fmt, frame_width_, frame_height_, 1); | 
 |   rewind(in_file_); | 
 |   impl_ptr_->first_pass_stats.clear(); | 
 |   for (i = 0; i < num_frames_; ++i) { | 
 |     assert(!vp9_lookahead_full(lookahead)); | 
 |     if (img_read(&img, in_file_)) { | 
 |       int next_show_idx = vp9_lookahead_next_show_idx(lookahead); | 
 |       int64_t ts_start = | 
 |           timebase_units_to_ticks(&oxcf.g_timebase_in_ts, next_show_idx); | 
 |       int64_t ts_end = | 
 |           timebase_units_to_ticks(&oxcf.g_timebase_in_ts, next_show_idx + 1); | 
 |       YV12_BUFFER_CONFIG sd; | 
 |       image2yuvconfig(&img, &sd); | 
 |       vp9_lookahead_push(lookahead, &sd, ts_start, ts_end, use_highbitdepth, 0); | 
 |       { | 
 |         int64_t time_stamp; | 
 |         int64_t time_end; | 
 |         int flush = 1;  // Makes vp9_get_compressed_data process a frame | 
 |         size_t size; | 
 |         unsigned int frame_flags = 0; | 
 |         ENCODE_FRAME_RESULT encode_frame_info; | 
 |         vp9_init_encode_frame_result(&encode_frame_info); | 
 |         // TODO(angiebird): Call vp9_first_pass directly | 
 |         vp9_get_compressed_data(cpi, &frame_flags, &size, nullptr, &time_stamp, | 
 |                                 &time_end, flush, &encode_frame_info); | 
 |         // vp9_get_compressed_data only generates first pass stats not | 
 |         // compresses data | 
 |         assert(size == 0); | 
 |         // Get vp9 first pass motion vector info. | 
 |         std::vector<MotionVectorInfo> mv_info(num_rows_16x16 * num_cols_16x16); | 
 |         update_motion_vector_info(&encode_frame_info.fp_motion_vector_info[0], | 
 |                                   num_rows_16x16, num_cols_16x16, | 
 |                                   mv_info.data()); | 
 |         fp_motion_vector_info_.push_back(mv_info); | 
 |       } | 
 |       impl_ptr_->first_pass_stats.push_back(vp9_get_frame_stats(&cpi->twopass)); | 
 |     } | 
 |   } | 
 |   vp9_end_first_pass(cpi); | 
 |   // TODO(angiebird): Store the total_stats apart form first_pass_stats | 
 |   impl_ptr_->first_pass_stats.push_back(vp9_get_total_stats(&cpi->twopass)); | 
 |   free_encoder(cpi); | 
 |   rewind(in_file_); | 
 |   vpx_img_free(&img); | 
 |  | 
 |   // Generate key_frame_map based on impl_ptr_->first_pass_stats. | 
 |   key_frame_map_ = ComputeKeyFrameMap(); | 
 | } | 
 |  | 
 | std::vector<std::vector<double>> SimpleEncode::ObserveFirstPassStats() { | 
 |   std::vector<std::vector<double>> output_stats; | 
 |   // TODO(angiebird): This function make several assumptions of | 
 |   // FIRSTPASS_STATS. 1) All elements in FIRSTPASS_STATS are double except the | 
 |   // last one. 2) The last entry of first_pass_stats is the total_stats. | 
 |   // Change the code structure, so that we don't have to make these assumptions | 
 |  | 
 |   // Note the last entry of first_pass_stats is the total_stats, we don't need | 
 |   // it. | 
 |   for (size_t i = 0; i < impl_ptr_->first_pass_stats.size() - 1; ++i) { | 
 |     double *buf_start = | 
 |         reinterpret_cast<double *>(&impl_ptr_->first_pass_stats[i]); | 
 |     // We use - 1 here because the last member in FIRSTPASS_STATS is not double | 
 |     double *buf_end = | 
 |         buf_start + sizeof(impl_ptr_->first_pass_stats[i]) / sizeof(*buf_end) - | 
 |         1; | 
 |     std::vector<double> this_stats(buf_start, buf_end); | 
 |     output_stats.push_back(this_stats); | 
 |   } | 
 |   return output_stats; | 
 | } | 
 |  | 
 | std::vector<std::vector<MotionVectorInfo>> | 
 | SimpleEncode::ObserveFirstPassMotionVectors() { | 
 |   return fp_motion_vector_info_; | 
 | } | 
 |  | 
 | void SimpleEncode::SetExternalGroupOfPicturesMap(int *gop_map, | 
 |                                                  int gop_map_size) { | 
 |   for (int i = 0; i < gop_map_size; ++i) { | 
 |     gop_map_.push_back(gop_map[i]); | 
 |   } | 
 |   // The following will check and modify gop_map_ to make sure the | 
 |   // gop_map_ satisfies the constraints. | 
 |   // 1) Each key frame position should be at the start of a gop. | 
 |   // 2) The last gop should not use an alt ref. | 
 |   assert(gop_map_.size() == key_frame_map_.size()); | 
 |   int last_gop_start = 0; | 
 |   for (int i = 0; static_cast<size_t>(i) < gop_map_.size(); ++i) { | 
 |     if (key_frame_map_[i] == 1 && gop_map_[i] == 0) { | 
 |       fprintf(stderr, "Add an extra gop start at show_idx %d\n", i); | 
 |       // Insert a gop start at key frame location. | 
 |       gop_map_[i] |= kGopMapFlagStart; | 
 |       gop_map_[i] |= kGopMapFlagUseAltRef; | 
 |     } | 
 |     if (gop_map_[i] & kGopMapFlagStart) { | 
 |       last_gop_start = i; | 
 |     } | 
 |   } | 
 |   if (gop_map_[last_gop_start] & kGopMapFlagUseAltRef) { | 
 |     fprintf(stderr, | 
 |             "Last group of pictures starting at show_idx %d shouldn't use alt " | 
 |             "ref\n", | 
 |             last_gop_start); | 
 |     gop_map_[last_gop_start] &= ~kGopMapFlagUseAltRef; | 
 |   } | 
 | } | 
 |  | 
 | std::vector<int> SimpleEncode::ObserveExternalGroupOfPicturesMap() { | 
 |   return gop_map_; | 
 | } | 
 |  | 
 | template <typename T> | 
 | T *GetVectorData(const std::vector<T> &v) { | 
 |   if (v.empty()) { | 
 |     return nullptr; | 
 |   } | 
 |   return const_cast<T *>(v.data()); | 
 | } | 
 |  | 
 | static GOP_COMMAND GetGopCommand(const std::vector<int> &gop_map, | 
 |                                  int start_show_index) { | 
 |   GOP_COMMAND gop_command; | 
 |   if (gop_map.size() > 0) { | 
 |     assert(static_cast<size_t>(start_show_index) < gop_map.size()); | 
 |     assert((gop_map[start_show_index] & kGopMapFlagStart) != 0); | 
 |     int end_show_index = start_show_index + 1; | 
 |     // gop_map[end_show_index] & kGopMapFlagStart == 0 means this is | 
 |     // the start of a gop. | 
 |     while (static_cast<size_t>(end_show_index) < gop_map.size() && | 
 |            (gop_map[end_show_index] & kGopMapFlagStart) == 0) { | 
 |       ++end_show_index; | 
 |     } | 
 |     const int show_frame_count = end_show_index - start_show_index; | 
 |     int use_alt_ref = (gop_map[start_show_index] & kGopMapFlagUseAltRef) != 0; | 
 |     if (static_cast<size_t>(end_show_index) == gop_map.size()) { | 
 |       // This is the last gop group, there must be no altref. | 
 |       use_alt_ref = 0; | 
 |     } | 
 |     gop_command_on(&gop_command, show_frame_count, use_alt_ref); | 
 |   } else { | 
 |     gop_command_off(&gop_command); | 
 |   } | 
 |   return gop_command; | 
 | } | 
 |  | 
 | void SimpleEncode::StartEncode() { | 
 |   assert(impl_ptr_->first_pass_stats.size() > 0); | 
 |   vpx_rational_t frame_rate = | 
 |       make_vpx_rational(frame_rate_num_, frame_rate_den_); | 
 |   VP9EncoderConfig oxcf = | 
 |       vp9_get_encoder_config(frame_width_, frame_height_, frame_rate, | 
 |                              target_bitrate_, encode_speed_, VPX_RC_LAST_PASS); | 
 |   vpx_fixed_buf_t stats; | 
 |   stats.buf = GetVectorData(impl_ptr_->first_pass_stats); | 
 |   stats.sz = sizeof(impl_ptr_->first_pass_stats[0]) * | 
 |              impl_ptr_->first_pass_stats.size(); | 
 |  | 
 |   vp9_set_first_pass_stats(&oxcf, &stats); | 
 |   assert(impl_ptr_->cpi == nullptr); | 
 |   impl_ptr_->cpi = init_encoder(&oxcf, impl_ptr_->img_fmt); | 
 |   vpx_img_alloc(&impl_ptr_->tmp_img, impl_ptr_->img_fmt, frame_width_, | 
 |                 frame_height_, 1); | 
 |  | 
 |   frame_coding_index_ = 0; | 
 |   show_frame_count_ = 0; | 
 |  | 
 |   UpdateKeyFrameGroup(show_frame_count_); | 
 |  | 
 |   const GOP_COMMAND gop_command = GetGopCommand(gop_map_, show_frame_count_); | 
 |   encode_command_set_gop_command(&impl_ptr_->cpi->encode_command, gop_command); | 
 |   UpdateGroupOfPicture(impl_ptr_->cpi, frame_coding_index_, ref_frame_info_, | 
 |                        &group_of_picture_); | 
 |   rewind(in_file_); | 
 |  | 
 |   if (out_file_ != nullptr) { | 
 |     const char *fourcc = "VP90"; | 
 |     // In SimpleEncode, we use time_base = 1 / TICKS_PER_SEC. | 
 |     // Based on that, the ivf_timestamp for each image is set to | 
 |     // show_idx * TICKS_PER_SEC / frame_rate | 
 |     // such that each image's actual timestamp in seconds can be computed as | 
 |     // ivf_timestamp * time_base == show_idx / frame_rate | 
 |     // TODO(angiebird): 1) Add unit test for ivf timestamp. | 
 |     // 2) Simplify the frame_rate setting process. | 
 |     vpx_rational_t time_base = make_vpx_rational(1, TICKS_PER_SEC); | 
 |     ivf_write_file_header_with_video_info(out_file_, *(const uint32_t *)fourcc, | 
 |                                           num_frames_, frame_width_, | 
 |                                           frame_height_, time_base); | 
 |   } | 
 | } | 
 |  | 
 | void SimpleEncode::EndEncode() { | 
 |   free_encoder(impl_ptr_->cpi); | 
 |   impl_ptr_->cpi = nullptr; | 
 |   vpx_img_free(&impl_ptr_->tmp_img); | 
 |   rewind(in_file_); | 
 | } | 
 |  | 
 | void SimpleEncode::UpdateKeyFrameGroup(int key_frame_show_index) { | 
 |   const VP9_COMP *cpi = impl_ptr_->cpi; | 
 |   key_frame_group_index_ = 0; | 
 |   key_frame_group_size_ = vp9_get_frames_to_next_key( | 
 |       &cpi->oxcf, &cpi->frame_info, &cpi->twopass.first_pass_info, | 
 |       key_frame_show_index, cpi->rc.min_gf_interval); | 
 |   assert(key_frame_group_size_ > 0); | 
 |   // Init the reference frame info when a new key frame group appears. | 
 |   InitRefFrameInfo(&ref_frame_info_); | 
 | } | 
 |  | 
 | void SimpleEncode::PostUpdateKeyFrameGroupIndex(FrameType frame_type) { | 
 |   if (frame_type != kFrameTypeAltRef) { | 
 |     // key_frame_group_index_ only counts show frames | 
 |     ++key_frame_group_index_; | 
 |   } | 
 | } | 
 |  | 
 | int SimpleEncode::GetKeyFrameGroupSize() const { return key_frame_group_size_; } | 
 |  | 
 | GroupOfPicture SimpleEncode::ObserveGroupOfPicture() const { | 
 |   return group_of_picture_; | 
 | } | 
 |  | 
 | EncodeFrameInfo SimpleEncode::GetNextEncodeFrameInfo() const { | 
 |   return group_of_picture_ | 
 |       .encode_frame_list[group_of_picture_.next_encode_frame_index]; | 
 | } | 
 |  | 
 | void SimpleEncode::PostUpdateState( | 
 |     const EncodeFrameResult &encode_frame_result) { | 
 |   // This function needs to be called before the increament of | 
 |   // frame_coding_index_ | 
 |   PostUpdateRefFrameInfo(encode_frame_result.frame_type, frame_coding_index_, | 
 |                          &ref_frame_info_); | 
 |   ++frame_coding_index_; | 
 |   if (encode_frame_result.frame_type != kFrameTypeAltRef) { | 
 |     // Only kFrameTypeAltRef is not a show frame | 
 |     ++show_frame_count_; | 
 |   } | 
 |  | 
 |   PostUpdateKeyFrameGroupIndex(encode_frame_result.frame_type); | 
 |   if (key_frame_group_index_ == key_frame_group_size_) { | 
 |     UpdateKeyFrameGroup(show_frame_count_); | 
 |   } | 
 |  | 
 |   IncreaseGroupOfPictureIndex(&group_of_picture_); | 
 |   if (IsGroupOfPictureFinished(group_of_picture_)) { | 
 |     const GOP_COMMAND gop_command = GetGopCommand(gop_map_, show_frame_count_); | 
 |     encode_command_set_gop_command(&impl_ptr_->cpi->encode_command, | 
 |                                    gop_command); | 
 |     // This function needs to be called after ref_frame_info_ is updated | 
 |     // properly in PostUpdateRefFrameInfo() and UpdateKeyFrameGroup(). | 
 |     UpdateGroupOfPicture(impl_ptr_->cpi, frame_coding_index_, ref_frame_info_, | 
 |                          &group_of_picture_); | 
 |   } | 
 | } | 
 |  | 
 | void SimpleEncode::EncodeFrame(EncodeFrameResult *encode_frame_result) { | 
 |   VP9_COMP *cpi = impl_ptr_->cpi; | 
 |   struct lookahead_ctx *lookahead = cpi->lookahead; | 
 |   int use_highbitdepth = 0; | 
 | #if CONFIG_VP9_HIGHBITDEPTH | 
 |   use_highbitdepth = cpi->common.use_highbitdepth; | 
 | #endif | 
 |   // The lookahead's size is set to oxcf->lag_in_frames. | 
 |   // We want to fill lookahead to it's max capacity if possible so that the | 
 |   // encoder can construct alt ref frame in time. | 
 |   // In the other words, we hope vp9_get_compressed_data to encode a frame | 
 |   // every time in the function | 
 |   while (!vp9_lookahead_full(lookahead)) { | 
 |     // TODO(angiebird): Check whether we can move this file read logics to | 
 |     // lookahead | 
 |     if (img_read(&impl_ptr_->tmp_img, in_file_)) { | 
 |       int next_show_idx = vp9_lookahead_next_show_idx(lookahead); | 
 |       int64_t ts_start = | 
 |           timebase_units_to_ticks(&cpi->oxcf.g_timebase_in_ts, next_show_idx); | 
 |       int64_t ts_end = timebase_units_to_ticks(&cpi->oxcf.g_timebase_in_ts, | 
 |                                                next_show_idx + 1); | 
 |       YV12_BUFFER_CONFIG sd; | 
 |       image2yuvconfig(&impl_ptr_->tmp_img, &sd); | 
 |       vp9_lookahead_push(lookahead, &sd, ts_start, ts_end, use_highbitdepth, 0); | 
 |     } else { | 
 |       break; | 
 |     } | 
 |   } | 
 |  | 
 |   if (init_encode_frame_result(encode_frame_result, frame_width_, frame_height_, | 
 |                                impl_ptr_->img_fmt)) { | 
 |     int64_t time_stamp; | 
 |     int64_t time_end; | 
 |     int flush = 1;  // Make vp9_get_compressed_data encode a frame | 
 |     unsigned int frame_flags = 0; | 
 |     ENCODE_FRAME_RESULT encode_frame_info; | 
 |     vp9_init_encode_frame_result(&encode_frame_info); | 
 |     ImageBuffer_to_IMAGE_BUFFER(encode_frame_result->coded_frame, | 
 |                                 &encode_frame_info.coded_frame); | 
 |     vp9_get_compressed_data(cpi, &frame_flags, | 
 |                             &encode_frame_result->coding_data_byte_size, | 
 |                             encode_frame_result->coding_data.get(), &time_stamp, | 
 |                             &time_end, flush, &encode_frame_info); | 
 |     if (out_file_ != nullptr) { | 
 |       ivf_write_frame_header(out_file_, time_stamp, | 
 |                              encode_frame_result->coding_data_byte_size); | 
 |       fwrite(encode_frame_result->coding_data.get(), 1, | 
 |              encode_frame_result->coding_data_byte_size, out_file_); | 
 |     } | 
 |  | 
 |     // vp9_get_compressed_data is expected to encode a frame every time, so the | 
 |     // data size should be greater than zero. | 
 |     if (encode_frame_result->coding_data_byte_size <= 0) { | 
 |       fprintf(stderr, "Coding data size <= 0\n"); | 
 |       abort(); | 
 |     } | 
 |     const size_t max_coding_data_byte_size = | 
 |         get_max_coding_data_byte_size(frame_width_, frame_height_); | 
 |     if (encode_frame_result->coding_data_byte_size > | 
 |         max_coding_data_byte_size) { | 
 |       fprintf(stderr, "Coding data size exceeds the maximum.\n"); | 
 |       abort(); | 
 |     } | 
 |  | 
 |     update_encode_frame_result(encode_frame_result, &encode_frame_info); | 
 |     PostUpdateState(*encode_frame_result); | 
 |   } else { | 
 |     // TODO(angiebird): Clean up encode_frame_result. | 
 |     fprintf(stderr, "init_encode_frame_result() failed.\n"); | 
 |     this->EndEncode(); | 
 |   } | 
 | } | 
 |  | 
 | void SimpleEncode::EncodeFrameWithQuantizeIndex( | 
 |     EncodeFrameResult *encode_frame_result, int quantize_index) { | 
 |   encode_command_set_external_quantize_index(&impl_ptr_->cpi->encode_command, | 
 |                                              quantize_index); | 
 |   EncodeFrame(encode_frame_result); | 
 |   encode_command_reset_external_quantize_index(&impl_ptr_->cpi->encode_command); | 
 | } | 
 |  | 
 | void SimpleEncode::EncodeFrameWithTargetFrameBits( | 
 |     EncodeFrameResult *encode_frame_result, int target_frame_bits) { | 
 |   encode_command_set_target_frame_bits(&impl_ptr_->cpi->encode_command, | 
 |                                        target_frame_bits); | 
 |   EncodeFrame(encode_frame_result); | 
 |   encode_command_reset_target_frame_bits(&impl_ptr_->cpi->encode_command); | 
 | } | 
 |  | 
 | static int GetCodingFrameNumFromGopMap(const std::vector<int> &gop_map) { | 
 |   int start_show_index = 0; | 
 |   int coding_frame_count = 0; | 
 |   while (static_cast<size_t>(start_show_index) < gop_map.size()) { | 
 |     const GOP_COMMAND gop_command = GetGopCommand(gop_map, start_show_index); | 
 |     start_show_index += gop_command.show_frame_count; | 
 |     coding_frame_count += gop_command_coding_frame_count(&gop_command); | 
 |   } | 
 |   assert(start_show_index == gop_map.size()); | 
 |   return coding_frame_count; | 
 | } | 
 |  | 
 | int SimpleEncode::GetCodingFrameNum() const { | 
 |   assert(impl_ptr_->first_pass_stats.size() > 0); | 
 |   if (gop_map_.size() > 0) { | 
 |     return GetCodingFrameNumFromGopMap(gop_map_); | 
 |   } | 
 |  | 
 |   // These are the default settings for now. | 
 |   const int multi_layer_arf = 0; | 
 |   const int allow_alt_ref = 1; | 
 |   vpx_rational_t frame_rate = | 
 |       make_vpx_rational(frame_rate_num_, frame_rate_den_); | 
 |   const VP9EncoderConfig oxcf = | 
 |       vp9_get_encoder_config(frame_width_, frame_height_, frame_rate, | 
 |                              target_bitrate_, encode_speed_, VPX_RC_LAST_PASS); | 
 |   FRAME_INFO frame_info = vp9_get_frame_info(&oxcf); | 
 |   FIRST_PASS_INFO first_pass_info; | 
 |   fps_init_first_pass_info(&first_pass_info, | 
 |                            GetVectorData(impl_ptr_->first_pass_stats), | 
 |                            num_frames_); | 
 |   return vp9_get_coding_frame_num(&oxcf, &frame_info, &first_pass_info, | 
 |                                   multi_layer_arf, allow_alt_ref); | 
 | } | 
 |  | 
 | std::vector<int> SimpleEncode::ComputeKeyFrameMap() const { | 
 |   // The last entry of first_pass_stats is the overall stats. | 
 |   assert(impl_ptr_->first_pass_stats.size() == num_frames_ + 1); | 
 |   vpx_rational_t frame_rate = | 
 |       make_vpx_rational(frame_rate_num_, frame_rate_den_); | 
 |   const VP9EncoderConfig oxcf = | 
 |       vp9_get_encoder_config(frame_width_, frame_height_, frame_rate, | 
 |                              target_bitrate_, encode_speed_, VPX_RC_LAST_PASS); | 
 |   FRAME_INFO frame_info = vp9_get_frame_info(&oxcf); | 
 |   FIRST_PASS_INFO first_pass_info; | 
 |   fps_init_first_pass_info(&first_pass_info, | 
 |                            GetVectorData(impl_ptr_->first_pass_stats), | 
 |                            num_frames_); | 
 |   std::vector<int> key_frame_map(num_frames_, 0); | 
 |   vp9_get_key_frame_map(&oxcf, &frame_info, &first_pass_info, | 
 |                         GetVectorData(key_frame_map)); | 
 |   return key_frame_map; | 
 | } | 
 |  | 
 | std::vector<int> SimpleEncode::ObserveKeyFrameMap() const { | 
 |   return key_frame_map_; | 
 | } | 
 |  | 
 | uint64_t SimpleEncode::GetFramePixelCount() const { | 
 |   assert(frame_width_ % 2 == 0); | 
 |   assert(frame_height_ % 2 == 0); | 
 |   switch (impl_ptr_->img_fmt) { | 
 |     case VPX_IMG_FMT_I420: return frame_width_ * frame_height_ * 3 / 2; | 
 |     case VPX_IMG_FMT_I422: return frame_width_ * frame_height_ * 2; | 
 |     case VPX_IMG_FMT_I444: return frame_width_ * frame_height_ * 3; | 
 |     case VPX_IMG_FMT_I440: return frame_width_ * frame_height_ * 2; | 
 |     case VPX_IMG_FMT_I42016: return frame_width_ * frame_height_ * 3 / 2; | 
 |     case VPX_IMG_FMT_I42216: return frame_width_ * frame_height_ * 2; | 
 |     case VPX_IMG_FMT_I44416: return frame_width_ * frame_height_ * 3; | 
 |     case VPX_IMG_FMT_I44016: return frame_width_ * frame_height_ * 2; | 
 |     default: return 0; | 
 |   } | 
 | } | 
 |  | 
 | SimpleEncode::~SimpleEncode() { | 
 |   if (in_file_ != nullptr) { | 
 |     fclose(in_file_); | 
 |   } | 
 |   if (out_file_ != nullptr) { | 
 |     fclose(out_file_); | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace vp9 |