blob: cf5b3b56c0604d04cfa713441746be315c7b3681 [file] [log] [blame]
/*
* 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <time.h>
#include <math.h>
#include <pthread.h>
#include <va/va.h>
#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,
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));
assert(bs->buffer);
}
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[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[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));
assert(task);
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 <width> <height> <ifile> <ofile> [options] \n", program);
fprintf(stderr, "\t<width>\t\tThe base picture width\n");
fprintf(stderr, "\t<height>\tThe base picture height\n");
fprintf(stderr, "\t<ifiles>\tThe input YUV I420 file\n");
fprintf(stderr, "\t<ofile>\t\tThe output H.264/SVC file\n\n");
fprintf(stderr, "\tAvailable options:\n");
fprintf(stderr, "\t--gop <value>\t\tGOP size, default is 8\n");
fprintf(stderr, "\t--goptype <value>\tGOP hierarchical type, 0 is P hierarchical GOP, 1 is B hierarchical GOP, default is 0\n");
fprintf(stderr, "\t--brcmode <value>\tBRC mode, 0 is CQP mode, otherwise CBR mode, default mode is CQP\n");
fprintf(stderr, "\t--brclayer<value>\tDisable/Enalbe BRC per temporal layer, default is disable(0)\n");
fprintf(stderr, "\t--bitrate <value>\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, "<base width> and <base height> 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->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;
}
}
assert(found != -1);
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;
memset(RefPicList0, 0, sizeof(RefPicList0));
memset(RefPicList1, 0, sizeof(RefPicList1));
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 = 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_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:
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;
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));
assert(profile_list);
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));
assert(entrypoint_list);
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, &current_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;
}