/* * Copyright (c) 2017 Intel Corporation. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sub license, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * This file has some codes for vp9 bitstream built, and * they are ported from libvpx (https://github.com/webmproject/libvpx/). * The original copyright and licence statement as below. */ /* * Copyright (c) 2010 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "va_display.h" #define KEY_FRAME 0 #define INTER_FRAME 1 #define CHECK_VASTATUS(va_status,func) \ if (va_status != VA_STATUS_SUCCESS) { \ fprintf(stderr,"%s:%s (%d) failed,exit\n", __func__, func, __LINE__); \ exit(1); \ } static VADisplay va_dpy; static int rc_mode; static int picture_width; static int picture_height; static int frame_size; static uint8_t *newImageBuffer = 0; static int hrd_window = 1500; static int vbr_max; static int qp_value = 60; static int intra_period = 30; static int frame_bit_rate = -1; static int frame_rate = 30; static int lf_level = 10; static int opt_header = 0; static int current_slot; static int frame_number; static int current_frame_type; static VASurfaceID vp9_ref_list[8]; #define SURFACE_NUM 8 #define SID_INPUT_PICTURE_0 0 #define SID_INPUT_PICTURE_1 1 #define SID_REFERENCE_PICTURE_L0 2 #define SID_REFERENCE_PICTURE_L1 3 #define SID_NUMBER 2 static VASurfaceID surface_ids[SID_NUMBER]; static VASurfaceID ref_surfaces[SURFACE_NUM + SID_NUMBER]; static int use_slot[SURFACE_NUM]; #ifndef VA_FOURCC_I420 #define VA_FOURCC_I420 0x30323449 #endif static int rc_default_mode[4] = { VA_RC_CQP, VA_RC_CBR, VA_RC_VBR, VA_RC_NONE }; static int vp9enc_entrypoint_lists[2] = { VAEntrypointEncSlice, VAEntrypointEncSliceLP }; static int select_entrypoint = -1; static const struct option long_opts[] = { {"help", no_argument, NULL, 0 }, {"rcmode", required_argument, NULL, 1 }, {"qp", required_argument, NULL, 2 }, {"intra_period", required_argument, NULL, 3 }, {"fb", required_argument, NULL, 4 }, {"lf_level", required_argument, NULL, 6 }, {"opt_header", required_argument, NULL, 7}, {"hrd_win", required_argument, NULL, 8}, {"vbr_max", required_argument, NULL, 9}, {"fn_num", required_argument, NULL, 10}, {"low_power", required_argument, NULL, 11}, {NULL, no_argument, NULL, 0 } }; struct vp9enc_bit_buffer { uint8_t *bit_buffer; int bit_offset; }; struct upload_thread_param { FILE *yuv_fp; VASurfaceID surface_id; }; struct vp9encode_context { VAProfile profile; VAEncSequenceParameterBufferVP9 seq_param; VAEncPictureParameterBufferVP9 pic_param; VAContextID context_id; VAConfigID config_id; VABufferID seq_param_buf_id; /* Sequence level parameter */ VABufferID pic_param_buf_id; /* Picture level parameter */ VABufferID codedbuf_buf_id; /* Output buffer, compressed data */ VABufferID misc_parameter_hrd_buf_id; /* for VAEncMiscParameterTypeVP9PerSegmantParam. VAQMatrixBufferType */ VABufferID qmatrix_buf_id; /* the buffer for VP9 super block. VAEncMacroblockMapBufferType */ VABufferID mb_seg_buf_id; VABufferID raw_data_header_buf_id; VABufferID raw_data_buf_id; VABufferID misc_fr_buf_id; VABufferID misc_rc_buf_id; int codedbuf_i_size; int codedbuf_pb_size; int current_input_surface; int rate_control_method; struct upload_thread_param upload_thread_param; pthread_t upload_thread_id; int upload_thread_value; }; static struct vp9encode_context vp9enc_context; static void vp9enc_write_word(char *ptr, uint32_t value) { uint8_t *tmp; tmp = (uint8_t *)ptr; *(tmp) = (value >> 0) & 0XFF; *(tmp + 1) = (value >> 8) & 0XFF; } static void vp9enc_write_dword(char *ptr, uint32_t value) { uint8_t *tmp; tmp = (uint8_t *)ptr; *(tmp) = (value >> 0) & 0XFF; *(tmp + 1) = (value >> 8) & 0XFF; *(tmp + 2) = (value >> 16) & 0XFF; *(tmp + 3) = (value >> 24) & 0XFF; } static void vp9enc_wb_write_bit(struct vp9enc_bit_buffer *wb, int bit) { const int off = wb->bit_offset; const int p = off / 8; const int q = 7 - off % 8; if (q == 7) { wb->bit_buffer[p] = bit << q; } else { wb->bit_buffer[p] &= ~(1 << q); wb->bit_buffer[p] |= bit << q; } wb->bit_offset = off + 1; } static void vp9enc_wb_write_literal(struct vp9enc_bit_buffer *wb, int data, int bits) { int bit; for (bit = bits - 1; bit >= 0; bit--) vp9enc_wb_write_bit(wb, (data >> bit) & 1); } static void vp9enc_write_bitdepth_colorspace_sampling(int codec_profile, struct vp9enc_bit_buffer *wb) { if (codec_profile >= 2) { /* the bit-depth will be added for VP9Profile2/3 */ /* this will be added later */ assert(0); } /* Add the default color-space */ vp9enc_wb_write_literal(wb, 0, 3); vp9enc_wb_write_bit(wb, 0); // 0: [16, 235] (i.e. xvYCC), 1: [0, 255] /* the sampling_x/y will be added for VP9Profile1/2/3 later */ } #define MAX_TILE_WIDTH_B64 64 #define MIN_TILE_WIDTH_B64 4 static int vp9enc_get_min_log2_tile_cols(const int sb_cols) { int min_log2 = 0; while ((MAX_TILE_WIDTH_B64 << min_log2) < sb_cols) ++min_log2; return min_log2; } static int vp9enc_get_max_log2_tile_cols(const int sb_cols) { int max_log2 = 1; while ((sb_cols >> max_log2) >= MIN_TILE_WIDTH_B64) ++max_log2; return max_log2 - 1; } static void vp9enc_write_uncompressed_header(struct vp9encode_context *enc_context, char *header_data, int *header_length) { #define VP9_SYNC_CODE_0 0x49 #define VP9_SYNC_CODE_1 0x83 #define VP9_SYNC_CODE_2 0x42 #define VP9_FRAME_MARKER 0x2 #define REFS_PER_FRAME 3 #define REF_FRAMES_LOG2 3 #define REF_FRAMES (1 << REF_FRAMES_LOG2) #define VP9_KEY_FRAME 0 VAEncPictureParameterBufferVP9 *pic_param; struct vp9enc_bit_buffer *wb, vp9_wb; if (!header_data || !header_length) return; pic_param = &enc_context->pic_param; vp9_wb.bit_buffer = (uint8_t *)header_data; vp9_wb.bit_offset = 0; wb = &vp9_wb; vp9enc_wb_write_literal(wb, VP9_FRAME_MARKER, 2); vp9enc_wb_write_literal(wb, 0, 2); vp9enc_wb_write_bit(wb, 0); // show_existing_frame vp9enc_wb_write_bit(wb, pic_param->pic_flags.bits.frame_type); vp9enc_wb_write_bit(wb, pic_param->pic_flags.bits.show_frame); vp9enc_wb_write_bit(wb, pic_param->pic_flags.bits.error_resilient_mode); if (pic_param->pic_flags.bits.frame_type == VP9_KEY_FRAME) { vp9enc_wb_write_literal(wb, VP9_SYNC_CODE_0, 8); vp9enc_wb_write_literal(wb, VP9_SYNC_CODE_1, 8); vp9enc_wb_write_literal(wb, VP9_SYNC_CODE_2, 8); vp9enc_write_bitdepth_colorspace_sampling(0, wb); /* write the encoded frame size */ vp9enc_wb_write_literal(wb, pic_param->frame_width_dst - 1, 16); vp9enc_wb_write_literal(wb, pic_param->frame_height_dst - 1, 16); /* write display size */ if ((pic_param->frame_width_dst != pic_param->frame_width_src) || (pic_param->frame_height_dst != pic_param->frame_height_src)) { vp9enc_wb_write_bit(wb, 1); vp9enc_wb_write_literal(wb, pic_param->frame_width_src - 1, 16); vp9enc_wb_write_literal(wb, pic_param->frame_height_src - 1, 16); } else vp9enc_wb_write_bit(wb, 0); } else { /* for the non-Key frame */ if (!pic_param->pic_flags.bits.show_frame) vp9enc_wb_write_bit(wb, pic_param->pic_flags.bits.intra_only); if (!pic_param->pic_flags.bits.error_resilient_mode) vp9enc_wb_write_literal(wb, pic_param->pic_flags.bits.reset_frame_context, 2); if (pic_param->pic_flags.bits.intra_only) { vp9enc_wb_write_literal(wb, VP9_SYNC_CODE_0, 8); vp9enc_wb_write_literal(wb, VP9_SYNC_CODE_1, 8); vp9enc_wb_write_literal(wb, VP9_SYNC_CODE_2, 8); /* Add the bit_depth for VP9Profile1/2/3 */ /* write the refreshed_frame_flags */ vp9enc_wb_write_literal(wb, pic_param->refresh_frame_flags, REF_FRAMES); /* write the encoded frame size */ vp9enc_wb_write_literal(wb, pic_param->frame_width_dst - 1, 16); vp9enc_wb_write_literal(wb, pic_param->frame_height_dst - 1, 16); /* write display size */ if ((pic_param->frame_width_dst != pic_param->frame_width_src) || (pic_param->frame_height_dst != pic_param->frame_height_src)) { vp9enc_wb_write_bit(wb, 1); vp9enc_wb_write_literal(wb, pic_param->frame_width_src - 1, 16); vp9enc_wb_write_literal(wb, pic_param->frame_height_src - 1, 16); } else vp9enc_wb_write_bit(wb, 0); } else { /* The refresh_frame_map is for the next frame so that it can select Last/Godlen/Alt ref_index */ vp9enc_wb_write_literal(wb, pic_param->refresh_frame_flags, REF_FRAMES); vp9enc_wb_write_literal(wb, pic_param->ref_flags.bits.ref_last_idx, REF_FRAMES_LOG2); vp9enc_wb_write_bit(wb, pic_param->ref_flags.bits.ref_last_sign_bias); vp9enc_wb_write_literal(wb, pic_param->ref_flags.bits.ref_gf_idx, REF_FRAMES_LOG2); vp9enc_wb_write_bit(wb, pic_param->ref_flags.bits.ref_gf_sign_bias); vp9enc_wb_write_literal(wb, pic_param->ref_flags.bits.ref_arf_idx, REF_FRAMES_LOG2); vp9enc_wb_write_bit(wb, pic_param->ref_flags.bits.ref_arf_sign_bias); /* write three bits with zero so that it can parse width/height directly */ vp9enc_wb_write_literal(wb, 0, 3); vp9enc_wb_write_literal(wb, pic_param->frame_width_dst - 1, 16); vp9enc_wb_write_literal(wb, pic_param->frame_height_dst - 1, 16); /* write display size */ if ((pic_param->frame_width_dst != pic_param->frame_width_src) || (pic_param->frame_height_dst != pic_param->frame_height_src)) { vp9enc_wb_write_bit(wb, 1); vp9enc_wb_write_literal(wb, pic_param->frame_width_src - 1, 16); vp9enc_wb_write_literal(wb, pic_param->frame_height_src - 1, 16); } else vp9enc_wb_write_bit(wb, 0); vp9enc_wb_write_bit(wb, pic_param->pic_flags.bits.allow_high_precision_mv); #define SWITCHABLE_FILTER 4 #define FILTER_MASK 3 if (pic_param->pic_flags.bits.mcomp_filter_type == SWITCHABLE_FILTER) vp9enc_wb_write_bit(wb, 1); else { const int filter_to_literal[4] = { 1, 0, 2, 3 }; uint8_t filter_flag = pic_param->pic_flags.bits.mcomp_filter_type; filter_flag = filter_flag & FILTER_MASK; vp9enc_wb_write_bit(wb, 0); vp9enc_wb_write_literal(wb, filter_to_literal[filter_flag], 2); } } } /* write refresh_frame_context/paralle frame_decoding */ if (!pic_param->pic_flags.bits.error_resilient_mode) { vp9enc_wb_write_bit(wb, pic_param->pic_flags.bits.refresh_frame_context); vp9enc_wb_write_bit(wb, pic_param->pic_flags.bits.frame_parallel_decoding_mode); } vp9enc_wb_write_literal(wb, pic_param->pic_flags.bits.frame_context_idx, 2); /* write loop filter */ pic_param->bit_offset_lf_level = wb->bit_offset; vp9enc_wb_write_literal(wb, pic_param->filter_level, 6); vp9enc_wb_write_literal(wb, pic_param->sharpness_level, 3); { int i, mode_flag; vp9enc_wb_write_bit(wb, 1); vp9enc_wb_write_bit(wb, 1); pic_param->bit_offset_ref_lf_delta = wb->bit_offset; for (i = 0; i < 4; i++) { /* * This check is skipped to prepare the bit_offset_lf_ref if (pic_param->ref_lf_delta[i] == 0) { vp9enc_wb_write_bit(wb, 0); continue; } */ vp9enc_wb_write_bit(wb, 1); mode_flag = pic_param->ref_lf_delta[i]; if (mode_flag >=0) { vp9enc_wb_write_literal(wb, mode_flag & (0x3F), 6); vp9enc_wb_write_bit(wb, 0); } else { mode_flag = -mode_flag; vp9enc_wb_write_literal(wb, mode_flag & (0x3F), 6); vp9enc_wb_write_bit(wb, 1); } } pic_param->bit_offset_mode_lf_delta = wb->bit_offset; for (i = 0; i < 2; i++) { /* * This check is skipped to prepare the bit_offset_lf_ref if (pic_param->mode_lf_delta[i] == 0) { vp9enc_wb_write_bit(wb, 0); continue; } */ vp9enc_wb_write_bit(wb, 1); mode_flag = pic_param->mode_lf_delta[i]; if (mode_flag >=0) { vp9enc_wb_write_literal(wb, mode_flag & (0x3F), 6); vp9enc_wb_write_bit(wb, 0); } else { mode_flag = -mode_flag; vp9enc_wb_write_literal(wb, mode_flag & (0x3F), 6); vp9enc_wb_write_bit(wb, 1); } } } /* write basic quantizer */ pic_param->bit_offset_qindex = wb->bit_offset; vp9enc_wb_write_literal(wb, pic_param->luma_ac_qindex, 8); if (pic_param->luma_dc_qindex_delta) { int delta_q = pic_param->luma_dc_qindex_delta; vp9enc_wb_write_bit(wb, 1); vp9enc_wb_write_literal(wb, abs(delta_q), 4); vp9enc_wb_write_bit(wb, delta_q < 0); } else vp9enc_wb_write_bit(wb, 0); if (pic_param->chroma_dc_qindex_delta) { int delta_q = pic_param->chroma_dc_qindex_delta; vp9enc_wb_write_bit(wb, 1); vp9enc_wb_write_literal(wb, abs(delta_q), 4); vp9enc_wb_write_bit(wb, delta_q < 0); } else vp9enc_wb_write_bit(wb, 0); if (pic_param->chroma_ac_qindex_delta) { int delta_q = pic_param->chroma_ac_qindex_delta; vp9enc_wb_write_bit(wb, 1); vp9enc_wb_write_literal(wb, abs(delta_q), 4); vp9enc_wb_write_bit(wb, delta_q < 0); } else vp9enc_wb_write_bit(wb, 0); /* segment is not enabled */ //vp9enc_wb_write_bit(wb, pic_param->pic_flags.bits.segmentation_enabled); vp9enc_wb_write_bit(wb, 0); { int sb_cols = (pic_param->frame_width_dst + 63) / 64; int min_log2_tile_cols, max_log2_tile_cols; int col_data; /* write tile column info */ min_log2_tile_cols = vp9enc_get_min_log2_tile_cols(sb_cols); max_log2_tile_cols = vp9enc_get_max_log2_tile_cols(sb_cols); col_data = pic_param->log2_tile_columns - min_log2_tile_cols; while(col_data--) { vp9enc_wb_write_bit(wb, 1); } if (pic_param->log2_tile_columns < max_log2_tile_cols) vp9enc_wb_write_bit(wb, 0); /* write tile row info */ vp9enc_wb_write_bit(wb, pic_param->log2_tile_rows); if (pic_param->log2_tile_rows) vp9enc_wb_write_bit(wb, (pic_param->log2_tile_rows != 1)); } /* get the bit_offset of the first partition size */ pic_param->bit_offset_first_partition_size = wb->bit_offset; /* reserve the space for writing the first partitions ize */ vp9enc_wb_write_literal(wb, 0, 16); *header_length = (wb->bit_offset + 7) / 8; } static void vp9enc_upload_yuv_to_surface(FILE *yuv_fp, VASurfaceID surface_id) { VAImage surface_image; VAStatus va_status; void *surface_p = NULL; uint8_t *y_src, *u_src, *v_src; uint8_t *y_dst, *u_dst, *v_dst; int y_size = picture_width * picture_height; int u_size = (picture_width >> 1) * (picture_height >> 1); int row, col; size_t n_items; do { n_items = fread(newImageBuffer, frame_size, 1, yuv_fp); } while (n_items != 1); va_status = vaDeriveImage(va_dpy, surface_id, &surface_image); CHECK_VASTATUS(va_status,"vaDeriveImage"); vaMapBuffer(va_dpy, surface_image.buf, &surface_p); assert(VA_STATUS_SUCCESS == va_status); y_src = newImageBuffer; u_src = newImageBuffer + y_size; /* UV offset for NV12 */ v_src = newImageBuffer + y_size + u_size; y_dst = surface_p + surface_image.offsets[0]; u_dst = surface_p + surface_image.offsets[1]; /* UV offset for NV12 */ v_dst = surface_p + surface_image.offsets[2]; /* Y plane */ for (row = 0; row < surface_image.height; row++) { memcpy(y_dst, y_src, surface_image.width); y_dst += surface_image.pitches[0]; y_src += picture_width; } if (surface_image.format.fourcc == VA_FOURCC_NV12) { /* UV plane */ for (row = 0; row < surface_image.height / 2; row++) { for (col = 0; col < surface_image.width / 2; col++) { u_dst[col * 2] = u_src[col]; u_dst[col * 2 + 1] = v_src[col]; } u_dst += surface_image.pitches[1]; u_src += (picture_width / 2); v_src += (picture_width / 2); } } else if (surface_image.format.fourcc == VA_FOURCC_YV12 || surface_image.format.fourcc == VA_FOURCC_I420) { const int U = surface_image.format.fourcc == VA_FOURCC_I420 ? 1 : 2; const int V = surface_image.format.fourcc == VA_FOURCC_I420 ? 2 : 1; u_dst = surface_p + surface_image.offsets[U]; v_dst = surface_p + surface_image.offsets[V]; for (row = 0; row < surface_image.height / 2; row++) { memcpy(u_dst, u_src, surface_image.width / 2); memcpy(v_dst, v_src, surface_image.width / 2); u_dst += surface_image.pitches[U]; v_dst += surface_image.pitches[V]; u_src += (picture_width / 2); v_src += (picture_width / 2); } } vaUnmapBuffer(va_dpy, surface_image.buf); vaDestroyImage(va_dpy, surface_image.image_id); } static void * vp9enc_upload_thread_function(void *data) { struct upload_thread_param *param = data; vp9enc_upload_yuv_to_surface(param->yuv_fp, param->surface_id); return NULL; } static void vp9enc_alloc_encode_resource(FILE *yuv_fp) { VAStatus va_status; VASurfaceAttrib attrib; int i; attrib.type = VASurfaceAttribPixelFormat; attrib.flags = VA_SURFACE_ATTRIB_SETTABLE; attrib.value.type = VAGenericValueTypeInteger; attrib.value.value.i = VA_FOURCC_NV12; // Create surface va_status = vaCreateSurfaces( va_dpy, VA_RT_FORMAT_YUV420, picture_width, picture_height, surface_ids, SID_NUMBER, &attrib, 1 ); CHECK_VASTATUS(va_status, "vaCreateSurfaces"); // Create surface va_status = vaCreateSurfaces( va_dpy, VA_RT_FORMAT_YUV420, picture_width, picture_height, ref_surfaces, SURFACE_NUM, &attrib, 1 ); for (i = 0; i < SID_NUMBER; i++) ref_surfaces[i + SURFACE_NUM] = surface_ids[i]; CHECK_VASTATUS(va_status, "vaCreateSurfaces"); newImageBuffer = (uint8_t *)malloc(frame_size); /* firstly upload YUV data to SID_INPUT_PICTURE_0 */ vp9enc_context.upload_thread_param.yuv_fp = yuv_fp; vp9enc_context.upload_thread_param.surface_id = surface_ids[SID_INPUT_PICTURE_0]; vp9enc_context.upload_thread_value = pthread_create(&vp9enc_context.upload_thread_id, NULL, vp9enc_upload_thread_function, (void*)&vp9enc_context.upload_thread_param); } static void vp9enc_create_encode_pipe(FILE *yuv_fp) { VAEntrypoint entrypoints[5]; int num_entrypoints,slice_entrypoint; VAConfigAttrib attrib[2]; int major_ver, minor_ver; VAStatus va_status; int i; va_dpy = va_open_display(); va_status = vaInitialize(va_dpy, &major_ver, &minor_ver); CHECK_VASTATUS(va_status, "vaInitialize"); vaQueryConfigEntrypoints(va_dpy, vp9enc_context.profile, entrypoints, &num_entrypoints); for (slice_entrypoint = 0; slice_entrypoint < num_entrypoints; slice_entrypoint++) { if (select_entrypoint == -1) { for (i = 0; i < 2; i++) { if (entrypoints[slice_entrypoint] == vp9enc_entrypoint_lists[i]) break; } if (i < 2) { select_entrypoint = i; break; } } else { assert(select_entrypoint == 0 || select_entrypoint == 1); if (entrypoints[slice_entrypoint] == vp9enc_entrypoint_lists[select_entrypoint]) break; } } if (slice_entrypoint == num_entrypoints) { /* not find Slice entry point */ assert(0); } /* find out the format for the render target, and rate control mode */ attrib[0].type = VAConfigAttribRTFormat; attrib[1].type = VAConfigAttribRateControl; vaGetConfigAttributes(va_dpy, vp9enc_context.profile, vp9enc_entrypoint_lists[select_entrypoint], &attrib[0], 2); if ((attrib[0].value & VA_RT_FORMAT_YUV420) == 0) { /* not find desired YUV420 RT format */ assert(0); } if ((attrib[1].value & vp9enc_context.rate_control_method) == 0) { /* Can't find matched RC mode */ printf("Can't find the desired RC mode, exit\n"); assert(0); } attrib[0].value = VA_RT_FORMAT_YUV420; /* set to desired RT format */ attrib[1].value = vp9enc_context.rate_control_method; /* set to desired RC mode */ va_status = vaCreateConfig(va_dpy, vp9enc_context.profile, vp9enc_entrypoint_lists[select_entrypoint], &attrib[0], 2,&vp9enc_context.config_id); CHECK_VASTATUS(va_status, "vaCreateConfig"); vp9enc_alloc_encode_resource(yuv_fp); /* Create a context for this decode pipe */ /* the surface is added to the render_target list when creating the context */ va_status = vaCreateContext(va_dpy, vp9enc_context.config_id, picture_width, picture_height, VA_PROGRESSIVE, ref_surfaces, SURFACE_NUM + 2, &vp9enc_context.context_id); CHECK_VASTATUS(va_status, "vaCreateContext"); } static void vp9enc_destory_encode_pipe() { vaDestroyContext(va_dpy,vp9enc_context.context_id); vaDestroyConfig(va_dpy,vp9enc_context.config_id); vaTerminate(va_dpy); va_close_display(va_dpy); } static void vp9enc_release_encode_resource() { pthread_join(vp9enc_context.upload_thread_id, NULL); free(newImageBuffer); vaDestroySurfaces(va_dpy, surface_ids, SID_NUMBER); vaDestroySurfaces(va_dpy, ref_surfaces, SURFACE_NUM); } static int vp9enc_get_free_slot() { int i, index = -1; for (i = 0; i < SURFACE_NUM; i++) { if (use_slot[i] == 0) { index = i; break; } } if (index < 0) { printf("WARNING: No free slot to store the reconstructed frame \n"); index = SURFACE_NUM - 1; } return index; } static void vp9enc_update_reference_list(void) { VASurfaceID last_surf; int last_slot; int i; /* Todo: Add the full support of reference frames */ if (current_frame_type == KEY_FRAME) { memset(use_slot, 0, sizeof(use_slot)); use_slot[current_slot] = 1; for (i = 0; i < SURFACE_NUM; i++) vp9_ref_list[i] = ref_surfaces[current_slot]; return; } last_slot = -1; use_slot[current_slot] = 1; last_surf = vp9_ref_list[0]; vp9_ref_list[0] = ref_surfaces[current_slot]; for (i = 0; i < SURFACE_NUM; i++) { if (ref_surfaces[i] == last_surf) { last_slot = i; break; } } if (last_slot != -1) { int used_flag = 0; for (i = 1; i < SURFACE_NUM;i++) { if (vp9_ref_list[i] == last_surf) { used_flag = 1; break; } } if (!used_flag) use_slot[last_slot] = 0; } } static void vp9enc_update_picture_parameter(int frame_type) { VAEncPictureParameterBufferVP9 *pic_param; int recon_index; VASurfaceID current_surface; int ref_num = 0; int i = 0; recon_index = vp9enc_get_free_slot(); current_slot = recon_index; current_surface = ref_surfaces[recon_index]; pic_param = &vp9enc_context.pic_param; pic_param->reconstructed_frame = current_surface; pic_param->coded_buf = vp9enc_context.codedbuf_buf_id; ref_num = sizeof(pic_param->reference_frames) / sizeof(VASurfaceID); if (frame_type == KEY_FRAME) { pic_param->pic_flags.bits.frame_type = KEY_FRAME; pic_param->pic_flags.bits.frame_context_idx = 0; pic_param->ref_flags.bits.ref_last_idx = 0; pic_param->ref_flags.bits.ref_gf_idx = 0; pic_param->ref_flags.bits.ref_arf_idx = 0; pic_param->pic_flags.bits.frame_context_idx = 0; pic_param->ref_flags.bits.ref_frame_ctrl_l0 = 0; for (i = 0;i < ref_num;i++) pic_param->reference_frames[i] = VA_INVALID_ID; } else { pic_param->refresh_frame_flags = 0x01; pic_param->pic_flags.bits.frame_type = INTER_FRAME; pic_param->ref_flags.bits.ref_frame_ctrl_l0 = 0x7; pic_param->ref_flags.bits.ref_last_idx = 0; pic_param->ref_flags.bits.ref_gf_idx = 1; pic_param->ref_flags.bits.ref_arf_idx = 2; pic_param->pic_flags.bits.frame_context_idx = 0; memcpy(&pic_param->reference_frames, vp9_ref_list, sizeof(vp9_ref_list)); } } static void vp9enc_create_picture_parameter_buf() { VAStatus va_status; va_status = vaCreateBuffer(va_dpy, vp9enc_context.context_id, VAEncPictureParameterBufferType, sizeof(vp9enc_context.pic_param), 1, &vp9enc_context.pic_param, &vp9enc_context.pic_param_buf_id); CHECK_VASTATUS(va_status,"vaCreateBuffer"); } static void vp9enc_begin_picture(FILE *yuv_fp, int frame_num, int frame_type) { VAStatus va_status; if (vp9enc_context.upload_thread_value != 0) { fprintf(stderr, "FATAL error!!!\n"); exit(1); } pthread_join(vp9enc_context.upload_thread_id, NULL); vp9enc_context.upload_thread_value = -1; /* sequence parameter set */ VAEncSequenceParameterBufferVP9 *seq_param = &vp9enc_context.seq_param; va_status = vaCreateBuffer(va_dpy, vp9enc_context.context_id, VAEncSequenceParameterBufferType, sizeof(*seq_param), 1, seq_param, &vp9enc_context.seq_param_buf_id); CHECK_VASTATUS(va_status,"vaCreateBuffer"); /* hrd parameter */ VAEncMiscParameterBuffer *misc_param; VAEncMiscParameterHRD *misc_hrd_param; vaCreateBuffer(va_dpy, vp9enc_context.context_id, VAEncMiscParameterBufferType, sizeof(VAEncMiscParameterBuffer) + sizeof(VAEncMiscParameterRateControl), 1, NULL, &vp9enc_context.misc_parameter_hrd_buf_id); CHECK_VASTATUS(va_status, "vaCreateBuffer"); vaMapBuffer(va_dpy, vp9enc_context.misc_parameter_hrd_buf_id, (void **)&misc_param); misc_param->type = VAEncMiscParameterTypeHRD; misc_hrd_param = (VAEncMiscParameterHRD *)misc_param->data; if (frame_bit_rate > 0) { misc_hrd_param->initial_buffer_fullness = frame_bit_rate * hrd_window / 2; misc_hrd_param->buffer_size = frame_bit_rate * hrd_window; } else { misc_hrd_param->initial_buffer_fullness = 0; misc_hrd_param->buffer_size = 0; } vaUnmapBuffer(va_dpy, vp9enc_context.misc_parameter_hrd_buf_id); VAEncMiscParameterTypeVP9PerSegmantParam seg_param; memset(&seg_param, 0, sizeof(seg_param)); va_status = vaCreateBuffer(va_dpy, vp9enc_context.context_id, VAQMatrixBufferType, sizeof(seg_param), 1, &seg_param, &vp9enc_context.qmatrix_buf_id); CHECK_VASTATUS(va_status,"vaCreateBuffer"); /* Create the Misc FR/RC buffer under non-CQP mode */ if (rc_mode != VA_RC_CQP && frame_type == KEY_FRAME) { VAEncMiscParameterFrameRate *misc_fr; VAEncMiscParameterRateControl *misc_rc; vaCreateBuffer(va_dpy, vp9enc_context.context_id, VAEncMiscParameterBufferType, sizeof(VAEncMiscParameterBuffer) + sizeof(VAEncMiscParameterFrameRate), 1, NULL, &vp9enc_context.misc_fr_buf_id); CHECK_VASTATUS(va_status, "vaCreateBuffer"); vaMapBuffer(va_dpy, vp9enc_context.misc_fr_buf_id, (void **)&misc_param); misc_param->type = VAEncMiscParameterTypeFrameRate; misc_fr = (VAEncMiscParameterFrameRate *)misc_param->data; misc_fr->framerate = frame_rate; vaUnmapBuffer(va_dpy, vp9enc_context.misc_fr_buf_id); vaCreateBuffer(va_dpy, vp9enc_context.context_id, VAEncMiscParameterBufferType, sizeof(VAEncMiscParameterBuffer) + sizeof(VAEncMiscParameterRateControl), 1, NULL, &vp9enc_context.misc_rc_buf_id); CHECK_VASTATUS(va_status, "vaCreateBuffer"); vaMapBuffer(va_dpy, vp9enc_context.misc_rc_buf_id, (void **)&misc_param); misc_param->type = VAEncMiscParameterTypeRateControl; misc_rc = (VAEncMiscParameterRateControl *)misc_param->data; misc_rc->bits_per_second = frame_bit_rate * 1000; misc_rc->window_size = hrd_window; misc_rc->initial_qp = qp_value; /* The target percentage is only for VBR. It is ignored in CBR */ misc_rc->target_percentage = 95; if (rc_mode == VA_RC_VBR) { misc_rc->bits_per_second = vbr_max * 1000; misc_rc->target_percentage = (frame_bit_rate * 100) / vbr_max; } vaUnmapBuffer(va_dpy, vp9enc_context.misc_rc_buf_id); } } static void vp9enc_render_picture() { VAStatus va_status; VABufferID va_buffers[10]; uint32_t num_va_buffers = 0; va_buffers[num_va_buffers++] = vp9enc_context.seq_param_buf_id; va_buffers[num_va_buffers++] = vp9enc_context.pic_param_buf_id; if (vp9enc_context.misc_parameter_hrd_buf_id != VA_INVALID_ID) va_buffers[num_va_buffers++] = vp9enc_context.misc_parameter_hrd_buf_id; if (vp9enc_context.qmatrix_buf_id != VA_INVALID_ID) va_buffers[num_va_buffers++] = vp9enc_context.qmatrix_buf_id; if (vp9enc_context.mb_seg_buf_id != VA_INVALID_ID) va_buffers[num_va_buffers++] = vp9enc_context.mb_seg_buf_id; if (vp9enc_context.raw_data_header_buf_id != VA_INVALID_ID) va_buffers[num_va_buffers++] = vp9enc_context.raw_data_header_buf_id; if (vp9enc_context.raw_data_buf_id != VA_INVALID_ID) va_buffers[num_va_buffers++] = vp9enc_context.raw_data_buf_id; if (vp9enc_context.misc_fr_buf_id != VA_INVALID_ID) va_buffers[num_va_buffers++] = vp9enc_context.misc_fr_buf_id; if (vp9enc_context.misc_rc_buf_id != VA_INVALID_ID) va_buffers[num_va_buffers++] = vp9enc_context.misc_rc_buf_id; va_status = vaBeginPicture(va_dpy, vp9enc_context.context_id, surface_ids[vp9enc_context.current_input_surface]); CHECK_VASTATUS(va_status,"vaBeginPicture"); va_status = vaRenderPicture(va_dpy, vp9enc_context.context_id, va_buffers, num_va_buffers); CHECK_VASTATUS(va_status,"vaRenderPicture"); va_status = vaEndPicture(va_dpy, vp9enc_context.context_id); CHECK_VASTATUS(va_status,"vaEndPicture"); } static void vp9enc_destroy_buffers(VABufferID *va_buffers, uint32_t num_va_buffers) { VAStatus va_status; uint32_t i; for (i = 0; i < num_va_buffers; i++) { if (va_buffers[i] != VA_INVALID_ID) { va_status = vaDestroyBuffer(va_dpy, va_buffers[i]); CHECK_VASTATUS(va_status,"vaDestroyBuffer"); va_buffers[i] = VA_INVALID_ID; } } } static void vp9enc_write_frame_header(FILE *vp9_output, int frame_size) { char header[12]; vp9enc_write_dword(header, (uint32_t)frame_size); vp9enc_write_dword(header + 4, 0); vp9enc_write_dword(header + 8, 0); fwrite(header, 1, 12, vp9_output); } static int vp9enc_store_coded_buffer(FILE *vp9_fp, int frame_type) { VACodedBufferSegment *coded_buffer_segment; uint8_t *coded_mem; int data_length; VAStatus va_status; VASurfaceStatus surface_status; size_t w_items; va_status = vaSyncSurface(va_dpy, surface_ids[vp9enc_context.current_input_surface]); CHECK_VASTATUS(va_status,"vaSyncSurface"); surface_status = 0; va_status = vaQuerySurfaceStatus(va_dpy, surface_ids[vp9enc_context.current_input_surface], &surface_status); CHECK_VASTATUS(va_status,"vaQuerySurfaceStatus"); va_status = vaMapBuffer(va_dpy, vp9enc_context.codedbuf_buf_id, (void **)(&coded_buffer_segment)); CHECK_VASTATUS(va_status,"vaMapBuffer"); coded_mem = coded_buffer_segment->buf; if (coded_buffer_segment->status & VA_CODED_BUF_STATUS_SLICE_OVERFLOW_MASK) { if (frame_type == KEY_FRAME) vp9enc_context.codedbuf_i_size *= 2; else vp9enc_context.codedbuf_pb_size *= 2; vaUnmapBuffer(va_dpy, vp9enc_context.codedbuf_buf_id); return -1; } data_length = coded_buffer_segment->size; vp9enc_write_frame_header(vp9_fp, data_length); do { w_items = fwrite(coded_mem, data_length, 1, vp9_fp); } while (w_items != 1); vaUnmapBuffer(va_dpy, vp9enc_context.codedbuf_buf_id); return 0; } static void vp9enc_write_ivf_header(FILE *vp9_file, int width, int height, int frame_num, int frame_rate) { #define VP9_FOURCC 0x30395056 char header[32]; header[0] = 'D'; header[1] = 'K'; header[2] = 'I'; header[3] = 'F'; vp9enc_write_word(header + 4, 0); vp9enc_write_word(header + 6, 32); vp9enc_write_dword(header + 8, VP9_FOURCC); vp9enc_write_word(header + 12, width); vp9enc_write_word(header + 14, height); vp9enc_write_dword(header + 16, 1); vp9enc_write_dword(header + 20, frame_rate); vp9enc_write_dword(header + 24, frame_num); vp9enc_write_dword(header + 28, 0); fwrite(header, 1, 32, vp9_file); } static void vp9enc_get_frame_type(int encoding_order, int gop_size, int ip_period, int *frame_type) { if (ip_period == 0 || (encoding_order % gop_size == 0)) *frame_type = KEY_FRAME; else *frame_type = INTER_FRAME; } static void vp9enc_end_picture() { vp9enc_destroy_buffers(&vp9enc_context.seq_param_buf_id, 1); vp9enc_destroy_buffers(&vp9enc_context.pic_param_buf_id, 1); vp9enc_destroy_buffers(&vp9enc_context.codedbuf_buf_id, 1); vp9enc_destroy_buffers(&vp9enc_context.qmatrix_buf_id, 1); vp9enc_destroy_buffers(&vp9enc_context.misc_parameter_hrd_buf_id, 1); vp9enc_destroy_buffers(&vp9enc_context.mb_seg_buf_id, 1); vp9enc_destroy_buffers(&vp9enc_context.raw_data_header_buf_id, 1); vp9enc_destroy_buffers(&vp9enc_context.raw_data_buf_id, 1); vp9enc_destroy_buffers(&vp9enc_context.misc_fr_buf_id, 1); vp9enc_destroy_buffers(&vp9enc_context.misc_rc_buf_id, 1); if (vp9enc_context.current_input_surface == SID_INPUT_PICTURE_0) vp9enc_context.current_input_surface = SID_INPUT_PICTURE_1; else vp9enc_context.current_input_surface = SID_INPUT_PICTURE_0; } static void vp9enc_encode_picture(FILE *yuv_fp, FILE *vp9_fp, int frame_num, int frame_type, int next_enc_frame) { VAStatus va_status; int ret = 0, codedbuf_size; vp9enc_begin_picture(yuv_fp, frame_num, frame_type); if (next_enc_frame < frame_num) { int index; /* prepare for next frame */ if (vp9enc_context.current_input_surface == SID_INPUT_PICTURE_0) index = SID_INPUT_PICTURE_1; else index = SID_INPUT_PICTURE_0; fseeko(yuv_fp, (off_t)frame_size * next_enc_frame, SEEK_SET); vp9enc_context.upload_thread_param.yuv_fp = yuv_fp; vp9enc_context.upload_thread_param.surface_id = surface_ids[index]; vp9enc_context.upload_thread_value = pthread_create(&vp9enc_context.upload_thread_id, NULL, vp9enc_upload_thread_function, (void*)&vp9enc_context.upload_thread_param); } do { vp9enc_destroy_buffers(&vp9enc_context.codedbuf_buf_id, 1); vp9enc_destroy_buffers(&vp9enc_context.pic_param_buf_id, 1); if (frame_type == KEY_FRAME) codedbuf_size = vp9enc_context.codedbuf_i_size; else codedbuf_size = vp9enc_context.codedbuf_pb_size; /* coded buffer */ va_status = vaCreateBuffer(va_dpy, vp9enc_context.context_id, VAEncCodedBufferType, codedbuf_size, 1, NULL, &vp9enc_context.codedbuf_buf_id); CHECK_VASTATUS(va_status,"vaCreateBuffer"); /* picture parameter set */ vp9enc_update_picture_parameter(current_frame_type); if (opt_header) { char raw_data[64]; int raw_data_length; VAEncPackedHeaderParameterBuffer packed_header_param_buffer; memset(raw_data, 0, sizeof(raw_data)); vp9enc_write_uncompressed_header(&vp9enc_context, raw_data, &raw_data_length); packed_header_param_buffer.type = VAEncPackedHeaderRawData; packed_header_param_buffer.bit_length = raw_data_length * 8; packed_header_param_buffer.has_emulation_bytes = 0; vaCreateBuffer(va_dpy, vp9enc_context.context_id, VAEncPackedHeaderParameterBufferType, sizeof(packed_header_param_buffer), 1, &packed_header_param_buffer, &vp9enc_context.raw_data_header_buf_id); vaCreateBuffer(va_dpy, vp9enc_context.context_id, VAEncPackedHeaderDataBufferType, raw_data_length, 1, raw_data, &vp9enc_context.raw_data_buf_id); } vp9enc_create_picture_parameter_buf(); vp9enc_render_picture(); ret = vp9enc_store_coded_buffer(vp9_fp, current_frame_type); } while (ret); vp9enc_update_reference_list(); vp9enc_end_picture(); } static void vp9enc_context_seq_param_init(VAEncSequenceParameterBufferVP9 *seq_param, int width, int height) { seq_param->intra_period = intra_period; seq_param->max_frame_width = width; seq_param->max_frame_height = height; seq_param->kf_auto = 0; seq_param->kf_min_dist = 1; seq_param->kf_max_dist = intra_period; if (frame_bit_rate > 0) seq_param->bits_per_second = 1000 * frame_bit_rate; /* use kbps as input */ else seq_param->bits_per_second = 0; } static void vp9enc_context_pic_param_init(VAEncPictureParameterBufferVP9 *pic_param, int width, int height) { int i, ref_num; memset(pic_param, 0, sizeof(VAEncPictureParameterBufferVP9)); ref_num = sizeof(pic_param->reference_frames) / sizeof(VASurfaceID); for (i = 0; i < ref_num; i++) pic_param->reference_frames[i] = VA_INVALID_ID; pic_param->coded_buf = VA_INVALID_ID; pic_param->reconstructed_frame = VA_INVALID_ID; pic_param->frame_width_src = width; pic_param->frame_height_src = height; pic_param->frame_width_dst = width; pic_param->frame_height_dst = height; pic_param->ref_flags.bits.force_kf = 0; /* always show it */ pic_param->pic_flags.bits.show_frame = 1; /* Not use silience mode */ pic_param->pic_flags.bits.error_resilient_mode = 0; /* Not use the frame parallel mode */ pic_param->pic_flags.bits.frame_parallel_decoding_mode = 0; pic_param->luma_ac_qindex = qp_value; pic_param->luma_dc_qindex_delta = 1; pic_param->chroma_ac_qindex_delta = 1; pic_param->chroma_dc_qindex_delta = 1; /* use the zero sharpness_level/lf_ref&mode deltas */ pic_param->filter_level = lf_level; for (i = 0; i < 4; i++) pic_param->ref_lf_delta[i] = 1; for (i = 0; i < 2; i++) pic_param->mode_lf_delta[i] = 1; } static void vp9enc_context_init(int width, int height) { memset(&vp9enc_context, 0, sizeof(vp9enc_context)); vp9enc_context.profile = VAProfileVP9Profile0; memset(&use_slot, 0, sizeof(use_slot)); vp9enc_context.seq_param_buf_id = VA_INVALID_ID; vp9enc_context.pic_param_buf_id = VA_INVALID_ID; vp9enc_context.mb_seg_buf_id = VA_INVALID_ID; vp9enc_context.misc_parameter_hrd_buf_id = VA_INVALID_ID; vp9enc_context.qmatrix_buf_id = VA_INVALID_ID; vp9enc_context.codedbuf_buf_id = VA_INVALID_ID; vp9enc_context.raw_data_header_buf_id = VA_INVALID_ID; vp9enc_context.raw_data_buf_id = VA_INVALID_ID; vp9enc_context.misc_fr_buf_id = VA_INVALID_ID; vp9enc_context.misc_rc_buf_id = VA_INVALID_ID; vp9enc_context.codedbuf_i_size = width * height; vp9enc_context.codedbuf_pb_size = width * height; vp9enc_context.current_input_surface = SID_INPUT_PICTURE_0; vp9enc_context.upload_thread_value = -1; vp9enc_context.rate_control_method = rc_mode; vp9enc_context_seq_param_init(&vp9enc_context.seq_param, 8192, 8192); vp9enc_context_pic_param_init(&vp9enc_context.pic_param, width, height); } static void vp9enc_show_help() { printf("Usage: vp9encode additional_option\n"); printf("output_vp9 should use *.ivf\n"); printf("The additional option is listed\n"); printf("-f \n"); printf("--intra_period \n"); printf("--qp \n"); printf("--rcmode 0: CQP, 1: CBR, 2: VBR\n"); printf("--fb (kbps unit)\n"); printf("--lf_level [0-63]\n"); printf("--hrd_win [1000-8000]\n"); printf("--vbr_max (kbps unit. It should be greater than fb)\n"); printf("--opt_header \n write the uncompressed header manually. without this, the driver will add those headers by itself\n"); printf("--fn_num \n how many frames to be encoded\n"); printf("--low_power 0: Normal mode, 1: Low power mode, others: auto mode\n"); } int main(int argc, char *argv[]) { char *yuv_input, *vp9_output, *tmp_vp9; FILE *yuv_fp; FILE *vp9_fp; off_t file_size; struct timeval tpstart,tpend; float timeuse; int ip_period; int fn_num; int frame_idx; va_init_display_args(&argc, argv); //TODO may be we should using option analytics library if(argc < 5) { vp9enc_show_help(); exit(0); } picture_width = atoi(argv[1]); picture_height = atoi(argv[2]); yuv_input = argv[3]; vp9_output = argv[4]; fn_num = -1; if (!yuv_input || !vp9_output) { vp9enc_show_help(); exit(0); } tmp_vp9 = strdup(argv[4]); assert(tmp_vp9); if (!strstr(tmp_vp9, ".ivf")) { free(tmp_vp9); vp9enc_show_help(); exit(0); } free(tmp_vp9); rc_mode = VA_RC_CQP; frame_rate = 30; qp_value = 60; intra_period = 30; frame_bit_rate = -1; vbr_max = -1; if (argc > 5) { int c, long_index, tmp_input; while (1) { c = getopt_long_only(argc - 4,argv + 4,"hf:?",long_opts,&long_index); if (c == -1) break; switch(c) { case 'h': case 0: vp9enc_show_help(); exit(0); break; case 'f': frame_rate = atoi(optarg); break; case 1: tmp_input = atoi(optarg); if (tmp_input > 3 || tmp_input < 0) tmp_input = 0; rc_mode = rc_default_mode[tmp_input]; break; case 2: tmp_input = atoi(optarg); if (tmp_input < 0 || tmp_input > 255) tmp_input = 60; qp_value = tmp_input; break; case 3: tmp_input = atoi(optarg); if (tmp_input < 0) tmp_input = 30; intra_period = tmp_input; break; case 4: tmp_input = atoi(optarg); frame_bit_rate = tmp_input; break; case 6: tmp_input = atoi(optarg); if (tmp_input < 0 || tmp_input > 63) tmp_input = 10; lf_level = tmp_input; break; case 7: tmp_input = atoi(optarg); if (tmp_input) tmp_input = 1; opt_header = tmp_input; break; case 8: tmp_input = atoi(optarg); if (tmp_input > 8000 || tmp_input < 1000) tmp_input = 1500; hrd_window = tmp_input; break; case 9: tmp_input = atoi(optarg); if (tmp_input < 0) tmp_input = 20000; else if (tmp_input > 100000) tmp_input = 100000; vbr_max = tmp_input; break; case 10: tmp_input = atoi(optarg); fn_num = tmp_input; break; case 11: tmp_input = atoi(optarg); if (tmp_input == 0 || tmp_input == 1) select_entrypoint = tmp_input; else select_entrypoint = -1; break; default: vp9enc_show_help(); exit(0); break; } } } if (rc_mode != VA_RC_CQP && (frame_bit_rate < 0)) { printf("Please specifiy the bit rate for CBR/VBR\n"); vp9enc_show_help(); exit(0); } if (rc_mode == VA_RC_VBR) { if (vbr_max < 0) { vbr_max = frame_bit_rate; frame_bit_rate = (vbr_max * 95 / 100); } if (vbr_max < frame_bit_rate) { printf("Under VBR, the max bit rate should be greater than or equal to fb\n"); vp9enc_show_help(); exit(0); } if (vbr_max > (frame_bit_rate * 2)) { printf("under VBR, the max bit rate is too much greater than the average bit\n"); vp9enc_show_help(); exit(0); } } yuv_fp = fopen(yuv_input,"rb"); if ( yuv_fp == NULL){ printf("Can't open input YUV file\n"); return -1; } fseeko(yuv_fp, (off_t)0, SEEK_END); file_size = ftello(yuv_fp); frame_size = picture_width * picture_height + ((picture_width * picture_height) >> 1) ; if ( (file_size < frame_size) || (file_size % frame_size) ) { fclose(yuv_fp); printf("The YUV file's size is not correct\n"); return -1; } frame_number = file_size / frame_size; fseeko(yuv_fp, (off_t)0, SEEK_SET); if (fn_num > 0 && fn_num <= frame_number) frame_number = fn_num; vp9_fp = fopen(vp9_output, "wb"); if ( vp9_fp == NULL) { fclose(yuv_fp); printf("Can't open output avc file\n"); return -1; } if (intra_period == 0) intra_period = frame_number; ip_period = 1; if (intra_period == 1) ip_period = 0; gettimeofday(&tpstart,NULL); vp9enc_write_ivf_header(vp9_fp, picture_width, picture_height, frame_number, frame_rate); vp9enc_context_init(picture_width, picture_height); vp9enc_create_encode_pipe(yuv_fp); for (frame_idx = 0; frame_idx < frame_number; frame_idx++) { vp9enc_get_frame_type(frame_idx, intra_period, ip_period, ¤t_frame_type); vp9enc_encode_picture(yuv_fp, vp9_fp, frame_number, current_frame_type, frame_idx + 1); printf("\r %d/%d ...", (frame_idx + 1), frame_number); fflush(stdout); } gettimeofday(&tpend,NULL); timeuse = 1000000 * (tpend.tv_sec-tpstart.tv_sec) + tpend.tv_usec-tpstart.tv_usec; timeuse /= 1000000; printf("\ndone!\n"); printf("encode %d frames in %f secondes, FPS is %.1f\n",frame_number, timeuse, frame_number/timeuse); vp9enc_release_encode_resource(); vp9enc_destory_encode_pipe(); fclose(yuv_fp); fclose(vp9_fp); return 0; }