From: Xiang, Haihao Date: Tue, 6 Sep 2016 13:25:29 +0000 (+0800) Subject: A simple encoder for H.264/AVC SVC temporal scalability X-Git-Tag: android-x86-7.1-r1~59^2~4 X-Git-Url: http://git.osdn.net/view?p=android-x86%2Fhardware-intel-common-libva.git;a=commitdiff_plain;h=909c9afd687e8bb62a31c730a6632f75011d4f0f A simple encoder for H.264/AVC SVC temporal scalability It is based on VA-API Signed-off-by: Xiang, Haihao Reviewed-by: Sean V Kelley (cherry picked from commit 7aa2dd979a3758e1f6378344b235595c5028a9e7) --- diff --git a/test/encode/Makefile.am b/test/encode/Makefile.am index d2ac265..9a47c2c 100644 --- a/test/encode/Makefile.am +++ b/test/encode/Makefile.am @@ -21,6 +21,7 @@ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. bin_PROGRAMS = avcenc mpeg2vaenc h264encode jpegenc +noinst_PROGRAMS = svctenc AM_CPPFLAGS = \ -Wall \ @@ -54,10 +55,17 @@ jpegenc_CFLAGS = -I$(top_srcdir)/test/common -g jpegenc_LDADD = \ $(top_builddir)/va/libva.la \ $(top_builddir)/test/common/libva-display.la \ - -lpthread - -valgrind: $(bin_PROGRAMS) - for a in $(bin_PROGRAMS); do \ + -lpthread + +svctenc_SOURCES = svctenc.c +svctenc_CFLAGS = -I$(top_srcdir)/test/common -g +svctenc_LDADD = \ + $(top_builddir)/va/libva.la \ + $(top_builddir)/test/common/libva-display.la \ + -lpthread -lm + +valgrind: $(bin_PROGRAMS) $(noinst_PROGRAMS) + for a in $(bin_PROGRAMS) $(noinst_PROGRAMS); do \ valgrind --leak-check=full --show-reachable=yes .libs/$$a; \ done diff --git a/test/encode/svctenc.c b/test/encode/svctenc.c new file mode 100644 index 0000000..f7406c5 --- /dev/null +++ b/test/encode/svctenc.c @@ -0,0 +1,3104 @@ +/* + * Copyright (c) 2016 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. + */ + +/* + * Simple H.264/AVC temporal scalability encoder based on libVA. + * + */ + +#include "sysdeps.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "va_display.h" + +#define SLICE_TYPE_P 0 +#define SLICE_TYPE_B 1 +#define SLICE_TYPE_I 2 + +#define IS_I_SLICE(type) (SLICE_TYPE_I == (type) || SLICE_TYPE_I == (type - 5)) +#define IS_P_SLICE(type) (SLICE_TYPE_P == (type) || SLICE_TYPE_P == (type - 5)) +#define IS_B_SLICE(type) (SLICE_TYPE_B == (type) || SLICE_TYPE_B == (type - 5)) + +#define NAL_REF_IDC_NONE 0 +#define NAL_REF_IDC_LOW 1 +#define NAL_REF_IDC_MEDIUM 2 +#define NAL_REF_IDC_HIGH 3 + +#define NAL_NON_IDR 1 +#define NAL_IDR 5 +#define NAL_SEI 6 +#define NAL_SPS 7 +#define NAL_PPS 8 +#define NAL_PREFIX 14 +#define NAL_SUBSET_SPS 15 + +#define ENTROPY_MODE_CAVLC 0 +#define ENTROPY_MODE_CABAC 1 + +#define PROFILE_IDC_BASELINE 66 +#define PROFILE_IDC_MAIN 77 +#define PROFILE_IDC_SCALABLE_BASELINE 83 +#define PROFILE_IDC_SCALABLE_HIGH 86 +#define PROFILE_IDC_HIGH 100 + +#define SRC_SURFACE_IN_ENCODING 0 +#define SRC_SURFACE_IN_STORAGE 1 + +#define NUM_SURFACES 32 + +#define ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0])) + +#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); \ + } + +#define MAX_SLICES 32 +#define MAX_LAYERS 4 + +#define MIN(a, b) ((a) > (b) ? (b) : (a)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +static VASurfaceID src_surfaces[NUM_SURFACES]; +static VASurfaceID rec_surfaces[NUM_SURFACES]; +static int src_surface_status[NUM_SURFACES]; + +static int temporal_ids_in_bgop[16] = { // index is (encoding order) % gop_size - 1, available from the 2nd encoded frame + 0, /* temporal 0 */ + 1, /* temporal 1 */ + 2, 2, /* temporal 2 */ + 3, 3, 3, 3, /* temporal 3 */ + 4, 4, 4, 4, 4, 4, 4, 4 /* temporal 4 */ +}; + +static int temporal_ids_in_pgop[16] = { // index is (encoding order) % gop_size - 1, available from the 2nd encoded frame + 1, 2, 1, 3, // each element is (the number of temporal layers - temporal id) + 1, 2, 1, 3, + 1, 2, 1, 3, + 1, 2, 1, 3, +}; + +static int gop_factors_in_bgop[16] = { + 1, + 1, + 1, 3, + 1, 3, 5, 7, + 1, 3, 5, 7, 9, 11, 13, 15 +}; + +static float frame_rates[4] = { + 7.5, + 15, + 30, + 60, +}; + +static VAProfile g_va_profiles[] = { + VAProfileH264High, + VAProfileH264Baseline, + VAProfileH264ConstrainedBaseline, +}; + +typedef struct _svcenc_surface +{ + int slot_in_surfaces; /* index in src_surfaces and rec_surfaces */ + int coding_order; + int display_order; + int temporal_id; + int frame_num; + int poc; + unsigned int is_intra : 1; + unsigned int is_idr : 1; + unsigned int is_ref : 1; + VAEncPictureType picture_type; + VASurfaceID rec_surface; +} svcenc_surface; + +static svcenc_surface ref_frames[16], ref_list0[32], ref_list1[32]; + +static pthread_mutex_t upload_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t upload_cond = PTHREAD_COND_INITIALIZER; + +struct upload_task_t { + void *next; + unsigned int display_order; + unsigned int surface; +}; + +struct svcenc_context +{ + /* parameter info */ + FILE *ifp; /* a FILE pointer for source YUV file */ + FILE *ofp; /* a FILE pointer for output SVC file */ + int width; + int height; + int frame_size; + int num_pictures; + int num_slices; + int qp; /* quantisation parameter, default value is 26 */ + unsigned char *frame_data_buffer; /* buffer for input surface, the length is the maximum frame_size */ + int gop_size; + int max_num_ref_frames; + int num_ref_frames; + int hierarchical_levels; + int layer_brc; + + /* the info for next picture in encoding order */ + svcenc_surface next_svcenc_surface; + + /* GOP info */ + int intra_idr_period; + int intra_period; + int ip_period; + int num_remainder_bframes; + int gop_type; /* 0: p hierarchical, 1: B hierarchical, default is 0 */ + + /* bitrate info */ + int rate_control_mode; + int bits_per_kbps; + int framerate_per_100s; + int i_initial_cpb_removal_delay; + int i_initial_cpb_removal_delay_offset; + int i_initial_cpb_removal_delay_length; + int i_cpb_removal_delay; + int i_cpb_removal_delay_length; + int i_dpb_output_delay_length; + int time_offset_length; + + unsigned long long idr_frame_num; + unsigned long long prev_idr_cpb_removal; + unsigned long long current_idr_cpb_removal; + unsigned long long current_cpb_removal; + + /* This is relative to the current_cpb_removal */ + unsigned int current_dpb_removal_delta; + + int profile_idc; + int constraint_set_flag; + + int svc_profile_idc; + int svc_constraint_set_flag; + + /* reordering info for l0/l1, + * bit0-3: ref_pic_list_modification_flag_lX (X=0,1), + * bit4-7: modification_of_pic_nums_idc, + * bit8-15: abs_diff_pic_num_minus1 + * bit16-23: num_ref_idx_active_override_flag + * bit24-31: num_ref_idx_lX_active_minus1 (X=0,1), + */ + unsigned int reordering_info[2]; + + /* VA info */ + VADisplay va_dpy; + VAProfile profile; + VAEncSequenceParameterBufferH264 seq_param; + VAEncPictureParameterBufferH264 pic_param; + VAEncSliceParameterBufferH264 slice_param[MAX_SLICES]; + VAContextID context_id; + VAConfigID config_id; + VABufferID seq_param_buf_id; /* Sequence level parameter */ + VABufferID pic_param_buf_id; /* Picture level parameter */ + VABufferID slice_param_buf_id[MAX_SLICES]; /* Slice level parameter, multil slices */ + VABufferID codedbuf_buf_id; /* Output buffer, compressed data */ + VABufferID packed_sei_scalability_info_header_param_buf_id; + VABufferID packed_sei_scalability_info_buf_id; + VABufferID packed_seq_header_param_buf_id; + VABufferID packed_seq_buf_id; + VABufferID packed_svc_seq_header_param_buf_id; + VABufferID packed_svc_seq_buf_id; + VABufferID packed_pic_header_param_buf_id; + VABufferID packed_pic_buf_id; + VABufferID packed_sei_header_param_buf_id; /* the SEI buffer */ + VABufferID packed_sei_buf_id; + VABufferID misc_parameter_layer_structure_buf_id; + VABufferID misc_parameter_ratecontrol_buf_id[MAX_LAYERS]; + VABufferID misc_parameter_framerate_buf_id[MAX_LAYERS]; + VABufferID misc_parameter_hrd_buf_id; + VABufferID packed_slice_header_param_buf_id[MAX_SLICES]; + VABufferID packed_slice_header_data_buf_id[MAX_SLICES]; + VABufferID packed_prefix_nal_unit_param_buf_id[MAX_SLICES]; + VABufferID packed_prefix_nal_unit_data_buf_id[MAX_SLICES]; + + /* thread info */ + pthread_t upload_thread; + struct upload_task_t *upload_task_header; + struct upload_task_t *upload_task_tail; +}; + +/* bitstream */ +#define BITSTREAM_ALLOCATE_STEPPING 4096 + +struct __bitstream { + unsigned int *buffer; + int bit_offset; + int max_size_in_dword; +}; + +typedef struct __bitstream bitstream; + +static unsigned int +va_swap32(unsigned int val) +{ + unsigned char *pval = (unsigned char *)&val; + + return ((pval[0] << 24) | + (pval[1] << 16) | + (pval[2] << 8) | + (pval[3] << 0)); +} + +static void +bitstream_start(bitstream *bs) +{ + bs->max_size_in_dword = BITSTREAM_ALLOCATE_STEPPING; + bs->buffer = calloc(bs->max_size_in_dword * sizeof(int), 1); + bs->bit_offset = 0; +} + +static void +bitstream_end(bitstream *bs) +{ + int pos = (bs->bit_offset >> 5); + int bit_offset = (bs->bit_offset & 0x1f); + int bit_left = 32 - bit_offset; + + if (bit_offset) { + bs->buffer[pos] = va_swap32((bs->buffer[pos] << bit_left)); + } +} + +static void +bitstream_put_ui(bitstream *bs, unsigned int val, int size_in_bits) +{ + int pos = (bs->bit_offset >> 5); + int bit_offset = (bs->bit_offset & 0x1f); + int bit_left = 32 - bit_offset; + + if (!size_in_bits) + return; + + bs->bit_offset += size_in_bits; + + if (bit_left > size_in_bits) { + bs->buffer[pos] = (bs->buffer[pos] << size_in_bits | val); + } else { + size_in_bits -= bit_left; + bs->buffer[pos] = (bs->buffer[pos] << bit_left) | (val >> size_in_bits); + bs->buffer[pos] = va_swap32(bs->buffer[pos]); + + if (pos + 1 == bs->max_size_in_dword) { + bs->max_size_in_dword += BITSTREAM_ALLOCATE_STEPPING; + bs->buffer = realloc(bs->buffer, bs->max_size_in_dword * sizeof(unsigned int)); + } + + bs->buffer[pos + 1] = val; + } +} + +static void +bitstream_put_ue(bitstream *bs, unsigned int val) +{ + int size_in_bits = 0; + int tmp_val = ++val; + + while (tmp_val) { + tmp_val >>= 1; + size_in_bits++; + } + + bitstream_put_ui(bs, 0, size_in_bits - 1); // leading zero + bitstream_put_ui(bs, val, size_in_bits); +} + +static void +bitstream_put_se(bitstream *bs, int val) +{ + unsigned int new_val; + + if (val <= 0) + new_val = -2 * val; + else + new_val = 2 * val - 1; + + bitstream_put_ue(bs, new_val); +} + +static void +bitstream_byte_aligning(bitstream *bs, int bit) +{ + int bit_offset = (bs->bit_offset & 0x7); + int bit_left = 8 - bit_offset; + int new_val; + + if (!bit_offset) + return; + + assert(bit == 0 || bit == 1); + + if (bit) + new_val = (1 << bit_left) - 1; + else + new_val = 0; + + bitstream_put_ui(bs, new_val, bit_left); +} + +static void +rbsp_trailing_bits(bitstream *bs) +{ + bitstream_put_ui(bs, 1, 1); + bitstream_byte_aligning(bs, 0); +} + +static void +nal_start_code_prefix(bitstream *bs) +{ + bitstream_put_ui(bs, 0x00000001, 32); +} + +static void +nal_header(bitstream *bs, int nal_ref_idc, int nal_unit_type) +{ + bitstream_put_ui(bs, 0, 1); /* forbidden_zero_bit: 0 */ + bitstream_put_ui(bs, nal_ref_idc, 2); + bitstream_put_ui(bs, nal_unit_type, 5); +} + +static void +sps_data(struct svcenc_context *ctx, + const VAEncSequenceParameterBufferH264 *seq_param, + bitstream *bs) +{ + bitstream_put_ui(bs, ctx->profile_idc, 8); /* profile_idc */ + bitstream_put_ui(bs, !!(ctx->constraint_set_flag & 1), 1); /* constraint_set0_flag */ + bitstream_put_ui(bs, !!(ctx->constraint_set_flag & 2), 1); /* constraint_set1_flag */ + bitstream_put_ui(bs, !!(ctx->constraint_set_flag & 4), 1); /* constraint_set2_flag */ + bitstream_put_ui(bs, !!(ctx->constraint_set_flag & 8), 1); /* constraint_set3_flag */ + bitstream_put_ui(bs, !!(ctx->constraint_set_flag & 16), 1); /* constraint_set4_flag */ + bitstream_put_ui(bs, !!(ctx->constraint_set_flag & 32), 1); /* constraint_set5_flag */ + bitstream_put_ui(bs, 0, 2); /* reserved_zero_2bits */ + bitstream_put_ui(bs, seq_param->level_idc, 8); /* level_idc */ + bitstream_put_ue(bs, seq_param->seq_parameter_set_id); /* seq_parameter_set_id */ + + if (ctx->profile_idc == PROFILE_IDC_HIGH || + ctx->profile_idc == PROFILE_IDC_SCALABLE_HIGH || + ctx->profile_idc == PROFILE_IDC_SCALABLE_BASELINE) { + bitstream_put_ue(bs, 1); /* chroma_format_idc = 1, 4:2:0 */ + bitstream_put_ue(bs, 0); /* bit_depth_luma_minus8 */ + bitstream_put_ue(bs, 0); /* bit_depth_chroma_minus8 */ + bitstream_put_ui(bs, 0, 1); /* qpprime_y_zero_transform_bypass_flag */ + bitstream_put_ui(bs, 0, 1); /* seq_scaling_matrix_present_flag */ + } + + bitstream_put_ue(bs, seq_param->seq_fields.bits.log2_max_frame_num_minus4); /* log2_max_frame_num_minus4 */ + bitstream_put_ue(bs, seq_param->seq_fields.bits.pic_order_cnt_type); /* pic_order_cnt_type */ + + if (seq_param->seq_fields.bits.pic_order_cnt_type == 0) + bitstream_put_ue(bs, seq_param->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4); /* log2_max_pic_order_cnt_lsb_minus4 */ + else { + assert(0); + } + + bitstream_put_ue(bs, seq_param->max_num_ref_frames); /* num_ref_frames */ + bitstream_put_ui(bs, 0, 1); /* gaps_in_frame_num_value_allowed_flag */ + + bitstream_put_ue(bs, seq_param->picture_width_in_mbs - 1); /* pic_width_in_mbs_minus1 */ + bitstream_put_ue(bs, seq_param->picture_height_in_mbs - 1); /* pic_height_in_map_units_minus1 */ + bitstream_put_ui(bs, seq_param->seq_fields.bits.frame_mbs_only_flag, 1); /* frame_mbs_only_flag */ + + if (!seq_param->seq_fields.bits.frame_mbs_only_flag) { + assert(0); + } + + bitstream_put_ui(bs, seq_param->seq_fields.bits.direct_8x8_inference_flag, 1); /* direct_8x8_inference_flag */ + bitstream_put_ui(bs, seq_param->frame_cropping_flag, 1); /* frame_cropping_flag */ + + if (seq_param->frame_cropping_flag) { + bitstream_put_ue(bs, seq_param->frame_crop_left_offset); /* frame_crop_left_offset */ + bitstream_put_ue(bs, seq_param->frame_crop_right_offset); /* frame_crop_right_offset */ + bitstream_put_ue(bs, seq_param->frame_crop_top_offset); /* frame_crop_top_offset */ + bitstream_put_ue(bs, seq_param->frame_crop_bottom_offset); /* frame_crop_bottom_offset */ + } + + if (ctx->bits_per_kbps < 0) { + bitstream_put_ui(bs, 0, 1); /* vui_parameters_present_flag */ + } else { + bitstream_put_ui(bs, 1, 1); /* vui_parameters_present_flag */ + bitstream_put_ui(bs, 0, 1); /* aspect_ratio_info_present_flag */ + bitstream_put_ui(bs, 0, 1); /* overscan_info_present_flag */ + bitstream_put_ui(bs, 0, 1); /* video_signal_type_present_flag */ + bitstream_put_ui(bs, 0, 1); /* chroma_loc_info_present_flag */ + bitstream_put_ui(bs, 1, 1); /* timing_info_present_flag */ + { + bitstream_put_ui(bs, seq_param->num_units_in_tick, 32); + bitstream_put_ui(bs, seq_param->time_scale, 32); + bitstream_put_ui(bs, 1, 1); + } + bitstream_put_ui(bs, 1, 1); /* nal_hrd_parameters_present_flag */ + { + // hrd_parameters + bitstream_put_ue(bs, 0); /* cpb_cnt_minus1 */ + bitstream_put_ui(bs, 0, 4); /* bit_rate_scale */ + bitstream_put_ui(bs, 2, 4); /* cpb_size_scale */ + + /* the bits_per_kbps is in kbps */ + bitstream_put_ue(bs, (((ctx->bits_per_kbps * 1024) >> 6) - 1)); /* bit_rate_value_minus1[0] */ + bitstream_put_ue(bs, ((ctx->bits_per_kbps * 8 * 1024) >> 6) - 1); /* cpb_size_value_minus1[0] */ + bitstream_put_ui(bs, 1, 1); /* cbr_flag[0] */ + + /* initial_cpb_removal_delay_length_minus1 */ + bitstream_put_ui(bs, (ctx->i_initial_cpb_removal_delay_length - 1), 5); + /* cpb_removal_delay_length_minus1 */ + bitstream_put_ui(bs, (ctx->i_cpb_removal_delay_length - 1), 5); + /* dpb_output_delay_length_minus1 */ + bitstream_put_ui(bs, (ctx->i_dpb_output_delay_length - 1), 5); + /* time_offset_length */ + bitstream_put_ui(bs, (ctx->time_offset_length - 1), 5); + } + + bitstream_put_ui(bs, 0, 1); /* vcl_hrd_parameters_present_flag */ + bitstream_put_ui(bs, 0, 1); /* low_delay_hrd_flag */ + + bitstream_put_ui(bs, 0, 1); /* pic_struct_present_flag */ + bitstream_put_ui(bs, 0, 1); /* bitstream_restriction_flag */ + } +} + +static void +sps_svc_extension(struct svcenc_context *ctx, + const VAEncSequenceParameterBufferH264 *sps_param, + bitstream *bs) +{ + bitstream_put_ui(bs, 0, 1); /* inter_layer_deblocking_filter_control_present_flag */ + bitstream_put_ui(bs, 0, 2); /* extended_spatial_scalability_idc */ + + /* if (ChromaArrayType == 1) */ + bitstream_put_ui(bs, 0, 1); /* chroma_phase_x_plus1_flag */ + bitstream_put_ui(bs, 1, 2); /* chroma_phase_y_plus1 */ + +#if 0 + if (extended_spatial_scalability_idc == 1) { + /* if (ChromaArrayType > 0) */ + bitstream_put_ui(bs, 0, 1); /* seq_ref_layer_chroma_phase_x_plus1_flag */ + bitstream_put_ui(bs, 0, 2); /* seq_ref_layer_chroma_phase_y_plus1 */ + + bitstream_put_se(bs, 0); /* seq_scaled_ref_layer_left_offset */ + bitstream_put_se(bs, 0); /* seq_scaled_ref_layer_top_offset */ + bitstream_put_se(bs, 0); /* seq_scaled_ref_layer_right_offset */ + bitstream_put_se(bs, 0); /* seq_scaled_ref_layer_bottom_offset */ + } +#endif + + bitstream_put_ui(bs, 0, 1); /* seq_tcoeff_level_prediction_flag */ + +#if 0 + if (seq_tcoeff_level_prediction_flag) + bitstream_put_ui(bs, 0, 1); /* adaptive_tcoeff_level_prediction_flag */ +#endif + + bitstream_put_ui(bs, 0, 1); /* slice_header_restriction_flag */ +} + +static void +sps_svc_vui_parameters_extension(struct svcenc_context *ctx, + const VAEncSequenceParameterBufferH264 *sps_param, + bitstream *bs) +{ + bitstream_put_ui(bs, 0, 1); /* svc_vui_parameters_present_flag */ +} + +static void +sps_additional_extension2(struct svcenc_context *ctx, + const VAEncSequenceParameterBufferH264 *sps_param, + bitstream *bs) +{ + bitstream_put_ui(bs, 0, 1); /* additional_extension2_flag */ +} + +static void +pps_rbsp(struct svcenc_context *ctx, + const VAEncPictureParameterBufferH264 *pic_param, + bitstream *bs) +{ + bitstream_put_ue(bs, pic_param->pic_parameter_set_id); /* pic_parameter_set_id */ + bitstream_put_ue(bs, pic_param->seq_parameter_set_id); /* seq_parameter_set_id */ + + bitstream_put_ui(bs, pic_param->pic_fields.bits.entropy_coding_mode_flag, 1); /* entropy_coding_mode_flag */ + + bitstream_put_ui(bs, 0, 1); /* pic_order_present_flag: 0 */ + + bitstream_put_ue(bs, 0); /* num_slice_groups_minus1 */ + + bitstream_put_ue(bs, pic_param->num_ref_idx_l0_active_minus1); /* num_ref_idx_l0_active_minus1 */ + bitstream_put_ue(bs, pic_param->num_ref_idx_l1_active_minus1); /* num_ref_idx_l1_active_minus1 1 */ + + bitstream_put_ui(bs, pic_param->pic_fields.bits.weighted_pred_flag, 1); /* weighted_pred_flag: 0 */ + bitstream_put_ui(bs, pic_param->pic_fields.bits.weighted_bipred_idc, 2); /* weighted_bipred_idc: 0 */ + + bitstream_put_se(bs, pic_param->pic_init_qp - 26); /* pic_init_qp_minus26 */ + bitstream_put_se(bs, 0); /* pic_init_qs_minus26 */ + bitstream_put_se(bs, 0); /* chroma_qp_index_offset */ + + bitstream_put_ui(bs, pic_param->pic_fields.bits.deblocking_filter_control_present_flag, 1); /* deblocking_filter_control_present_flag */ + bitstream_put_ui(bs, 0, 1); /* constrained_intra_pred_flag */ + bitstream_put_ui(bs, 0, 1); /* redundant_pic_cnt_present_flag */ + + /* more_rbsp_data */ + bitstream_put_ui(bs, pic_param->pic_fields.bits.transform_8x8_mode_flag, 1); /*transform_8x8_mode_flag */ + bitstream_put_ui(bs, 0, 1); /* pic_scaling_matrix_present_flag */ + bitstream_put_se(bs, pic_param->second_chroma_qp_index_offset ); /*second_chroma_qp_index_offset */ + + rbsp_trailing_bits(bs); +} + +static int +build_packed_seq_buffer(struct svcenc_context *ctx, + const VAEncSequenceParameterBufferH264 *seq_param, + unsigned char **header_buffer) +{ + bitstream bs; + + bitstream_start(&bs); + + nal_start_code_prefix(&bs); + nal_header(&bs, NAL_REF_IDC_HIGH, NAL_SPS); + + sps_data(ctx, seq_param, &bs); + rbsp_trailing_bits(&bs); /* rbsp_trailing_bits */ + + bitstream_end(&bs); + + *header_buffer = (unsigned char *)bs.buffer; + return bs.bit_offset; +} + +static int +build_packed_subset_seq_buffer(struct svcenc_context *ctx, + const VAEncSequenceParameterBufferH264 *seq_param, + unsigned char **header_buffer) +{ + bitstream bs; + + bitstream_start(&bs); + + nal_start_code_prefix(&bs); + nal_header(&bs, NAL_REF_IDC_HIGH, NAL_SUBSET_SPS); + + sps_data(ctx, seq_param, &bs); + sps_svc_extension(ctx, seq_param, &bs); + sps_svc_vui_parameters_extension(ctx, seq_param, &bs); + sps_additional_extension2(ctx, seq_param, &bs); + rbsp_trailing_bits(&bs); /* rbsp_trailing_bits */ + + bitstream_end(&bs); + + *header_buffer = (unsigned char *)bs.buffer; + return bs.bit_offset; +} + +static int +build_packed_pic_buffer(struct svcenc_context *ctx, + const VAEncPictureParameterBufferH264 *pic_param, + unsigned char **header_buffer) +{ + bitstream bs; + + bitstream_start(&bs); + nal_start_code_prefix(&bs); + nal_header(&bs, NAL_REF_IDC_HIGH, NAL_PPS); + pps_rbsp(ctx, pic_param, &bs); + bitstream_end(&bs); + + *header_buffer = (unsigned char *)bs.buffer; + return bs.bit_offset; +} + +static int +build_packed_sei_buffering_period_buffer(struct svcenc_context *ctx, + const VAEncSequenceParameterBufferH264 *seq_param, + int frame_num, + unsigned char **sei_buffer) +{ + bitstream sei_bp_bs; + + if (ctx->rate_control_mode & VA_RC_CQP || + frame_num) { + *sei_buffer = NULL; + return 0; + } + + bitstream_start(&sei_bp_bs); + bitstream_put_ue(&sei_bp_bs, seq_param->seq_parameter_set_id); /* seq_parameter_set_id */ + /* SEI buffer period info */ + /* NALHrdBpPresentFlag == 1 */ + bitstream_put_ui(&sei_bp_bs, ctx->i_initial_cpb_removal_delay, ctx->i_initial_cpb_removal_delay_length); + bitstream_put_ui(&sei_bp_bs, ctx->i_initial_cpb_removal_delay_offset, ctx->i_initial_cpb_removal_delay_length); + + if (sei_bp_bs.bit_offset & 0x7) { + bitstream_put_ui(&sei_bp_bs, 1, 1); + } + + bitstream_end(&sei_bp_bs); + + *sei_buffer = (unsigned char *)sei_bp_bs.buffer; + + return (sei_bp_bs.bit_offset + 7) / 8; +} + +static int +build_packed_sei_pic_timing_buffer(struct svcenc_context *ctx, int frame_num, unsigned char **sei_buffer) +{ + bitstream sei_pic_bs; + unsigned int cpb_removal_delay; + + if (ctx->rate_control_mode & VA_RC_CQP) { + *sei_buffer = 0; + return 0; + } + + /* SEI pic timing info */ + bitstream_start(&sei_pic_bs); + + /* The info of CPB and DPB delay is controlled by CpbDpbDelaysPresentFlag, + * which is derived as 1 if one of the following conditions is true: + * nal_hrd_parameters_present_flag is present in the bitstream and is equal to 1, + * vcl_hrd_parameters_present_flag is present in the bitstream and is equal to 1, + */ + cpb_removal_delay = (ctx->current_cpb_removal - ctx->prev_idr_cpb_removal); + bitstream_put_ui(&sei_pic_bs, cpb_removal_delay, ctx->i_cpb_removal_delay_length); + bitstream_put_ui(&sei_pic_bs, ctx->current_dpb_removal_delta, ctx->i_dpb_output_delay_length); + + if (sei_pic_bs.bit_offset & 0x7) { + bitstream_put_ui(&sei_pic_bs, 1, 1); + } + + /* The pic_structure_present_flag determines whether the pic_structure + * info is written into the SEI pic timing info. + * Currently it is set to zero. + */ + bitstream_end(&sei_pic_bs); + + *sei_buffer = (unsigned char *)sei_pic_bs.buffer; + + return (sei_pic_bs.bit_offset + 7) / 8; +} + +static int +build_packed_sei_scalability_info_buffer(struct svcenc_context *ctx, + const VAEncSequenceParameterBufferH264 *seq_param, + const VAEncPictureParameterBufferH264 *pic_param, + int frame_num, + unsigned char **sei_buffer) +{ + bitstream scalability_info_bs; + int i; + + if (frame_num) { // non IDR + *sei_buffer = NULL; + return 0; + } + + /* Write scalability_info */ + bitstream_start(&scalability_info_bs); + + bitstream_put_ui(&scalability_info_bs, 0, 1); // temporal_id_nesting_flag: false + bitstream_put_ui(&scalability_info_bs, 0, 1); // priority_layer_info_present_flag: false + bitstream_put_ui(&scalability_info_bs, 0, 1); // priority_id_setting_flag: false + bitstream_put_ue(&scalability_info_bs, ctx->hierarchical_levels - 1); // num_layers_minus1 + + for (i = 0; i < ctx->hierarchical_levels; i++) { + bitstream_put_ue(&scalability_info_bs, i); // layer_id[i] + bitstream_put_ui(&scalability_info_bs, 0, 6); // priority_id[i[ + bitstream_put_ui(&scalability_info_bs, 0, 1); // discardable_flag[i] + bitstream_put_ui(&scalability_info_bs, 0, 3); // dependency_id[i] + bitstream_put_ui(&scalability_info_bs, 0, 4); // quality_id[i] + bitstream_put_ui(&scalability_info_bs, i, 3); // temporal_id[i] + bitstream_put_ui(&scalability_info_bs, 0, 1); // sub_pic_layer_flag[i] + bitstream_put_ui(&scalability_info_bs, 0, 1); // sub_region_layer_flag[i] + bitstream_put_ui(&scalability_info_bs, 0, 1); // iroi_division_info_present_flag[i] + bitstream_put_ui(&scalability_info_bs, 0, 1); // profile_level_info_present_flag[i] + bitstream_put_ui(&scalability_info_bs, 0, 1); // bitrate_info_present_flag[i] + bitstream_put_ui(&scalability_info_bs, 1, 1); // frm_rate_info_present_flag[i] + bitstream_put_ui(&scalability_info_bs, 1, 1); // frm_size_info_present_flag[i] + bitstream_put_ui(&scalability_info_bs, 0, 1); // layer_dependency_info_present_flag[i] + bitstream_put_ui(&scalability_info_bs, 0, 1); // parameter_sets_info_present_flag[i] + bitstream_put_ui(&scalability_info_bs, 0, 1); // bitstream_restriction_info_present_flag[i] + bitstream_put_ui(&scalability_info_bs, 0, 1); // exact_interlayer_pred_flag[i] + bitstream_put_ui(&scalability_info_bs, 0, 1); // layer_conversion_flag[i] + bitstream_put_ui(&scalability_info_bs, 0, 1); // layer_output_flag[i] + + bitstream_put_ui(&scalability_info_bs, 0, 2); // constant_frm_bitrate_idc[i] + bitstream_put_ui(&scalability_info_bs, (int)floor(frame_rates[i] * 256 + 0.5), 16); // avg_frm_rate + + bitstream_put_ue(&scalability_info_bs, seq_param->picture_width_in_mbs - 1); // frm_width_in_mbs_minus1 + bitstream_put_ue(&scalability_info_bs, seq_param->picture_height_in_mbs - 1); // frm_height_in_mbs_minus1 + + bitstream_put_ue(&scalability_info_bs, 0); // layer_dependency_info_src_layer_id_delta[i] + bitstream_put_ue(&scalability_info_bs, 0); // parameter_sets_info_src_layer_id_delta[i] + } + + rbsp_trailing_bits(&scalability_info_bs); + bitstream_end(&scalability_info_bs); + + *sei_buffer = (unsigned char *)scalability_info_bs.buffer; + + return (scalability_info_bs.bit_offset + 7) / 8; +} + +static void +svcenc_update_sei_info(struct svcenc_context *ctx, svcenc_surface *current_surface) +{ + unsigned long long frame_interval; + + if (!(ctx->rate_control_mode & VA_RC_CBR)) { + return; + } + + frame_interval = current_surface->coding_order - ctx->idr_frame_num; + + if (current_surface->is_idr) { + ctx->current_cpb_removal = ctx->prev_idr_cpb_removal + frame_interval * 2; + ctx->idr_frame_num = current_surface->coding_order; + ctx->current_idr_cpb_removal = ctx->current_cpb_removal; + + if (ctx->ip_period) + ctx->current_dpb_removal_delta = (ctx->ip_period + 1) * 2; + else + ctx->current_dpb_removal_delta = 2; + } else { + ctx->current_cpb_removal = ctx->current_idr_cpb_removal + frame_interval * 2; + + if (current_surface->picture_type == VAEncPictureTypeIntra || + current_surface->picture_type == VAEncPictureTypePredictive) { + if (ctx->ip_period) + ctx->current_dpb_removal_delta = (ctx->ip_period + 1) * 2; + else + ctx->current_dpb_removal_delta = 2; + } else + ctx->current_dpb_removal_delta = 2; + } +} + +static int +build_packed_sei_buffer(struct svcenc_context *ctx, + const VAEncSequenceParameterBufferH264 *seq_param, + const VAEncPictureParameterBufferH264 *pic_param, + svcenc_surface *current_surface, + unsigned char **sei_buffer) +{ + unsigned char *scalability_info_buffer = NULL, *buffering_period_buffer = NULL, *pic_timing_buffer = NULL; + int scalability_info_size = 0, buffering_period_size = 0, pic_timing_size = 0; + bitstream nal_bs; + int i; + + svcenc_update_sei_info(ctx, current_surface); + + buffering_period_size = build_packed_sei_buffering_period_buffer(ctx, seq_param, current_surface->frame_num, &buffering_period_buffer); + pic_timing_size = build_packed_sei_pic_timing_buffer(ctx, current_surface->frame_num, &pic_timing_buffer); + scalability_info_size = build_packed_sei_scalability_info_buffer(ctx, seq_param, pic_param, current_surface->frame_num, &scalability_info_buffer); + + if (!buffering_period_buffer && + !pic_timing_buffer && + !scalability_info_buffer) { + *sei_buffer = NULL; + return 0; + } + + bitstream_start(&nal_bs); + nal_start_code_prefix(&nal_bs); + nal_header(&nal_bs, NAL_REF_IDC_NONE, NAL_SEI); + + /* Write the SEI buffer */ + if (buffering_period_buffer) { + assert(buffering_period_size); + + bitstream_put_ui(&nal_bs, 0, 8); // last_payload_type_byte: 0 + bitstream_put_ui(&nal_bs, buffering_period_size, 8); // last_payload_size_byte + + for (i = 0; i < buffering_period_size; i++) { + bitstream_put_ui(&nal_bs, buffering_period_buffer[i], 8); + } + + free(buffering_period_buffer); + } + + if (pic_timing_buffer) { + assert(pic_timing_size); + + bitstream_put_ui(&nal_bs, 1, 8); // last_payload_type_byte: 1 + bitstream_put_ui(&nal_bs, pic_timing_size, 8); // last_payload_size_byte + + for (i = 0; i < pic_timing_size; i++) { + bitstream_put_ui(&nal_bs, pic_timing_buffer[i], 8); + } + + free(pic_timing_buffer); + } + + if (scalability_info_buffer) { + assert(scalability_info_size); + + bitstream_put_ui(&nal_bs, 24, 8); // last_payload_type_byte: 24 + bitstream_put_ui(&nal_bs, scalability_info_size, 8); // last_payload_size_byte + + for (i = 0; i < scalability_info_size; i++) { + bitstream_put_ui(&nal_bs, scalability_info_buffer[i], 8); + } + + free(scalability_info_buffer); + } + + rbsp_trailing_bits(&nal_bs); + bitstream_end(&nal_bs); + + *sei_buffer = (unsigned char *)nal_bs.buffer; + + return nal_bs.bit_offset; +} + +static void +slice_header(bitstream *bs, + VAEncSequenceParameterBufferH264 *sps_param, + VAEncPictureParameterBufferH264 *pic_param, + VAEncSliceParameterBufferH264 *slice_param, + unsigned int reordering_info[2]) +{ + int first_mb_in_slice = slice_param->macroblock_address; + int i; + + bitstream_put_ue(bs, first_mb_in_slice); /* first_mb_in_slice: 0 */ + bitstream_put_ue(bs, slice_param->slice_type); /* slice_type */ + bitstream_put_ue(bs, slice_param->pic_parameter_set_id); /* pic_parameter_set_id: 0 */ + bitstream_put_ui(bs, pic_param->frame_num, sps_param->seq_fields.bits.log2_max_frame_num_minus4 + 4); /* frame_num */ + + /* frame_mbs_only_flag == 1 */ + if (!sps_param->seq_fields.bits.frame_mbs_only_flag) { + /* FIXME: */ + assert(0); + } + + if (pic_param->pic_fields.bits.idr_pic_flag) + bitstream_put_ue(bs, slice_param->idr_pic_id); /* idr_pic_id: 0 */ + + if (sps_param->seq_fields.bits.pic_order_cnt_type == 0) { + bitstream_put_ui(bs, pic_param->CurrPic.TopFieldOrderCnt, sps_param->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 + 4); + /* pic_order_present_flag == 0 */ + } else { + /* FIXME: */ + assert(0); + } + + /* redundant_pic_cnt_present_flag == 0 */ + /* slice type */ + if (IS_P_SLICE(slice_param->slice_type)) { + bitstream_put_ui(bs, !!((reordering_info[0] >> 16) & 0xFF), 1); /* num_ref_idx_active_override_flag: */ + + if ((reordering_info[0] >> 16) & 0xFF) + bitstream_put_ue(bs, (reordering_info[0] >> 24) & 0xFF); /* num_ref_idx_l0_active_minus1 */ + + /* ref_pic_list_reordering */ + if (!reordering_info || !(reordering_info[0] & 0x0F)) + bitstream_put_ui(bs, 0, 1); /* ref_pic_list_reordering_flag_l0: 0 */ + else { + bitstream_put_ui(bs, 1, 1); /* ref_pic_list_reordering_flag_l0: 1 */ + bitstream_put_ue(bs, (reordering_info[0] >> 4) & 0x0F); + bitstream_put_ue(bs, (reordering_info[0] >> 8) & 0xFF); + bitstream_put_ue(bs, 3); /* modification_of_pic_nums_idc: 3 */ + } + } else if (IS_B_SLICE(slice_param->slice_type)) { + bitstream_put_ui(bs, slice_param->direct_spatial_mv_pred_flag, 1); /* direct_spatial_mv_pred: 1 */ + + bitstream_put_ui(bs, ((reordering_info[0] >> 16) & 0xFF) || ((reordering_info[1] >> 16) & 0xFF) , 1); /* num_ref_idx_active_override_flag: */ + + if (((reordering_info[0] >> 16) & 0xFF) || ((reordering_info[1] >> 16) & 0xFF)) { + bitstream_put_ue(bs, (reordering_info[0] >> 24) & 0xFF); /* num_ref_idx_l0_active_minus1 */ + bitstream_put_ue(bs, (reordering_info[1] >> 24) & 0xFF); /* num_ref_idx_l0_active_minus1 */ + } + + /* ref_pic_list_reordering */ + + for (i = 0; i < 2; i++) { + if (!reordering_info || !(reordering_info[i] & 0x0F)) + bitstream_put_ui(bs, 0, 1); /* ref_pic_list_reordering_flag_l0/l1: 0 */ + else { + bitstream_put_ui(bs, 1, 1); /* ref_pic_list_reordering_flag_l0/l1: 1 */ + bitstream_put_ue(bs, (reordering_info[i] >> 4) & 0x0F); + bitstream_put_ue(bs, (reordering_info[i] >> 8) & 0xFF); + bitstream_put_ue(bs, 3); /* modification_of_pic_nums_idc: 3 */ + } + } + } + + if ((pic_param->pic_fields.bits.weighted_pred_flag && + IS_P_SLICE(slice_param->slice_type)) || + ((pic_param->pic_fields.bits.weighted_bipred_idc == 1) && + IS_B_SLICE(slice_param->slice_type))) { + /* FIXME: fill weight/offset table */ + assert(0); + } + + /* dec_ref_pic_marking */ + if (pic_param->pic_fields.bits.reference_pic_flag) { /* nal_ref_idc != 0 */ + unsigned char no_output_of_prior_pics_flag = 0; + unsigned char long_term_reference_flag = 0; + unsigned char adaptive_ref_pic_marking_mode_flag = 0; + + if (pic_param->pic_fields.bits.idr_pic_flag) { + bitstream_put_ui(bs, no_output_of_prior_pics_flag, 1); /* no_output_of_prior_pics_flag: 0 */ + bitstream_put_ui(bs, long_term_reference_flag, 1); /* long_term_reference_flag: 0 */ + } else { + bitstream_put_ui(bs, adaptive_ref_pic_marking_mode_flag, 1); /* adaptive_ref_pic_marking_mode_flag: 0 */ + } + } + + if (pic_param->pic_fields.bits.entropy_coding_mode_flag && + !IS_I_SLICE(slice_param->slice_type)) + bitstream_put_ue(bs, slice_param->cabac_init_idc); /* cabac_init_idc: 0 */ + + bitstream_put_se(bs, slice_param->slice_qp_delta); /* slice_qp_delta: 0 */ + + /* ignore for SP/SI */ + + if (pic_param->pic_fields.bits.deblocking_filter_control_present_flag) { + bitstream_put_ue(bs, slice_param->disable_deblocking_filter_idc); /* disable_deblocking_filter_idc: 0 */ + + if (slice_param->disable_deblocking_filter_idc != 1) { + bitstream_put_se(bs, slice_param->slice_alpha_c0_offset_div2); /* slice_alpha_c0_offset_div2: 2 */ + bitstream_put_se(bs, slice_param->slice_beta_offset_div2); /* slice_beta_offset_div2: 2 */ + } + } + + if (pic_param->pic_fields.bits.entropy_coding_mode_flag) { + bitstream_byte_aligning(bs, 1); + } +} + +static void +nal_header_extension(bitstream *bs, + VAEncPictureParameterBufferH264 *pic_param, + unsigned int temporal_id) +{ + int is_idr = !!pic_param->pic_fields.bits.idr_pic_flag; + + /* 3 bytes */ + bitstream_put_ui(bs, 1, 1); /* svc_extension_flag */ + bitstream_put_ui(bs, !!is_idr, 1); + bitstream_put_ui(bs, 0, 6); /* priority_id */ + bitstream_put_ui(bs, 1, 1); /* no_inter_layer_pred_flag */ + bitstream_put_ui(bs, 0, 3); /* dependency_id */ + bitstream_put_ui(bs, 0, 4); /* quality_id */ + bitstream_put_ui(bs, temporal_id, 3); /* temporal_id */ + bitstream_put_ui(bs, 0, 1); /* use_ref_base_pic_flag */ + bitstream_put_ui(bs, 1, 1); /* discardable_flag */ + bitstream_put_ui(bs, 1, 1); /* output_flag */ + bitstream_put_ui(bs, 3, 2); /* reserved_three_2bits */ +} + +static void +nal_unit_svc_prefix_rbsp(bitstream *bs, int is_ref) +{ + if (is_ref) { + bitstream_put_ui(bs, 0, 1); /* store_ref_base_pic_flag */ + bitstream_put_ui(bs, 0, 1); /* additional_prefix_nal_unit_extension_flag*/ + } else { + /* no more rbsp data */ + } + + rbsp_trailing_bits(bs); +} + +static int +build_packed_svc_prefix_nal_unit(VAEncPictureParameterBufferH264 *pic_param, + unsigned int temporal_id, + unsigned char **nal_unit_buffer) +{ + int is_ref = !!pic_param->pic_fields.bits.reference_pic_flag; + bitstream bs; + + bitstream_start(&bs); + nal_start_code_prefix(&bs); + + nal_header(&bs, is_ref ? NAL_REF_IDC_LOW : NAL_REF_IDC_NONE, NAL_PREFIX); + nal_header_extension(&bs, pic_param, temporal_id); + nal_unit_svc_prefix_rbsp(&bs, is_ref); + + bitstream_end(&bs); + *nal_unit_buffer = (unsigned char *)bs.buffer; + + return bs.bit_offset; +} + +static int +build_packed_slice_header_buffer(struct svcenc_context *ctx, + VAEncSequenceParameterBufferH264 *sps_param, + VAEncPictureParameterBufferH264 *pic_param, + VAEncSliceParameterBufferH264 *slice_param, + unsigned int reordering_info[2], + unsigned char **header_buffer) +{ + bitstream bs; + int is_idr = !!pic_param->pic_fields.bits.idr_pic_flag; + int is_ref = !!pic_param->pic_fields.bits.reference_pic_flag; + + bitstream_start(&bs); + nal_start_code_prefix(&bs); + + if (IS_I_SLICE(slice_param->slice_type)) { + nal_header(&bs, NAL_REF_IDC_HIGH, is_idr ? NAL_IDR : NAL_NON_IDR); + } else if (IS_P_SLICE(slice_param->slice_type)) { + assert(!is_idr); + nal_header(&bs, is_ref ? NAL_REF_IDC_MEDIUM : NAL_REF_IDC_NONE, NAL_NON_IDR); + } else { + assert(IS_B_SLICE(slice_param->slice_type)); + assert(!is_idr); + nal_header(&bs, is_ref ? NAL_REF_IDC_LOW : NAL_REF_IDC_NONE, NAL_NON_IDR); + } + + slice_header(&bs, sps_param, pic_param, slice_param, reordering_info); + + bitstream_end(&bs); + *header_buffer = (unsigned char *)bs.buffer; + + return bs.bit_offset; +} + +/* upload thread */ +static struct upload_task_t * +upload_task_dequeue(struct svcenc_context *ctx) +{ + struct upload_task_t *task; + + pthread_mutex_lock(&upload_mutex); + + task = ctx->upload_task_header; + + if (task) { + ctx->upload_task_header = task->next; + + if (!ctx->upload_task_header) + ctx->upload_task_tail = NULL; + } + + pthread_mutex_unlock(&upload_mutex); + + return task; +} + +static int +upload_task_queue(struct svcenc_context *ctx, unsigned int display_order, int surface) +{ + struct upload_task_t *task; + + task = calloc(1, sizeof(struct upload_task_t)); + task->display_order = display_order; + task->surface = surface; + task->next = NULL; + + pthread_mutex_lock(&upload_mutex); + + if (!ctx->upload_task_header) + ctx->upload_task_header = task; + + if (ctx->upload_task_tail) + ctx->upload_task_tail->next = task; + + ctx->upload_task_tail = task; + src_surface_status[surface] = SRC_SURFACE_IN_STORAGE; + pthread_cond_signal(&upload_cond); + + pthread_mutex_unlock(&upload_mutex); + + return 0; +} + +static void +upload_task(struct svcenc_context *ctx, unsigned int display_order, int surface) +{ + VAStatus va_status; + VAImage surface_image; + void *surface_p = NULL; + size_t n_items; + unsigned char *y_src, *u_src, *v_src; + unsigned char *y_dst, *u_dst, *v_dst; + int row, col; + int y_size = ctx->width * ctx->height; + int u_size = (ctx->width >> 1) * (ctx->height >> 1); + + fseek(ctx->ifp, ctx->frame_size * display_order, SEEK_SET); + + do { + n_items = fread(ctx->frame_data_buffer, ctx->frame_size, 1, ctx->ifp); + } while (n_items != 1); + + va_status = vaDeriveImage(ctx->va_dpy, src_surfaces[surface], &surface_image); + CHECK_VASTATUS(va_status,"vaDeriveImage"); + + vaMapBuffer(ctx->va_dpy, surface_image.buf, &surface_p); + assert(VA_STATUS_SUCCESS == va_status); + + y_src = ctx->frame_data_buffer; + u_src = ctx->frame_data_buffer + y_size; /* UV offset for NV12 */ + v_src = ctx->frame_data_buffer + 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 += ctx->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 += (ctx->width / 2); + v_src += (ctx->width / 2); + } + } else { + for (row = 0; row < surface_image.height / 2; row++) { + for (col = 0; col < surface_image.width / 2; col++) { + u_dst[col] = u_src[col]; + v_dst[col] = v_src[col]; + } + + u_dst += surface_image.pitches[1]; + v_dst += surface_image.pitches[2]; + u_src += (ctx->width / 2); + v_src += (ctx->width / 2); + } + } + + vaUnmapBuffer(ctx->va_dpy, surface_image.buf); + vaDestroyImage(ctx->va_dpy, surface_image.image_id); + + pthread_mutex_lock(&upload_mutex); + src_surface_status[surface] = SRC_SURFACE_IN_ENCODING; + pthread_mutex_unlock(&upload_mutex); +} + +static void * +upload_task_thread(void *data) +{ + struct svcenc_context *ctx = (struct svcenc_context *)data; + int num = 0; + + while (1) { + struct upload_task_t *task; + + task = upload_task_dequeue(ctx); + if (!task) { + pthread_mutex_lock(&upload_mutex); + pthread_cond_wait(&upload_cond, &upload_mutex); + pthread_mutex_unlock(&upload_mutex); + + continue; + } + + upload_task(ctx, task->display_order, task->surface); + free(task); + + if (++num >= ctx->num_pictures) + break; + } + + return 0; +} + +/* main thread */ +static void +usage(char *program) +{ + fprintf(stderr, "Usage: %s --help\n", program); + fprintf(stderr, "\t--help print this message\n"); + fprintf(stderr, "Usage: %s [options] \n", program); + fprintf(stderr, "\t\t\tThe base picture width\n"); + fprintf(stderr, "\t\tThe base picture height\n"); + fprintf(stderr, "\t\tThe input YUV I420 file\n"); + fprintf(stderr, "\t\t\tThe output H.264/SVC file\n\n"); + fprintf(stderr, "\tAvailable options:\n"); + fprintf(stderr, "\t--gop \t\tGOP size, default is 8\n"); + fprintf(stderr, "\t--goptype \tGOP hierarchical type, 0 is P hierarchical GOP, 1 is B hierarchical GOP, default is 0\n"); + fprintf(stderr, "\t--brcmode \tBRC mode, 0 is CQP mode, otherwise CBR mode, default mode is CQP\n"); + fprintf(stderr, "\t--brclayer\tDisable/Enalbe BRC per temporal layer, default is disable(0)\n"); + fprintf(stderr, "\t--bitrate \tbit rate in Kbps(1024 bits). It is available for CBR mode only and default value is 2000\n"); +} + +static void +svcenc_exit(struct svcenc_context *ctx, int exit_code) +{ + if (ctx->ifp) { + fclose(ctx->ifp); + ctx->ifp = NULL; + } + + if (ctx->ofp) { + fclose(ctx->ofp); + ctx->ofp = NULL; + } + + exit(exit_code); +} + +static void +parse_args(struct svcenc_context *ctx, int argc, char **argv) +{ + int c, tmp; + int option_index = 0; + long file_size; + + static struct option long_options[] = { + {"gop", required_argument, 0, 'g'}, + {"goptype", required_argument, 0, 't'}, + {"bitrate", required_argument, 0, 'b'}, + {"brcmode", required_argument, 0, 'm'}, + {"brclayer", required_argument, 0, 'l'}, + {"help", no_argument, 0, 'h'}, + { NULL, 0, NULL, 0 } + }; + + va_init_display_args(&argc, argv); + + if ((argc == 2 && strcmp(argv[1], "--help") == 0) || + (argc < 5)) + goto print_usage; + + ctx->width = atoi(argv[1]); + ctx->height = atoi(argv[2]); + + if (ctx->width <= 0 || ctx->height <= 0) { + fprintf(stderr, " and must be greater than 0\n"); + goto err_exit; + } + + ctx->frame_size = ctx->width * ctx->height * 3 / 2; + ctx->ifp = fopen(argv[3], "rb"); + + if (ctx->ifp == NULL) { + fprintf(stderr, "Can't open the input file\n"); + goto err_exit; + } + + fseek(ctx->ifp, 0l, SEEK_END); + file_size = ftell(ctx->ifp); + + if ((file_size < ctx->frame_size) || + (file_size % ctx->frame_size)) { + fprintf(stderr, "The input file size %ld isn't a multiple of the frame size %d\n", file_size, ctx->frame_size); + goto err_exit; + } + + assert(ctx->num_pictures == 0); + ctx->num_pictures = file_size / ctx->frame_size; + fseek(ctx->ifp, 0l, SEEK_SET); + + ctx->ofp = fopen(argv[4], "wb"); + + if (ctx->ofp == NULL) { + fprintf(stderr, "Can't create the output file\n"); + goto err_exit; + } + + opterr = 0; + optind = 5; + ctx->gop_size = 8; + ctx->gop_type = 0; + ctx->bits_per_kbps = -1; + ctx->rate_control_mode = VA_RC_CQP; + ctx->layer_brc = 0; + + while ((c = getopt_long(argc, argv, + "", + long_options, + &option_index)) != -1) { + switch(c) { + case 'g': + tmp = atoi(optarg); + + if ((tmp & (tmp - 1)) || + tmp > 8) { + fprintf(stderr, "Warning: invalid GOP size\n"); + tmp = 8; + } + + ctx->gop_size = tmp; + + break; + + case 't': + tmp = atoi(optarg); + + ctx->gop_type = !!tmp; + + break; + + case 'b': + tmp = atoi(optarg); + + if (tmp <= 0) + ctx->bits_per_kbps = -1; + else { + if (tmp >= 30000) + tmp = 30000; + + ctx->bits_per_kbps = tmp; + } + + break; + + case 'm': + tmp = atoi(optarg); + + if (tmp == 0) + ctx->rate_control_mode = VA_RC_CQP; + else + ctx->rate_control_mode = VA_RC_CBR; + + break; + + case 'l': + tmp = atoi(optarg); + + ctx->layer_brc = !!tmp; + + break; + + case '?': + fprintf(stderr, "Error: unkown command options\n"); + + case 'h': + goto print_usage; + } + } + + ctx->num_pictures = ((ctx->num_pictures - 1) & ~(ctx->gop_size - 1)) + 1; + + if (ctx->rate_control_mode == VA_RC_CQP) + ctx->bits_per_kbps = -1; + else { + if (ctx->bits_per_kbps == -1) + ctx->bits_per_kbps = 2000; + } + + return; + +print_usage: + usage(argv[0]); +err_exit: + svcenc_exit(ctx, 1); +} + +void +svcenc_update_ref_frames(struct svcenc_context *ctx, svcenc_surface *current_surface) +{ + int i; + + if (!current_surface->is_ref) + return; + + if (current_surface->is_idr) + ctx->num_ref_frames = 1; + else + ctx->num_ref_frames++; + + if (ctx->num_ref_frames > ctx->max_num_ref_frames) + ctx->num_ref_frames = ctx->max_num_ref_frames; + + for (i = ctx->num_ref_frames - 1; i > 0; i--) + ref_frames[i] = ref_frames[i - 1]; + + ref_frames[0] = *current_surface; +} + +static int +svcenc_coding_order_to_display_order(struct svcenc_context *ctx, int coding_order) +{ + int n, m; + int level; + + assert(coding_order > 0); + + n = (coding_order - 1) / ctx->gop_size; + m = (coding_order - 1) % ctx->gop_size; + level = ctx->hierarchical_levels - temporal_ids_in_bgop[m] - 1; + + return n * ctx->gop_size + gop_factors_in_bgop[m] * (int)pow(2.00, level); +} + +static void +update_next_picture_info(struct svcenc_context *ctx) +{ + svcenc_surface *current_surface = NULL; + svcenc_surface next_surface; + int current_frame_num; + + current_surface = &ctx->next_svcenc_surface; + current_frame_num = current_surface->frame_num; + + next_surface.coding_order = current_surface->coding_order + 1; + next_surface.display_order = svcenc_coding_order_to_display_order(ctx, next_surface.coding_order); + next_surface.slot_in_surfaces = (current_surface->slot_in_surfaces + 1) % NUM_SURFACES; + next_surface.temporal_id = temporal_ids_in_bgop[current_surface->coding_order % ctx->gop_size]; + next_surface.is_ref = (next_surface.temporal_id != ctx->hierarchical_levels - 1); + + if (next_surface.temporal_id == 0) { + if (!(next_surface.display_order % ctx->intra_period)) + next_surface.picture_type = VAEncPictureTypeIntra; + else + next_surface.picture_type = VAEncPictureTypePredictive; + } else + next_surface.picture_type = VAEncPictureTypeBidirectional; + + if (current_surface->temporal_id == ctx->hierarchical_levels - 1) + next_surface.frame_num = current_frame_num; + else + next_surface.frame_num = current_frame_num + 1; + + next_surface.is_intra = (next_surface.picture_type == VAEncPictureTypeIntra); + next_surface.is_idr = 0; + next_surface.rec_surface = rec_surfaces[next_surface.slot_in_surfaces]; + next_surface.poc = next_surface.display_order; + + if (next_surface.is_idr) + next_surface.frame_num = 0; + + *current_surface = next_surface; +} + +static int +svcenc_coding_order_to_level_pgop(struct svcenc_context *ctx, int coding_order) +{ + int m; + int level; + + assert(coding_order > 0); + + m = coding_order % ctx->gop_size; + + if (m == 0) + return 0; + + level = ctx->hierarchical_levels - temporal_ids_in_pgop[m - 1]; + + assert(level > 0 && level < 5); + + return level; +} + +static void +update_next_picture_info_pgop(struct svcenc_context *ctx) +{ + svcenc_surface *current_surface = NULL; + svcenc_surface next_surface; + int current_frame_num; + + current_surface = &ctx->next_svcenc_surface; + current_frame_num = current_surface->frame_num; + + next_surface.coding_order = current_surface->coding_order + 1; + next_surface.display_order = next_surface.coding_order; + next_surface.slot_in_surfaces = (current_surface->slot_in_surfaces + 1) % NUM_SURFACES; + next_surface.temporal_id = svcenc_coding_order_to_level_pgop(ctx, next_surface.coding_order); + next_surface.is_ref = (next_surface.temporal_id != ctx->hierarchical_levels - 1); + + if (next_surface.temporal_id == 0) { + if (!(next_surface.display_order % ctx->intra_period)) + next_surface.picture_type = VAEncPictureTypeIntra; + else + next_surface.picture_type = VAEncPictureTypePredictive; + } else { + next_surface.picture_type = VAEncPictureTypePredictive; + } + + next_surface.frame_num = current_frame_num + 1; + next_surface.is_intra = (next_surface.picture_type == VAEncPictureTypeIntra); + next_surface.is_idr = !(next_surface.display_order % ctx->intra_idr_period); + next_surface.rec_surface = rec_surfaces[next_surface.slot_in_surfaces]; + next_surface.poc = next_surface.display_order % ctx->intra_idr_period; + + if (next_surface.is_idr) { + next_surface.frame_num = 0; + assert(next_surface.poc == 0); + } + + *current_surface = next_surface; +} + +static int +svcenc_seq_parameter_set_id(struct svcenc_context *ctx, + svcenc_surface *current_surface) +{ + return 0; +} + +static int +svcenc_pic_parameter_set_id(struct svcenc_context *ctx, + svcenc_surface *current_surface) +{ + return 0; +} + +void +svcenc_update_sequence_parameter_buffer(struct svcenc_context *ctx, + svcenc_surface *current_surface) +{ + VAEncSequenceParameterBufferH264 *seq_param = &ctx->seq_param; + VAStatus va_status; + int width_in_mbs = (ctx->width + 15) / 16; + int height_in_mbs = (ctx->height + 15) / 16; + int frame_cropping_flag = 0; + int frame_crop_bottom_offset = 0; + + memset(seq_param, 0, sizeof(*seq_param)); + seq_param->seq_parameter_set_id = svcenc_seq_parameter_set_id(ctx, current_surface); + seq_param->level_idc = 41; + seq_param->intra_period = ctx->intra_period; + seq_param->ip_period = ctx->ip_period; + seq_param->intra_idr_period = ctx->intra_idr_period; + seq_param->max_num_ref_frames = ctx->max_num_ref_frames; + seq_param->picture_width_in_mbs = width_in_mbs; + seq_param->picture_height_in_mbs = height_in_mbs; + seq_param->seq_fields.bits.frame_mbs_only_flag = 1; + + if (ctx->bits_per_kbps > 0) + seq_param->bits_per_second = 1024 * ctx->bits_per_kbps; + else + seq_param->bits_per_second = 0; + + seq_param->time_scale = ctx->framerate_per_100s * 2; + seq_param->num_units_in_tick = 100; + + if (height_in_mbs * 16 - ctx->height) { + frame_cropping_flag = 1; + frame_crop_bottom_offset = + (height_in_mbs * 16 - ctx->height) / (2 * (!seq_param->seq_fields.bits.frame_mbs_only_flag + 1)); + } + + seq_param->frame_cropping_flag = frame_cropping_flag; + seq_param->frame_crop_left_offset = 0; + seq_param->frame_crop_right_offset = 0; + seq_param->frame_crop_top_offset = 0; + seq_param->frame_crop_bottom_offset = frame_crop_bottom_offset; + + seq_param->seq_fields.bits.pic_order_cnt_type = 0; + + if ((ctx->profile_idc == PROFILE_IDC_SCALABLE_HIGH && + ctx->constraint_set_flag & (1 << 3)) || + (ctx->profile_idc == PROFILE_IDC_SCALABLE_BASELINE && + ctx->constraint_set_flag & (1 << 5))) + seq_param->seq_fields.bits.direct_8x8_inference_flag = 0; + else + seq_param->seq_fields.bits.direct_8x8_inference_flag = 1; + + seq_param->seq_fields.bits.log2_max_frame_num_minus4 = 10; + seq_param->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 = 10; + + if (ctx->bits_per_kbps > 0) + seq_param->vui_parameters_present_flag = 1; //HRD info located in vui + else + seq_param->vui_parameters_present_flag = 0; + + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncSequenceParameterBufferType, + sizeof(*seq_param), + 1, + seq_param, + &ctx->seq_param_buf_id); + CHECK_VASTATUS(va_status,"vaCreateBuffer"); +} + +void +svcenc_update_picture_parameter_buffer(struct svcenc_context *ctx, + svcenc_surface *current_surface) +{ + VAEncPictureParameterBufferH264 *pic_param; + VAStatus va_status; + int i; + + /* Allocate the new coded buffer */ + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncCodedBufferType, + ctx->frame_size, + 1, + NULL, + &ctx->codedbuf_buf_id); + assert(ctx->codedbuf_buf_id != VA_INVALID_ID); + CHECK_VASTATUS(va_status,"vaCreateBuffer"); + + /* H.264 picture parameter */ + pic_param = &ctx->pic_param; + memset(pic_param, 0, sizeof(*pic_param)); + + pic_param->seq_parameter_set_id = svcenc_seq_parameter_set_id(ctx, current_surface); + pic_param->pic_parameter_set_id = svcenc_pic_parameter_set_id(ctx, current_surface); + pic_param->last_picture = 0; + pic_param->pic_init_qp = 34; + + pic_param->num_ref_idx_l0_active_minus1 = ctx->hierarchical_levels - 1; + pic_param->num_ref_idx_l1_active_minus1 = ctx->hierarchical_levels - 1; + + pic_param->coded_buf = ctx->codedbuf_buf_id; + pic_param->frame_num = current_surface->frame_num; + + pic_param->CurrPic.picture_id = current_surface->rec_surface; + pic_param->CurrPic.TopFieldOrderCnt = current_surface->poc * 2; + pic_param->CurrPic.BottomFieldOrderCnt = pic_param->CurrPic.TopFieldOrderCnt; + pic_param->CurrPic.frame_idx = current_surface->frame_num; + pic_param->CurrPic.flags = 0; /* frame */ + + for (i = 0; i < ctx->num_ref_frames; i++) { + assert(pic_param->CurrPic.picture_id != ref_frames[i].rec_surface); + + pic_param->ReferenceFrames[i].picture_id = ref_frames[i].rec_surface; + pic_param->ReferenceFrames[i].TopFieldOrderCnt = ref_frames[i].poc * 2; + pic_param->ReferenceFrames[i].BottomFieldOrderCnt = pic_param[i].ReferenceFrames[i].TopFieldOrderCnt; + pic_param->ReferenceFrames[i].flags = VA_PICTURE_H264_SHORT_TERM_REFERENCE; + } + + for (; i < 16; i++) { + pic_param->ReferenceFrames[i].picture_id = VA_INVALID_ID; + pic_param->ReferenceFrames[i].flags = VA_PICTURE_H264_INVALID; + } + + pic_param->pic_fields.bits.entropy_coding_mode_flag = ENTROPY_MODE_CAVLC; + pic_param->pic_fields.bits.weighted_pred_flag = 0; + pic_param->pic_fields.bits.weighted_bipred_idc = 0; + pic_param->pic_fields.bits.idr_pic_flag = !!current_surface->is_idr; + pic_param->pic_fields.bits.reference_pic_flag = !!current_surface->is_ref; + pic_param->pic_fields.bits.deblocking_filter_control_present_flag = 1; + + if (ctx->profile_idc == PROFILE_IDC_BASELINE || + ctx->profile_idc == PROFILE_IDC_MAIN || + ctx->profile_idc == PROFILE_IDC_SCALABLE_BASELINE) + pic_param->pic_fields.bits.transform_8x8_mode_flag = 0; + else + pic_param->pic_fields.bits.transform_8x8_mode_flag = 0; + + /* Allocate the new picture parameter buffer */ + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncPictureParameterBufferType, + sizeof(*pic_param), + 1, + pic_param, + &ctx->pic_param_buf_id); + assert(ctx->pic_param_buf_id != VA_INVALID_ID); + CHECK_VASTATUS(va_status,"vaCreateBuffer"); +} + +#define partition(ref, field, key, ascending) \ + while (i <= j) { \ + if (ascending) { \ + while (ref[i].field < key) \ + i++; \ + while (ref[j].field > key) \ + j--; \ + } else { \ + while (ref[i].field > key) \ + i++; \ + while (ref[j].field < key) \ + j--; \ + } \ + if (i <= j) { \ + tmp = ref[i]; \ + ref[i] = ref[j]; \ + ref[j] = tmp; \ + i++; \ + j--; \ + } \ + } \ + +static void +sort_one(svcenc_surface ref[], + int left, int right, + int ascending, int is_frame_num) +{ + int i = left, j = right; + int key; + svcenc_surface tmp; + + if (is_frame_num) { + key = ref[(left + right) / 2].frame_num; + partition(ref, frame_num, key, ascending); + } else { + key = ref[(left + right) / 2].display_order; + partition(ref, display_order, (signed int)key, ascending); + } + + /* recursion */ + if (left < j) + sort_one(ref, left, j, ascending, is_frame_num); + + if (i < right) + sort_one(ref, i, right, ascending, is_frame_num); +} + +static void +sort_two(svcenc_surface ref[], + int left, int right, + int key, int is_frame_num, + int partition_ascending, + int list0_ascending, int list1_ascending) +{ + int i = left, j = right; + svcenc_surface tmp; + + if (is_frame_num) { + partition(ref, frame_num, key, partition_ascending); + } else { + partition(ref, display_order, key, partition_ascending); + } + + sort_one(ref, left, i - 1, list0_ascending, is_frame_num); + sort_one(ref, j + 1, right, list1_ascending, is_frame_num); +} + +static void +svcenc_update_ref_lists(struct svcenc_context *ctx, + svcenc_surface *current_surface) +{ + if (current_surface->picture_type == VAEncPictureTypePredictive) { + memcpy(ref_list0, ref_frames, ctx->num_ref_frames * sizeof(svcenc_surface)); + sort_one(ref_list0, 0, ctx->num_ref_frames - 1, 0, 1); + } else if (current_surface->picture_type == VAEncPictureTypeBidirectional) { + memcpy(ref_list0, ref_frames, ctx->num_ref_frames * sizeof(svcenc_surface)); + sort_two(ref_list0, + 0, ctx->num_ref_frames - 1, + current_surface->display_order, 0, + 1, + 0, 1); + memcpy(ref_list1, ref_frames, ctx->num_ref_frames * sizeof(svcenc_surface)); + sort_two(ref_list1, + 0, ctx->num_ref_frames - 1, + current_surface->display_order, 0, + 0, + 1, 0); + } +} + +static int +avc_temporal_find_surface(VAPictureH264 *curr_pic, + VAPictureH264 *ref_list, + int num_pictures, + int dir) +{ + int i, found = -1, min = 0x7FFFFFFF; + + for (i = 0; i < num_pictures; i++) { + int tmp; + + if ((ref_list[i].flags & VA_PICTURE_H264_INVALID) || + (ref_list[i].picture_id == VA_INVALID_SURFACE)) + break; + + tmp = curr_pic->TopFieldOrderCnt - ref_list[i].TopFieldOrderCnt; + + if (dir) + tmp = -tmp; + + if (tmp > 0 && tmp < min) { + min = tmp; + found = i; + } + } + + return found; +} + +static int +avc_temporal_find_surface_pgop(svcenc_surface *curr_pic, + svcenc_surface *ref_list, + int num_pictures) +{ + int i, found = -1, min = 0x7FFFFFFF; + + for (i = 0; i < num_pictures; i++) { + int tmp; + + if (ref_list[i].rec_surface == VA_INVALID_SURFACE) + break; + + if (curr_pic->temporal_id < ref_list[i].temporal_id) + continue; + + tmp = curr_pic->display_order - ref_list[i].display_order; + + if (tmp > 0 && tmp < min) { + min = tmp; + found = i; + } + } + + assert(found != -1); + + return found; +} + +void +svcenc_update_slice_parameter_buffer(struct svcenc_context *ctx, + svcenc_surface *current_surface) +{ + VAEncPictureParameterBufferH264 *pic_param = &ctx->pic_param; + VAEncSliceParameterBufferH264 *slice_param; + VAStatus va_status; + int i, width_in_mbs, height_in_mbs; + int slice_type; + int num_ref_idx_active_override_flag = 0, num_ref_l0 = 1, num_ref_l1 = 1; + VAPictureH264 RefPicList0[32]; + VAPictureH264 RefPicList1[32]; + VAPictureH264 *curr_pic; + int ref_idx; + + svcenc_update_ref_lists(ctx, current_surface); + + switch (current_surface->picture_type) { + case VAEncPictureTypeBidirectional: + slice_type = SLICE_TYPE_B; + break; + + case VAEncPictureTypePredictive: + slice_type = SLICE_TYPE_P; + break; + + case VAEncPictureTypeIntra: + default: + slice_type = SLICE_TYPE_I; + break; + } + + width_in_mbs = (ctx->width + 15) / 16; + height_in_mbs = (ctx->height + 15) / 16; + + // Slice level + i = 0; + slice_param = &ctx->slice_param[i]; + memset(slice_param, 0, sizeof(*slice_param)); + + slice_param->macroblock_address = 0; + slice_param->num_macroblocks = height_in_mbs * width_in_mbs; + slice_param->pic_parameter_set_id = svcenc_pic_parameter_set_id(ctx, current_surface); + slice_param->slice_type = slice_type; + + slice_param->direct_spatial_mv_pred_flag = 1; /* + * It is required for the slice layer extension and the base + * layer bitstream + * See G.7.4.3 j), G.10.1.2 a), G.10.1.2.1 a) + */ + slice_param->num_ref_idx_active_override_flag = 0; + slice_param->num_ref_idx_l0_active_minus1 = 0; + slice_param->num_ref_idx_l1_active_minus1 = 0; + slice_param->cabac_init_idc = 0; + slice_param->slice_qp_delta = 0; + slice_param->disable_deblocking_filter_idc = 0; + slice_param->slice_alpha_c0_offset_div2 = 2; + slice_param->slice_beta_offset_div2 = 2; + slice_param->idr_pic_id = 0; + + ctx->reordering_info[0] = ctx->reordering_info[1] = 0; + + /* FIXME: fill other fields */ + if ((slice_type == SLICE_TYPE_P) || (slice_type == SLICE_TYPE_B)) { + int j; + num_ref_l0 = MIN((pic_param->num_ref_idx_l0_active_minus1 + 1), ctx->num_ref_frames); + + for (j = 0; j < num_ref_l0; j++) { + RefPicList0[j].picture_id = ref_list0[j].rec_surface; + RefPicList0[j].TopFieldOrderCnt = ref_list0[j].poc * 2; + RefPicList0[j].BottomFieldOrderCnt = RefPicList0[j].TopFieldOrderCnt; + RefPicList0[j].frame_idx = ref_list0[j].frame_num; + RefPicList0[j].flags = VA_PICTURE_H264_SHORT_TERM_REFERENCE; + } + + for (; j < 32; j++) { + RefPicList0[j].picture_id = VA_INVALID_SURFACE; + RefPicList0[j].flags = VA_PICTURE_H264_INVALID; + } + + if (num_ref_l0 != pic_param->num_ref_idx_l0_active_minus1 + 1) + num_ref_idx_active_override_flag = 1; + } + + if ((slice_type == SLICE_TYPE_B)) { + int j; + num_ref_l1 = MIN((pic_param->num_ref_idx_l1_active_minus1 + 1), ctx->num_ref_frames); + + for (j = 0; j < num_ref_l1; j++) { + RefPicList1[j].picture_id = ref_list1[j].rec_surface; + RefPicList1[j].TopFieldOrderCnt = ref_list1[j].poc * 2; + RefPicList1[j].BottomFieldOrderCnt = RefPicList1[j].TopFieldOrderCnt; + RefPicList1[j].frame_idx = ref_list1[j].frame_num; + RefPicList1[j].flags = VA_PICTURE_H264_SHORT_TERM_REFERENCE; + } + + for (; j < 32; j++) { + RefPicList1[j].picture_id = VA_INVALID_SURFACE; + RefPicList1[j].flags = VA_PICTURE_H264_INVALID; + } + + if (num_ref_l1 != pic_param->num_ref_idx_l1_active_minus1 + 1) + num_ref_idx_active_override_flag = 1; + } + + if (num_ref_l0 != 1 || num_ref_l1 != 1) { + curr_pic = &pic_param->CurrPic; + + /* select the reference frame in temporal space */ + if ((slice_type == SLICE_TYPE_P) || (slice_type == SLICE_TYPE_B)) { + if (ctx->gop_type == 1) + ref_idx = avc_temporal_find_surface(curr_pic, RefPicList0, num_ref_l0, 0); + else + ref_idx = avc_temporal_find_surface_pgop(current_surface, ref_list0, num_ref_l0); + + if (ref_idx != 0) { + ctx->reordering_info[0] |= (1 << 0); + + if (curr_pic->frame_idx > RefPicList0[ref_idx].frame_idx) { + ctx->reordering_info[0] |= (0 << 4); + ctx->reordering_info[0] |= ((curr_pic->frame_idx - RefPicList0[ref_idx].frame_idx - 1) << 8); + } else { + ctx->reordering_info[0] |= (1 << 4); + ctx->reordering_info[0] |= ((RefPicList0[ref_idx].frame_idx - curr_pic->frame_idx - 1) << 8); + } + } + + slice_param->RefPicList0[0] = RefPicList0[ref_idx]; + } + + if (slice_type == SLICE_TYPE_B) { + assert(ctx->gop_type == 1); + ref_idx = avc_temporal_find_surface(curr_pic, RefPicList1, num_ref_l1, 1); + + if (ref_idx != 0) { + ctx->reordering_info[1] |= (1 << 0); + + if (curr_pic->frame_idx > RefPicList1[ref_idx].frame_idx) { + ctx->reordering_info[1] |= (0 << 4); + ctx->reordering_info[1] |= ((curr_pic->frame_idx - RefPicList1[ref_idx].frame_idx - 1) << 8); + } else { + ctx->reordering_info[1] |= (1 << 4); + ctx->reordering_info[1] |= ((RefPicList1[ref_idx].frame_idx - curr_pic->frame_idx - 1) << 8); + } + } + + slice_param->RefPicList1[0] = RefPicList1[ref_idx]; + } + + slice_param->num_ref_idx_active_override_flag = 1; + slice_param->num_ref_idx_l0_active_minus1 = 0; + slice_param->num_ref_idx_l1_active_minus1 = 0; + ctx->reordering_info[0] |= ((1 << 16) | (0 << 24)); + ctx->reordering_info[1] |= ((1 << 16) | (0 << 24)); + } else { + if (num_ref_idx_active_override_flag == 1) { + slice_param->num_ref_idx_active_override_flag = 1; + slice_param->num_ref_idx_l0_active_minus1 = 0; + slice_param->num_ref_idx_l1_active_minus1 = 0; + ctx->reordering_info[0] |= ((1 << 16) | (0 << 24)); + ctx->reordering_info[1] |= ((1 << 16) | (0 << 24)); + } + + if ((slice_type == SLICE_TYPE_P) || (slice_type == SLICE_TYPE_B)) { + slice_param->RefPicList0[0] = RefPicList0[0]; + } + + if (slice_type == SLICE_TYPE_B) { + slice_param->RefPicList1[0] = RefPicList1[0]; + } + } + + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncSliceParameterBufferType, + sizeof(*slice_param), + 1, + slice_param, + &ctx->slice_param_buf_id[i]); + CHECK_VASTATUS(va_status,"vaCreateBuffer");; + + ctx->num_slices = ++i; +} + +void +svcenc_update_misc_parameter_buffer(struct svcenc_context *ctx, svcenc_surface *current_surface) +{ + VAStatus va_status; + VAEncMiscParameterBuffer *misc_param; + VAEncMiscParameterTemporalLayerStructure *misc_layer_structure_param; + VAEncMiscParameterRateControl *misc_ratecontrol_param; + VAEncMiscParameterFrameRate *misc_framerate_param; + VAEncMiscParameterHRD *misc_hrd_param; + + if (current_surface->frame_num != 0) + return; + + if (ctx->rate_control_mode & VA_RC_CQP) + return; + + assert(ctx->bits_per_kbps > 0); + + if (!ctx->layer_brc) { + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncMiscParameterBufferType, + sizeof(VAEncMiscParameterBuffer) + sizeof(VAEncMiscParameterRateControl), + 1, + NULL, + &ctx->misc_parameter_ratecontrol_buf_id[0]); + CHECK_VASTATUS(va_status, "vaCreateBuffer"); + + vaMapBuffer(ctx->va_dpy, + ctx->misc_parameter_ratecontrol_buf_id[0], + (void **)&misc_param); + + misc_param->type = VAEncMiscParameterTypeRateControl; + misc_ratecontrol_param = (VAEncMiscParameterRateControl *)misc_param->data; + + misc_ratecontrol_param->bits_per_second = 1024 * ctx->bits_per_kbps; + + vaUnmapBuffer(ctx->va_dpy, ctx->misc_parameter_ratecontrol_buf_id[0]); + + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncMiscParameterBufferType, + sizeof(VAEncMiscParameterBuffer) + sizeof(VAEncMiscParameterFrameRate), + 1, + NULL, + &ctx->misc_parameter_framerate_buf_id[0]); + CHECK_VASTATUS(va_status, "vaCreateBuffer"); + + vaMapBuffer(ctx->va_dpy, + ctx->misc_parameter_framerate_buf_id[0], + (void **)&misc_param); + + misc_param->type = VAEncMiscParameterTypeFrameRate; + misc_framerate_param = (VAEncMiscParameterFrameRate *)misc_param->data; + misc_framerate_param->framerate = ((100 << 16) | ctx->framerate_per_100s); + + misc_ratecontrol_param->bits_per_second = 1024 * ctx->bits_per_kbps; + + vaUnmapBuffer(ctx->va_dpy, ctx->misc_parameter_ratecontrol_buf_id[0]); + + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncMiscParameterBufferType, + sizeof(VAEncMiscParameterBuffer) + sizeof(VAEncMiscParameterHRD), + 1, + NULL, + &ctx->misc_parameter_hrd_buf_id); + CHECK_VASTATUS(va_status, "vaCreateBuffer"); + + vaMapBuffer(ctx->va_dpy, + ctx->misc_parameter_hrd_buf_id, + (void **)&misc_param); + + misc_param->type = VAEncMiscParameterTypeHRD; + misc_hrd_param = (VAEncMiscParameterHRD *)misc_param->data; + + misc_hrd_param->initial_buffer_fullness = ctx->bits_per_kbps * 1024 * 4; + misc_hrd_param->buffer_size = ctx->bits_per_kbps * 1024 * 8; + + vaUnmapBuffer(ctx->va_dpy, ctx->misc_parameter_hrd_buf_id); + } else { + int i, j, nlayers = 0; + + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncMiscParameterBufferType, + sizeof(VAEncMiscParameterBuffer) + sizeof(VAEncMiscParameterTemporalLayerStructure), + 1, + NULL, + &ctx->misc_parameter_layer_structure_buf_id); + CHECK_VASTATUS(va_status, "vaCreateBuffer"); + + vaMapBuffer(ctx->va_dpy, + ctx->misc_parameter_layer_structure_buf_id, + (void **)&misc_param); + + misc_param->type = VAEncMiscParameterTypeTemporalLayerStructure; + misc_layer_structure_param = (VAEncMiscParameterTemporalLayerStructure *)misc_param->data; + + misc_layer_structure_param->number_of_layers = ctx->hierarchical_levels; + misc_layer_structure_param->periodicity = ctx->gop_size; + + for (i = 0; i < ctx->gop_size; i++) { + if (ctx->gop_type == 1) { // B + misc_layer_structure_param->layer_id[i] = temporal_ids_in_bgop[i]; + } else { + misc_layer_structure_param->layer_id[i] = svcenc_coding_order_to_level_pgop(ctx, i + 1); + } + } + + vaUnmapBuffer(ctx->va_dpy, ctx->misc_parameter_layer_structure_buf_id); + + for (i = 1; i <= ctx->hierarchical_levels; i++) { + nlayers += i; + } + + for (i = 0; i < ctx->hierarchical_levels; i++) { + int framerate_per_100s = frame_rates[i] * 100; + int numerator = 0; + + for (j = 0; j < ctx->gop_size; j++) { + if (misc_layer_structure_param->layer_id[j] <= i) + numerator++; + } + + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncMiscParameterBufferType, + sizeof(VAEncMiscParameterBuffer) + sizeof(VAEncMiscParameterRateControl), + 1, + NULL, + &ctx->misc_parameter_ratecontrol_buf_id[i]); + CHECK_VASTATUS(va_status, "vaCreateBuffer"); + + vaMapBuffer(ctx->va_dpy, + ctx->misc_parameter_ratecontrol_buf_id[i], + (void **)&misc_param); + + misc_param->type = VAEncMiscParameterTypeRateControl; + misc_ratecontrol_param = (VAEncMiscParameterRateControl *)misc_param->data; + misc_ratecontrol_param->bits_per_second = 1024 * ctx->bits_per_kbps * ((float)numerator / ctx->gop_size); + misc_ratecontrol_param->rc_flags.bits.temporal_id = i; + + vaUnmapBuffer(ctx->va_dpy, ctx->misc_parameter_ratecontrol_buf_id[i]); + + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncMiscParameterBufferType, + sizeof(VAEncMiscParameterBuffer) + sizeof(VAEncMiscParameterFrameRate), + 1, + NULL, + &ctx->misc_parameter_framerate_buf_id[i]); + CHECK_VASTATUS(va_status, "vaCreateBuffer"); + + vaMapBuffer(ctx->va_dpy, + ctx->misc_parameter_framerate_buf_id[i], + (void **)&misc_param); + + misc_param->type = VAEncMiscParameterTypeFrameRate; + misc_framerate_param = (VAEncMiscParameterFrameRate *)misc_param->data; + misc_framerate_param->framerate = ((100 << 16) | framerate_per_100s); + misc_framerate_param->framerate_flags.bits.temporal_id = i; + + vaUnmapBuffer(ctx->va_dpy, ctx->misc_parameter_ratecontrol_buf_id[i]); + } + + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncMiscParameterBufferType, + sizeof(VAEncMiscParameterBuffer) + sizeof(VAEncMiscParameterHRD), + 1, + NULL, + &ctx->misc_parameter_hrd_buf_id); + CHECK_VASTATUS(va_status, "vaCreateBuffer"); + + vaMapBuffer(ctx->va_dpy, + ctx->misc_parameter_hrd_buf_id, + (void **)&misc_param); + + misc_param->type = VAEncMiscParameterTypeHRD; + misc_hrd_param = (VAEncMiscParameterHRD *)misc_param->data; + + misc_hrd_param->initial_buffer_fullness = ctx->bits_per_kbps * 1024 * 4; + misc_hrd_param->buffer_size = ctx->bits_per_kbps * 1024 * 8; + + vaUnmapBuffer(ctx->va_dpy, ctx->misc_parameter_hrd_buf_id); + } +} + +static void +svcenc_update_packed_slice_header_buffer(struct svcenc_context *ctx, unsigned int temporal_id) +{ + VAStatus va_status; + VAEncPackedHeaderParameterBuffer packed_header_param_buffer; + unsigned int length_in_bits; + unsigned char *packed_header_data_buffer = NULL; + int i; + + for (i = 0; i < ctx->num_slices; i++) { + length_in_bits = build_packed_svc_prefix_nal_unit(&ctx->pic_param, + temporal_id, + &packed_header_data_buffer); + + packed_header_param_buffer.type = VAEncPackedHeaderRawData; + packed_header_param_buffer.bit_length = length_in_bits; + packed_header_param_buffer.has_emulation_bytes = 0; + + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncPackedHeaderParameterBufferType, + sizeof(packed_header_param_buffer), + 1, + &packed_header_param_buffer, + &ctx->packed_prefix_nal_unit_param_buf_id[i]); + CHECK_VASTATUS(va_status,"vaCreateBuffer"); + + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncPackedHeaderDataBufferType, + (length_in_bits + 7) / 8, + 1, + packed_header_data_buffer, + &ctx->packed_prefix_nal_unit_data_buf_id[i]); + CHECK_VASTATUS(va_status,"vaCreateBuffer"); + + free(packed_header_data_buffer); + + length_in_bits = build_packed_slice_header_buffer(ctx, + &ctx->seq_param, + &ctx->pic_param, + &ctx->slice_param[i], + ctx->reordering_info, + &packed_header_data_buffer); + + packed_header_param_buffer.type = VAEncPackedHeaderSlice; + packed_header_param_buffer.bit_length = length_in_bits; + packed_header_param_buffer.has_emulation_bytes = 0; + + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncPackedHeaderParameterBufferType, + sizeof(packed_header_param_buffer), + 1, + &packed_header_param_buffer, + &ctx->packed_slice_header_param_buf_id[i]); + CHECK_VASTATUS(va_status,"vaCreateBuffer"); + + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncPackedHeaderDataBufferType, + (length_in_bits + 7) / 8, + 1, + packed_header_data_buffer, + &ctx->packed_slice_header_data_buf_id[i]); + CHECK_VASTATUS(va_status,"vaCreateBuffer"); + + free(packed_header_data_buffer); + } +} + +void +svcenc_update_packed_buffers(struct svcenc_context *ctx, + svcenc_surface *current_surface) +{ + VAStatus va_status; + VAEncPackedHeaderParameterBuffer packed_header_param_buffer; + unsigned int length_in_bits; + unsigned char *packed_seq_buffer = NULL, *packed_pic_buffer = NULL, *packed_sei_buffer = NULL; + + length_in_bits = build_packed_sei_buffer(ctx, + &ctx->seq_param, + &ctx->pic_param, + current_surface, + &packed_sei_buffer); + + if (packed_sei_buffer) { + assert(length_in_bits); + + packed_header_param_buffer.type = VAEncPackedHeaderH264_SEI; + packed_header_param_buffer.bit_length = length_in_bits; + packed_header_param_buffer.has_emulation_bytes = 0; + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncPackedHeaderParameterBufferType, + sizeof(packed_header_param_buffer), + 1, + &packed_header_param_buffer, + &ctx->packed_sei_header_param_buf_id); + CHECK_VASTATUS(va_status,"vaCreateBuffer"); + + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncPackedHeaderDataBufferType, + (length_in_bits + 7) / 8, + 1, + packed_sei_buffer, + &ctx->packed_sei_buf_id); + CHECK_VASTATUS(va_status,"vaCreateBuffer"); + + free(packed_sei_buffer); + } + + if (current_surface->frame_num == 0) { + int save_profile_idc, save_constraint_set_flag; + unsigned char *packed_svc_seq_buffer = NULL; + + length_in_bits = build_packed_seq_buffer(ctx, + &ctx->seq_param, + &packed_seq_buffer); + + packed_header_param_buffer.type = VAEncPackedHeaderH264_SPS; + packed_header_param_buffer.bit_length = length_in_bits; + packed_header_param_buffer.has_emulation_bytes = 0; + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncPackedHeaderParameterBufferType, + sizeof(packed_header_param_buffer), + 1, + &packed_header_param_buffer, + &ctx->packed_seq_header_param_buf_id); + CHECK_VASTATUS(va_status,"vaCreateBuffer"); + + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncPackedHeaderDataBufferType, + (length_in_bits + 7) / 8, + 1, + packed_seq_buffer, + &ctx->packed_seq_buf_id); + CHECK_VASTATUS(va_status,"vaCreateBuffer"); + + save_profile_idc = ctx->profile_idc; + save_constraint_set_flag = ctx->constraint_set_flag; + ctx->profile_idc = ctx->svc_profile_idc; + ctx->constraint_set_flag = ctx->svc_constraint_set_flag; + + length_in_bits = build_packed_subset_seq_buffer(ctx, + &ctx->seq_param, + &packed_svc_seq_buffer); + packed_header_param_buffer.type = VAEncPackedHeaderRawData; + packed_header_param_buffer.bit_length = length_in_bits; + packed_header_param_buffer.has_emulation_bytes = 0; + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncPackedHeaderParameterBufferType, + sizeof(packed_header_param_buffer), + 1, + &packed_header_param_buffer, + &ctx->packed_svc_seq_header_param_buf_id); + CHECK_VASTATUS(va_status,"vaCreateBuffer"); + + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncPackedHeaderDataBufferType, + (length_in_bits + 7) / 8, + 1, + packed_svc_seq_buffer, + &ctx->packed_svc_seq_buf_id); + CHECK_VASTATUS(va_status,"vaCreateBuffer"); + + ctx->profile_idc = save_profile_idc; + ctx->constraint_set_flag = save_constraint_set_flag; + + free(packed_svc_seq_buffer); + + length_in_bits = build_packed_pic_buffer(ctx, + &ctx->pic_param, + &packed_pic_buffer); + packed_header_param_buffer.type = VAEncPackedHeaderH264_PPS; + packed_header_param_buffer.bit_length = length_in_bits; + packed_header_param_buffer.has_emulation_bytes = 0; + + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncPackedHeaderParameterBufferType, + sizeof(packed_header_param_buffer), + 1, + &packed_header_param_buffer, + &ctx->packed_pic_header_param_buf_id); + CHECK_VASTATUS(va_status,"vaCreateBuffer"); + + va_status = vaCreateBuffer(ctx->va_dpy, + ctx->context_id, + VAEncPackedHeaderDataBufferType, + (length_in_bits + 7) / 8, + 1, + packed_pic_buffer, + &ctx->packed_pic_buf_id); + CHECK_VASTATUS(va_status,"vaCreateBuffer"); + + free(packed_seq_buffer); + free(packed_pic_buffer); + } + + svcenc_update_packed_slice_header_buffer(ctx, current_surface->temporal_id); +} + +void +svcenc_update_profile_idc(struct svcenc_context *ctx, + svcenc_surface *current_surface) +{ + ctx->profile_idc = PROFILE_IDC_HIGH; + ctx->constraint_set_flag = 0; + + switch (ctx->profile) { + case VAProfileH264High: + ctx->profile_idc = PROFILE_IDC_HIGH; + ctx->constraint_set_flag |= (1 << 1); + break; + + case VAProfileH264ConstrainedBaseline: + case VAProfileH264Baseline: + ctx->profile_idc = PROFILE_IDC_BASELINE; + ctx->constraint_set_flag |= (1 << 0 | + 1 << 1 | + 1 << 2); + break; + + default: + /* Never get here !!! */ + assert(0); + break; + } + + ctx->svc_profile_idc = PROFILE_IDC_SCALABLE_HIGH; + ctx->svc_constraint_set_flag = 0; + + switch (ctx->profile) { + case VAProfileH264High: + ctx->svc_profile_idc = PROFILE_IDC_SCALABLE_HIGH; + ctx->svc_constraint_set_flag |= (1 << 1); + break; + + case VAProfileH264ConstrainedBaseline: + ctx->svc_profile_idc = PROFILE_IDC_SCALABLE_BASELINE; + ctx->svc_constraint_set_flag |= (1 << 0 | 1 << 1 | 1 << 5); + break; + + case VAProfileH264Baseline: + ctx->svc_profile_idc = PROFILE_IDC_SCALABLE_BASELINE; + ctx->svc_constraint_set_flag |= (1 << 0); + break; + + default: + /* Never get here !!! */ + assert(0); + break; + } +} + +int +begin_picture(struct svcenc_context *ctx, + svcenc_surface *current_surface) +{ + svcenc_update_profile_idc(ctx, current_surface); + svcenc_update_sequence_parameter_buffer(ctx, current_surface); + svcenc_update_picture_parameter_buffer(ctx, current_surface); + svcenc_update_slice_parameter_buffer(ctx, current_surface); + svcenc_update_misc_parameter_buffer(ctx, current_surface); + svcenc_update_packed_buffers(ctx, current_surface); + + return 0; +} + +static int +svcenc_store_coded_buffer(struct svcenc_context *ctx, + svcenc_surface *current_surface) +{ + VACodedBufferSegment *coded_buffer_segment; + unsigned char *coded_mem; + int slice_data_length; + VAStatus va_status; + size_t w_items; + + va_status = vaSyncSurface(ctx->va_dpy, src_surfaces[current_surface->slot_in_surfaces]); + CHECK_VASTATUS(va_status,"vaSyncSurface"); + + va_status = vaMapBuffer(ctx->va_dpy, ctx->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) { + /* Fatal error !!! */ + assert(0); + } + + slice_data_length = coded_buffer_segment->size; + + do { + w_items = fwrite(coded_mem, slice_data_length, 1, ctx->ofp); + } while (w_items != 1); + + vaUnmapBuffer(ctx->va_dpy, ctx->codedbuf_buf_id); + + return 0; +} + +int +render_picture(struct svcenc_context *ctx, + svcenc_surface *current_surface) +{ + VAStatus va_status; + VABufferID va_buffers[32]; + unsigned int num_va_buffers = 0; + int i; + + va_buffers[num_va_buffers++] = ctx->seq_param_buf_id; + va_buffers[num_va_buffers++] = ctx->pic_param_buf_id; + + if (ctx->packed_sei_scalability_info_header_param_buf_id != VA_INVALID_ID) + va_buffers[num_va_buffers++] = ctx->packed_sei_scalability_info_header_param_buf_id; + + if (ctx->packed_sei_scalability_info_buf_id != VA_INVALID_ID) + va_buffers[num_va_buffers++] = ctx->packed_sei_scalability_info_buf_id; + + if (ctx->packed_seq_header_param_buf_id != VA_INVALID_ID) + va_buffers[num_va_buffers++] = ctx->packed_seq_header_param_buf_id; + + if (ctx->packed_seq_buf_id != VA_INVALID_ID) + va_buffers[num_va_buffers++] = ctx->packed_seq_buf_id; + + if (ctx->packed_svc_seq_header_param_buf_id != VA_INVALID_ID) + va_buffers[num_va_buffers++] = ctx->packed_svc_seq_header_param_buf_id; + + if (ctx->packed_svc_seq_buf_id != VA_INVALID_ID) + va_buffers[num_va_buffers++] = ctx->packed_svc_seq_buf_id; + + if (ctx->packed_pic_header_param_buf_id != VA_INVALID_ID) + va_buffers[num_va_buffers++] = ctx->packed_pic_header_param_buf_id; + + if (ctx->packed_pic_buf_id != VA_INVALID_ID) + va_buffers[num_va_buffers++] = ctx->packed_pic_buf_id; + + if (ctx->packed_sei_header_param_buf_id != VA_INVALID_ID) + va_buffers[num_va_buffers++] = ctx->packed_sei_header_param_buf_id; + + if (ctx->packed_sei_buf_id != VA_INVALID_ID) + va_buffers[num_va_buffers++] = ctx->packed_sei_buf_id; + + if (ctx->misc_parameter_layer_structure_buf_id != VA_INVALID_ID) + va_buffers[num_va_buffers++] = ctx->misc_parameter_layer_structure_buf_id; + + for (i = 0; i < MAX_LAYERS; i++) { + if (ctx->misc_parameter_ratecontrol_buf_id[i] != VA_INVALID_ID) + va_buffers[num_va_buffers++] = ctx->misc_parameter_ratecontrol_buf_id[i]; + + if (ctx->misc_parameter_framerate_buf_id[i] != VA_INVALID_ID) + va_buffers[num_va_buffers++] = ctx->misc_parameter_framerate_buf_id[i]; + } + + if (ctx->misc_parameter_hrd_buf_id != VA_INVALID_ID) + va_buffers[num_va_buffers++] = ctx->misc_parameter_hrd_buf_id; + + va_status = vaBeginPicture(ctx->va_dpy, + ctx->context_id, + src_surfaces[current_surface->slot_in_surfaces]); + CHECK_VASTATUS(va_status,"vaBeginPicture"); + + va_status = vaRenderPicture(ctx->va_dpy, + ctx->context_id, + va_buffers, + num_va_buffers); + CHECK_VASTATUS(va_status,"vaRenderPicture"); + + for (i = 0; i < ctx->num_slices; i++) { + va_status = vaRenderPicture(ctx->va_dpy, + ctx->context_id, + &ctx->packed_prefix_nal_unit_param_buf_id[i], + 1); + CHECK_VASTATUS(va_status,"vaRenderPicture"); + + va_status = vaRenderPicture(ctx->va_dpy, + ctx->context_id, + &ctx->packed_prefix_nal_unit_data_buf_id[i], + 1); + CHECK_VASTATUS(va_status,"vaRenderPicture"); + + va_status = vaRenderPicture(ctx->va_dpy, + ctx->context_id, + &ctx->packed_slice_header_param_buf_id[i], + 1); + CHECK_VASTATUS(va_status,"vaRenderPicture"); + + va_status = vaRenderPicture(ctx->va_dpy, + ctx->context_id, + &ctx->packed_slice_header_data_buf_id[i], + 1); + CHECK_VASTATUS(va_status,"vaRenderPicture"); + + va_status = vaRenderPicture(ctx->va_dpy, + ctx->context_id, + &ctx->slice_param_buf_id[i], + 1); + CHECK_VASTATUS(va_status,"vaRenderPicture"); + } + + va_status = vaEndPicture(ctx->va_dpy, ctx->context_id); + CHECK_VASTATUS(va_status,"vaEndPicture"); + + svcenc_store_coded_buffer(ctx, current_surface); + + return 0; +} + +static int +svcenc_destroy_buffers(struct svcenc_context *ctx, + VABufferID *va_buffers, + unsigned int num_va_buffers) +{ + VAStatus va_status; + unsigned int i; + + for (i = 0; i < num_va_buffers; i++) { + if (va_buffers[i] != VA_INVALID_ID) { + va_status = vaDestroyBuffer(ctx->va_dpy, va_buffers[i]); + CHECK_VASTATUS(va_status,"vaDestroyBuffer"); + va_buffers[i] = VA_INVALID_ID; + } + } + + return 0; +} + +int +end_picture(struct svcenc_context *ctx, + svcenc_surface *current_surface) +{ + svcenc_destroy_buffers(ctx, &ctx->seq_param_buf_id, 1); + svcenc_destroy_buffers(ctx, &ctx->pic_param_buf_id, 1); + svcenc_destroy_buffers(ctx, &ctx->packed_sei_scalability_info_header_param_buf_id, 1); + svcenc_destroy_buffers(ctx, &ctx->packed_sei_scalability_info_buf_id, 1); + svcenc_destroy_buffers(ctx, &ctx->packed_seq_header_param_buf_id, 1); + svcenc_destroy_buffers(ctx, &ctx->packed_seq_buf_id, 1); + svcenc_destroy_buffers(ctx, &ctx->packed_svc_seq_header_param_buf_id, 1); + svcenc_destroy_buffers(ctx, &ctx->packed_svc_seq_buf_id, 1); + svcenc_destroy_buffers(ctx, &ctx->packed_pic_header_param_buf_id, 1); + svcenc_destroy_buffers(ctx, &ctx->packed_pic_buf_id, 1); + svcenc_destroy_buffers(ctx, &ctx->slice_param_buf_id[0], ctx->num_slices); + svcenc_destroy_buffers(ctx, &ctx->codedbuf_buf_id, 1); + svcenc_destroy_buffers(ctx, &ctx->misc_parameter_layer_structure_buf_id, 1); + svcenc_destroy_buffers(ctx, &ctx->misc_parameter_ratecontrol_buf_id[0], MAX_LAYERS); + svcenc_destroy_buffers(ctx, &ctx->misc_parameter_framerate_buf_id[0], MAX_LAYERS); + svcenc_destroy_buffers(ctx, &ctx->misc_parameter_hrd_buf_id, 1); + svcenc_destroy_buffers(ctx, &ctx->packed_sei_header_param_buf_id, 1); + svcenc_destroy_buffers(ctx, &ctx->packed_sei_buf_id, 1); + svcenc_destroy_buffers(ctx, &ctx->packed_slice_header_param_buf_id[0], ctx->num_slices); + svcenc_destroy_buffers(ctx, &ctx->packed_slice_header_data_buf_id[0], ctx->num_slices); + svcenc_destroy_buffers(ctx, &ctx->packed_prefix_nal_unit_param_buf_id[0], ctx->num_slices); + svcenc_destroy_buffers(ctx, &ctx->packed_prefix_nal_unit_data_buf_id[0], ctx->num_slices); + + memset(ctx->slice_param, 0, sizeof(ctx->slice_param)); + ctx->num_slices = 0; + + return 0; +} + +void +encode_picture(struct svcenc_context *ctx, svcenc_surface *current_surface) +{ + begin_picture(ctx, current_surface); + render_picture(ctx, current_surface); + end_picture(ctx, current_surface); + + svcenc_update_ref_frames(ctx, current_surface); + + if ((current_surface->is_idr) && + (ctx->rate_control_mode & VA_RC_CBR)) { + ctx->prev_idr_cpb_removal = ctx->current_idr_cpb_removal; + } +} + +static void +svcenc_va_init(struct svcenc_context *ctx) +{ + VAProfile *profile_list; + VAEntrypoint *entrypoint_list; + VAConfigAttrib attrib_list[4]; + VAStatus va_status; + int max_profiles, num_profiles = 0, profile = VAProfileNone; + int max_entrypoints, num_entrypoints = 0, entrypoint; + int major_ver, minor_ver; + int i, j; + + ctx->va_dpy = va_open_display(); + va_status = vaInitialize(ctx->va_dpy, + &major_ver, + &minor_ver); + CHECK_VASTATUS(va_status, "vaInitialize"); + + max_profiles = vaMaxNumProfiles(ctx->va_dpy); + profile_list = malloc(max_profiles * sizeof(VAProfile)); + vaQueryConfigProfiles(ctx->va_dpy, + profile_list, + &num_profiles); + + for (i = 0; i < ARRAY_ELEMS(g_va_profiles); i++) { + for (j = 0; j < num_profiles; j++) { + if (g_va_profiles[i] == profile_list[j]) { + profile = g_va_profiles[i]; + break; + } + } + + if (profile != VAProfileNone) + break; + } + + free(profile_list); + + if (profile == VAProfileNone) { + fprintf(stderr, "Can't find a supported profile\n"); + exit(-1); + } + + ctx->profile = profile; + + max_entrypoints = vaMaxNumEntrypoints(ctx->va_dpy); + entrypoint_list = malloc(max_entrypoints * sizeof(VAEntrypoint)); + vaQueryConfigEntrypoints(ctx->va_dpy, + ctx->profile, + entrypoint_list, + &num_entrypoints); + + for (entrypoint = 0; entrypoint < num_entrypoints; entrypoint++) { + if (entrypoint_list[entrypoint] == VAEntrypointEncSlice) + break; + } + + free(entrypoint_list); + + if (entrypoint == num_entrypoints) { + /* not find Slice entry point */ + fprintf(stderr, "Can't find the supported entrypoint\n"); + exit(-1); + } + + /* find out the format for the render target, and rate control mode */ + attrib_list[0].type = VAConfigAttribRTFormat; + attrib_list[1].type = VAConfigAttribRateControl; + attrib_list[2].type = VAConfigAttribEncPackedHeaders; + attrib_list[3].type = VAConfigAttribEncRateControlExt; + + vaGetConfigAttributes(ctx->va_dpy, + ctx->profile, + VAEntrypointEncSlice, + &attrib_list[0], + 4); + + if ((attrib_list[0].value & VA_RT_FORMAT_YUV420) == 0) { + /* not find desired YUV420 RT format */ + assert(0); + } + + if ((attrib_list[1].value & ctx->rate_control_mode) == 0) { + /* Can't find matched RC mode */ + fprintf(stderr, "RC mode %d isn't found, exit\n", ctx->rate_control_mode); + assert(0); + } + + if ((attrib_list[2].value & (VA_ENC_PACKED_HEADER_SEQUENCE | + VA_ENC_PACKED_HEADER_PICTURE | + VA_ENC_PACKED_HEADER_SLICE | + VA_ENC_PACKED_HEADER_RAW_DATA)) == 0) { + /* Can't find matched PACKED HEADER mode */ + assert(0); + } + + if (attrib_list[3].value == VA_ATTRIB_NOT_SUPPORTED) { + ctx->layer_brc = 0; // force to 0 + } else { + VAConfigAttribValEncRateControlExt *val = (VAConfigAttribValEncRateControlExt *)&attrib_list[3].value; + + if (!val->bits.max_num_temporal_layers_minus1) { + fprintf(stderr, "Do not support temporal layer\n"); + assert(0); + } + + if (ctx->hierarchical_levels > (val->bits.max_num_temporal_layers_minus1 + 1)) { + fprintf(stderr, "Over the maximum number of the supported layers\n"); + assert(0); + } + + if (!val->bits.temporal_layer_bitrate_control_flag) + ctx->layer_brc = 0; // force to 0 + } + + attrib_list[0].value = VA_RT_FORMAT_YUV420; /* set to desired RT format */ + attrib_list[1].value = ctx->rate_control_mode; /* set to desired RC mode */ + attrib_list[2].value = (VA_ENC_PACKED_HEADER_SEQUENCE | + VA_ENC_PACKED_HEADER_PICTURE | + VA_ENC_PACKED_HEADER_SLICE | + VA_ENC_PACKED_HEADER_RAW_DATA); + + va_status = vaCreateConfig(ctx->va_dpy, + ctx->profile, + VAEntrypointEncSlice, + attrib_list, + 3, + &ctx->config_id); + CHECK_VASTATUS(va_status, "vaCreateConfig"); + + /* Create a context for this decode pipe */ + va_status = vaCreateContext(ctx->va_dpy, + ctx->config_id, + ctx->width, + ctx->height, + VA_PROGRESSIVE, + 0, + 0, + &ctx->context_id); + CHECK_VASTATUS(va_status, "vaCreateContext"); + + va_status = vaCreateSurfaces(ctx->va_dpy, + VA_RT_FORMAT_YUV420, + ctx->width, + ctx->height, + src_surfaces, + NUM_SURFACES, + NULL, + 0); + CHECK_VASTATUS(va_status, "vaCreateSurfaces"); + + va_status = vaCreateSurfaces(ctx->va_dpy, + VA_RT_FORMAT_YUV420, + ctx->width, + ctx->height, + rec_surfaces, + NUM_SURFACES, + NULL, + 0); + CHECK_VASTATUS(va_status, "vaCreateSurfaces"); +} + +static void +svcenc_sei_init(struct svcenc_context *ctx) +{ + if (!(ctx->rate_control_mode & VA_RC_CBR)) + return; + + /* it comes for the bps defined in SPS */ + ctx->i_initial_cpb_removal_delay = 2 * 90000; + ctx->i_initial_cpb_removal_delay_offset = 2 * 90000; + + ctx->i_cpb_removal_delay = 2; + ctx->i_initial_cpb_removal_delay_length = 24; + ctx->i_cpb_removal_delay_length = 24; + ctx->i_dpb_output_delay_length = 24; + ctx->time_offset_length = 24; + + ctx->prev_idr_cpb_removal = ctx->i_initial_cpb_removal_delay / 90000; + ctx->current_idr_cpb_removal = ctx->prev_idr_cpb_removal; + ctx->current_cpb_removal = 0; + ctx->idr_frame_num = 0; +} + +static void +svcenc_init(struct svcenc_context *ctx) +{ + int i; + + ctx->frame_data_buffer = (unsigned char *)malloc(ctx->frame_size); + ctx->num_remainder_bframes = 0; + ctx->max_num_ref_frames = ctx->gop_size; + ctx->num_ref_frames = 0; + + switch (ctx->gop_size) { + case 1: + ctx->hierarchical_levels = 1; + break; + + case 2: + ctx->hierarchical_levels = 2; + break; + + case 4: + ctx->hierarchical_levels = 3; + break; + + case 8: + ctx->hierarchical_levels = 4; + break; + + default: + /* Never get here !!! */ + assert(0); + break; + } + + if (ctx->gop_type == 1) { // B + ctx->intra_period = 2 * ctx->gop_size; + ctx->ip_period = ctx->gop_size; + ctx->intra_idr_period = 0; // 0 means infinite + } else { + ctx->intra_period = 2 * ctx->gop_size; + ctx->ip_period = 1; + ctx->intra_idr_period = 8 * ctx->intra_period; + } + + ctx->framerate_per_100s = frame_rates[ctx->hierarchical_levels - 1] * 100; + + ctx->upload_task_header = NULL; + ctx->upload_task_tail = NULL; + + ctx->reordering_info[0] = ctx->reordering_info[1] = 0; + + ctx->seq_param_buf_id = VA_INVALID_ID; + ctx->pic_param_buf_id = VA_INVALID_ID; + ctx->packed_sei_scalability_info_header_param_buf_id = VA_INVALID_ID; + ctx->packed_sei_scalability_info_buf_id = VA_INVALID_ID; + ctx->packed_seq_header_param_buf_id = VA_INVALID_ID; + ctx->packed_seq_buf_id = VA_INVALID_ID; + ctx->packed_svc_seq_header_param_buf_id = VA_INVALID_ID; + ctx->packed_svc_seq_buf_id = VA_INVALID_ID; + ctx->packed_pic_header_param_buf_id = VA_INVALID_ID; + ctx->packed_pic_buf_id = VA_INVALID_ID; + ctx->codedbuf_buf_id = VA_INVALID_ID; + ctx->misc_parameter_layer_structure_buf_id = VA_INVALID_ID; + + for (i = 0; i < MAX_LAYERS; i++) { + ctx->misc_parameter_ratecontrol_buf_id[i] = VA_INVALID_ID; + ctx->misc_parameter_framerate_buf_id[i] = VA_INVALID_ID; + } + + ctx->misc_parameter_hrd_buf_id = VA_INVALID_ID; + ctx->packed_sei_header_param_buf_id = VA_INVALID_ID; + ctx->packed_sei_buf_id = VA_INVALID_ID; + + for (i = 0; i < MAX_SLICES; i++) { + ctx->packed_slice_header_param_buf_id[i] = VA_INVALID_ID; + ctx->packed_slice_header_data_buf_id[i] = VA_INVALID_ID; + ctx->packed_prefix_nal_unit_param_buf_id[i] = VA_INVALID_ID; + ctx->packed_prefix_nal_unit_data_buf_id[i] = VA_INVALID_ID; + } + + svcenc_va_init(ctx); + + /* update the next surface status */ + ctx->next_svcenc_surface.slot_in_surfaces = 0; + ctx->next_svcenc_surface.coding_order = 0; + ctx->next_svcenc_surface.display_order = 0; + ctx->next_svcenc_surface.temporal_id = 0; + ctx->next_svcenc_surface.frame_num = 0; + ctx->next_svcenc_surface.poc = 0; + ctx->next_svcenc_surface.picture_type = VAEncPictureTypeIntra; + ctx->next_svcenc_surface.is_intra = 1; + ctx->next_svcenc_surface.is_idr = 1; + ctx->next_svcenc_surface.is_ref = 1; + ctx->next_svcenc_surface.rec_surface = rec_surfaces[ctx->next_svcenc_surface.slot_in_surfaces]; + memset(src_surface_status, SRC_SURFACE_IN_STORAGE, sizeof(src_surface_status)); + + svcenc_sei_init(ctx); +} + +static void +svcenc_run(struct svcenc_context *ctx) +{ + int coding_order = 0; + svcenc_surface current_surface; + + pthread_create(&ctx->upload_thread, NULL, upload_task_thread, ctx); + current_surface = ctx->next_svcenc_surface; + upload_task_queue(ctx, current_surface.display_order, current_surface.slot_in_surfaces); + + while (coding_order < ctx->num_pictures) { + if (coding_order < (ctx->num_pictures - 1)) { + if (ctx->gop_type == 1) { + update_next_picture_info(ctx); + } else { + update_next_picture_info_pgop(ctx); + } + + assert(current_surface.display_order != ctx->next_svcenc_surface.display_order); + upload_task_queue(ctx, ctx->next_svcenc_surface.display_order, ctx->next_svcenc_surface.slot_in_surfaces); + } + + while (src_surface_status[current_surface.slot_in_surfaces] != SRC_SURFACE_IN_ENCODING) + usleep(1); + + encode_picture(ctx, ¤t_surface); + + if (coding_order < (ctx->num_pictures - 1)) + current_surface = ctx->next_svcenc_surface; + + coding_order++; + fprintf(stderr, "\r %d/%d ...", coding_order, ctx->num_pictures); + fflush(stdout); + } + + pthread_join(ctx->upload_thread, NULL); +} + +static void +svcenc_va_end(struct svcenc_context *ctx) +{ + vaDestroySurfaces(ctx->va_dpy, src_surfaces, NUM_SURFACES); + vaDestroySurfaces(ctx->va_dpy, rec_surfaces, NUM_SURFACES); + + vaDestroyContext(ctx->va_dpy, ctx->context_id); + vaDestroyConfig(ctx->va_dpy, ctx->config_id); + vaTerminate(ctx->va_dpy); + va_close_display(ctx->va_dpy); +} + +static void +svcenc_end(struct svcenc_context *ctx) +{ + free(ctx->frame_data_buffer); + ctx->frame_data_buffer = NULL; + svcenc_va_end(ctx); +} + +int +main(int argc, char *argv[]) +{ + struct svcenc_context ctx; + struct timeval tpstart, tpend; + float timeuse; + + gettimeofday(&tpstart, NULL); + + memset(&ctx, 0, sizeof(ctx)); + parse_args(&ctx, argc, argv); + + svcenc_init(&ctx); + svcenc_run(&ctx); + svcenc_end(&ctx); + + gettimeofday(&tpend, NULL); + timeuse = 1000000 * (tpend.tv_sec - tpstart.tv_sec) + tpend.tv_usec - tpstart.tv_usec; + timeuse /= 1000000; + fprintf(stderr, "\ndone!\n"); + fprintf(stderr, "encode %d frames in %f secondes, FPS is %.1f\n", ctx.num_pictures, timeuse, ctx.num_pictures / timeuse); + + svcenc_exit(&ctx, 0); + + return 0; +} diff --git a/va/wayland/wayland-drm-client-protocol.h b/va/wayland/wayland-drm-client-protocol.h index cba188e..7f2789b 100644 --- a/va/wayland/wayland-drm-client-protocol.h +++ b/va/wayland/wayland-drm-client-protocol.h @@ -1,7 +1,26 @@ -/* +/* Generated by wayland-scanner 1.11.90 */ + +#ifndef DRM_CLIENT_PROTOCOL_H +#define DRM_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_drm The drm protocol + * @section page_ifaces_drm Interfaces + * - @subpage page_iface_wl_drm - + * @section page_copyright_drm Copyright + *
+ *
  * Copyright © 2008-2011 Kristian Høgsberg
  * Copyright © 2010-2011 Intel Corporation
- * 
+ *
  * Permission to use, copy, modify, distribute, and sell this
  * software and its documentation for any purpose is hereby granted
  * without fee, provided that\n the above copyright notice appear in
@@ -13,7 +32,7 @@
  * representations about the suitability of this software for any
  * purpose.  It is provided "as is" without express or implied
  * warranty.
- * 
+ *
  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
@@ -22,24 +41,19 @@
  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
  * THIS SOFTWARE.
+ * 
*/ - -#ifndef DRM_CLIENT_PROTOCOL_H -#define DRM_CLIENT_PROTOCOL_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include "wayland-client.h" - -struct wl_client; -struct wl_resource; - +struct wl_buffer; struct wl_drm; +/** + * @page page_iface_wl_drm wl_drm + * @section page_iface_wl_drm_api API + * See @ref iface_wl_drm. + */ +/** + * @defgroup iface_wl_drm The wl_drm interface + */ extern const struct wl_interface wl_drm_interface; #ifndef WL_DRM_ERROR_ENUM @@ -115,28 +129,30 @@ enum wl_drm_format { }; #endif /* WL_DRM_FORMAT_ENUM */ +/** + * @ingroup iface_wl_drm + * @struct wl_drm_listener + */ struct wl_drm_listener { /** - * device - device - * @name: name */ void (*device)(void *data, struct wl_drm *wl_drm, const char *name); /** - * format - format - * @format: format */ void (*format)(void *data, struct wl_drm *wl_drm, uint32_t format); /** - * authenticated - authenticated */ void (*authenticated)(void *data, struct wl_drm *wl_drm); }; +/** + * @ingroup wl_drm_iface + */ static inline int wl_drm_add_listener(struct wl_drm *wl_drm, const struct wl_drm_listener *listener, void *data) @@ -145,28 +161,53 @@ wl_drm_add_listener(struct wl_drm *wl_drm, (void (**)(void)) listener, data); } -#define WL_DRM_AUTHENTICATE 0 -#define WL_DRM_CREATE_BUFFER 1 -#define WL_DRM_CREATE_PLANAR_BUFFER 2 +#define WL_DRM_AUTHENTICATE 0 +#define WL_DRM_CREATE_BUFFER 1 +#define WL_DRM_CREATE_PLANAR_BUFFER 2 + +/** + * @ingroup iface_wl_drm + */ +#define WL_DRM_AUTHENTICATE_SINCE_VERSION 1 +/** + * @ingroup iface_wl_drm + */ +#define WL_DRM_CREATE_BUFFER_SINCE_VERSION 1 +/** + * @ingroup iface_wl_drm + */ +#define WL_DRM_CREATE_PLANAR_BUFFER_SINCE_VERSION 1 +/** @ingroup iface_wl_drm */ static inline void wl_drm_set_user_data(struct wl_drm *wl_drm, void *user_data) { wl_proxy_set_user_data((struct wl_proxy *) wl_drm, user_data); } +/** @ingroup iface_wl_drm */ static inline void * wl_drm_get_user_data(struct wl_drm *wl_drm) { return wl_proxy_get_user_data((struct wl_proxy *) wl_drm); } +static inline uint32_t +wl_drm_get_version(struct wl_drm *wl_drm) +{ + return wl_proxy_get_version((struct wl_proxy *) wl_drm); +} + +/** @ingroup iface_wl_drm */ static inline void wl_drm_destroy(struct wl_drm *wl_drm) { wl_proxy_destroy((struct wl_proxy *) wl_drm); } +/** + * @ingroup iface_wl_drm + */ static inline void wl_drm_authenticate(struct wl_drm *wl_drm, uint32_t id) { @@ -174,34 +215,30 @@ wl_drm_authenticate(struct wl_drm *wl_drm, uint32_t id) WL_DRM_AUTHENTICATE, id); } +/** + * @ingroup iface_wl_drm + */ static inline struct wl_buffer * wl_drm_create_buffer(struct wl_drm *wl_drm, uint32_t name, int32_t width, int32_t height, uint32_t stride, uint32_t format) { struct wl_proxy *id; - id = wl_proxy_create((struct wl_proxy *) wl_drm, - &wl_buffer_interface); - if (!id) - return NULL; - - wl_proxy_marshal((struct wl_proxy *) wl_drm, - WL_DRM_CREATE_BUFFER, id, name, width, height, stride, format); + id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_drm, + WL_DRM_CREATE_BUFFER, &wl_buffer_interface, NULL, name, width, height, stride, format); return (struct wl_buffer *) id; } +/** + * @ingroup iface_wl_drm + */ static inline struct wl_buffer * wl_drm_create_planar_buffer(struct wl_drm *wl_drm, uint32_t name, int32_t width, int32_t height, uint32_t format, int32_t offset0, int32_t stride0, int32_t offset1, int32_t stride1, int32_t offset2, int32_t stride2) { struct wl_proxy *id; - id = wl_proxy_create((struct wl_proxy *) wl_drm, - &wl_buffer_interface); - if (!id) - return NULL; - - wl_proxy_marshal((struct wl_proxy *) wl_drm, - WL_DRM_CREATE_PLANAR_BUFFER, id, name, width, height, format, offset0, stride0, offset1, stride1, offset2, stride2); + id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_drm, + WL_DRM_CREATE_PLANAR_BUFFER, &wl_buffer_interface, NULL, name, width, height, format, offset0, stride0, offset1, stride1, offset2, stride2); return (struct wl_buffer *) id; }