| /* |
| * 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 <stdbool.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <getopt.h> |
| #include <unistd.h> |
| |
| #include <sys/time.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <assert.h> |
| #include <time.h> |
| #include <stdlib.h> |
| #include <pthread.h> |
| |
| #include <va/va.h> |
| #include <va/va_enc_vp9.h> |
| #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 <width> <height> <input_yuvfile> <output_vp9> additional_option\n"); |
| printf("output_vp9 should use *.ivf\n"); |
| printf("The additional option is listed\n"); |
| printf("-f <frame rate> \n"); |
| printf("--intra_period <key_frame interval>\n"); |
| printf("--qp <quantization parameter> \n"); |
| printf("--rcmode <rate control mode> 0: CQP, 1: CBR, 2: VBR\n"); |
| printf("--fb <bitrate> (kbps unit)\n"); |
| printf("--lf_level <loop filter level> [0-63]\n"); |
| printf("--hrd_win <num> [1000-8000]\n"); |
| printf("--vbr_max <num> (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 <num>\n how many frames to be encoded\n"); |
| printf("--low_power <num> 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; |
| } |