--- /dev/null
+/*
+ * gstvaapiencoder_h265.c - H.265 encoder
+ *
+ * Copyright (C) 2015 Intel Corporation
+ * Author: Sreerenj Balachandran <sreerenj.balachandran@intel.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+/* GValueArray has deprecated without providing an alternative in glib >= 2.32
+ * See https://bugzilla.gnome.org/show_bug.cgi?id=667228
+ */
+#define GLIB_DISABLE_DEPRECATION_WARNINGS
+
+#include "sysdeps.h"
+#include <math.h>
+#include <va/va.h>
+#include <va/va_enc_hevc.h>
+#include <gst/base/gstbitwriter.h>
+#include <gst/codecparsers/gsth265parser.h>
+#include "gstvaapicompat.h"
+#include "gstvaapiencoder_priv.h"
+#include "gstvaapiencoder_h265.h"
+#include "gstvaapiutils_h265.h"
+#include "gstvaapiutils_h265_priv.h"
+#include "gstvaapicodedbufferproxy_priv.h"
+#include "gstvaapisurface.h"
+
+#define DEBUG 1
+#include "gstvaapidebug.h"
+
+/* Define the maximum IDR period */
+#define MAX_IDR_PERIOD 512
+
+/* Define default rate control mode ("constant-qp") */
+#define DEFAULT_RATECONTROL GST_VAAPI_RATECONTROL_CQP
+
+/* Supported set of VA rate controls, within this implementation */
+#define SUPPORTED_RATECONTROLS \
+ (GST_VAAPI_RATECONTROL_MASK (CQP))
+
+/* Supported set of tuning options, within this implementation */
+#define SUPPORTED_TUNE_OPTIONS \
+ (GST_VAAPI_ENCODER_TUNE_MASK (NONE))
+
+/* Supported set of VA packed headers, within this implementation */
+#define SUPPORTED_PACKED_HEADERS \
+ (VA_ENC_PACKED_HEADER_SEQUENCE | \
+ VA_ENC_PACKED_HEADER_PICTURE | \
+ VA_ENC_PACKED_HEADER_SLICE)
+
+typedef struct
+{
+ GstVaapiSurfaceProxy *pic;
+ guint poc;
+} GstVaapiEncoderH265Ref;
+
+typedef enum
+{
+ GST_VAAPI_ENC_H265_REORD_NONE = 0,
+ GST_VAAPI_ENC_H265_REORD_DUMP_FRAMES = 1,
+ GST_VAAPI_ENC_H265_REORD_WAIT_FRAMES = 2
+} GstVaapiEncH265ReorderState;
+
+typedef struct _GstVaapiH265RefPool
+{
+ GQueue ref_list;
+ guint max_ref_frames;
+ guint max_reflist0_count;
+ guint max_reflist1_count;
+} GstVaapiH265RefPool;
+
+typedef struct _GstVaapiH265ReorderPool
+{
+ GQueue reorder_frame_list;
+ guint reorder_state;
+ guint frame_index;
+ guint cur_present_index;
+} GstVaapiH265ReorderPool;
+
+/* ------------------------------------------------------------------------- */
+/* --- H.265 Encoder --- */
+/* ------------------------------------------------------------------------- */
+
+#define GST_VAAPI_ENCODER_H265_CAST(encoder) \
+ ((GstVaapiEncoderH265 *)(encoder))
+
+struct _GstVaapiEncoderH265
+{
+ GstVaapiEncoder parent_instance;
+
+ GstVaapiProfile profile;
+ GstVaapiTierH265 tier;
+ GstVaapiLevelH265 level;
+ guint8 profile_idc;
+ guint8 max_profile_idc;
+ guint8 hw_max_profile_idc;
+ guint8 level_idc;
+ guint32 idr_period;
+ guint32 init_qp;
+ guint32 min_qp;
+ guint32 num_slices;
+ guint32 num_bframes;
+ guint32 ctu_width; /* CTU == Coding Tree Unit */
+ guint32 ctu_height;
+ guint32 luma_width;
+ guint32 luma_height;
+ GstClockTime cts_offset;
+ gboolean config_changed;
+
+ /* maximum required size of the decoded picture buffer */
+ guint32 max_dec_pic_buffering;
+ /* maximum allowed number of pictures that can precede any picture in
+ * the CVS in decoding order and follow that picture in output order */
+ guint32 max_num_reorder_pics;
+
+ /* frame, poc */
+ guint32 max_pic_order_cnt;
+ guint32 log2_max_pic_order_cnt;
+ guint32 idr_num;
+
+ GstBuffer *vps_data;
+ GstBuffer *sps_data;
+ GstBuffer *pps_data;
+
+ guint bitrate_bits; // bitrate (bits)
+
+ /* Crop rectangle */
+ guint conformance_window_flag:1;
+ guint32 conf_win_left_offset;
+ guint32 conf_win_right_offset;
+ guint32 conf_win_top_offset;
+ guint32 conf_win_bottom_offset;
+
+ GstVaapiH265RefPool ref_pool;
+ GstVaapiH265ReorderPool reorder_pool;
+ guint first_slice_segment_in_pic_flag:1;
+ guint sps_temporal_mvp_enabled_flag:1;
+ guint sample_adaptive_offset_enabled_flag:1;
+};
+
+static inline gboolean
+_poc_greater_than (guint poc1, guint poc2, guint max_poc)
+{
+ return (((poc1 - poc2) & (max_poc - 1)) < max_poc / 2);
+}
+
+/* Get slice_type value for H.265 specification */
+static guint8
+h265_get_slice_type (GstVaapiPictureType type)
+{
+ switch (type) {
+ case GST_VAAPI_PICTURE_TYPE_I:
+ return GST_H265_I_SLICE;
+ case GST_VAAPI_PICTURE_TYPE_P:
+ return GST_H265_P_SLICE;
+ case GST_VAAPI_PICTURE_TYPE_B:
+ return GST_H265_B_SLICE;
+ default:
+ break;
+ }
+ return -1;
+}
+
+/* Get log2_max_pic_order_cnt value for H.265 specification */
+static guint
+h265_get_log2_max_pic_order_cnt (guint num)
+{
+ guint ret = 0;
+
+ while (num) {
+ ++ret;
+ num >>= 1;
+ }
+ if (ret <= 4)
+ ret = 4;
+ else if (ret > 10)
+ ret = 10;
+ /* must be greater than 4 */
+ return ret;
+}
+
+/* ------------------------------------------------------------------------- */
+/* --- H.265 Bitstream Writer --- */
+/* ------------------------------------------------------------------------- */
+
+#define WRITE_UINT32(bs, val, nbits) do { \
+ if (!gst_bit_writer_put_bits_uint32 (bs, val, nbits)) { \
+ GST_WARNING ("failed to write uint32, nbits: %d", nbits); \
+ goto bs_error; \
+ } \
+ } while (0)
+
+#define WRITE_UE(bs, val) do { \
+ if (!bs_write_ue (bs, val)) { \
+ GST_WARNING ("failed to write ue(v)"); \
+ goto bs_error; \
+ } \
+ } while (0)
+
+#define WRITE_SE(bs, val) do { \
+ if (!bs_write_se (bs, val)) { \
+ GST_WARNING ("failed to write se(v)"); \
+ goto bs_error; \
+ } \
+ } while (0)
+
+/* Write an unsigned integer Exp-Golomb-coded syntax element. i.e. ue(v) */
+static gboolean
+bs_write_ue (GstBitWriter * bs, guint32 value)
+{
+ guint32 size_in_bits = 0;
+ guint32 tmp_value = ++value;
+
+ while (tmp_value) {
+ ++size_in_bits;
+ tmp_value >>= 1;
+ }
+ if (size_in_bits > 1
+ && !gst_bit_writer_put_bits_uint32 (bs, 0, size_in_bits - 1))
+ return FALSE;
+ if (!gst_bit_writer_put_bits_uint32 (bs, value, size_in_bits))
+ return FALSE;
+ return TRUE;
+}
+
+/* Write a signed integer Exp-Golomb-coded syntax element. i.e. se(v) */
+static gboolean
+bs_write_se (GstBitWriter * bs, gint32 value)
+{
+ guint32 new_val;
+
+ if (value <= 0)
+ new_val = -(value << 1);
+ else
+ new_val = (value << 1) - 1;
+
+ if (!bs_write_ue (bs, new_val))
+ return FALSE;
+ return TRUE;
+}
+
+/* Write the NAL unit header */
+static gboolean
+bs_write_nal_header (GstBitWriter * bs, guint32 nal_unit_type)
+{
+ guint8 nuh_layer_id = 0;
+ guint8 nuh_temporal_id_plus1 = 1;
+
+ WRITE_UINT32 (bs, 0, 1);
+ WRITE_UINT32 (bs, nal_unit_type, 6);
+ WRITE_UINT32 (bs, nuh_layer_id, 6);
+ WRITE_UINT32 (bs, nuh_temporal_id_plus1, 3);
+
+ return TRUE;
+
+ /* ERRORS */
+bs_error:
+ {
+ GST_WARNING ("failed to write NAL unit header");
+ return FALSE;
+ }
+}
+
+/* Write the NAL unit trailing bits */
+static gboolean
+bs_write_trailing_bits (GstBitWriter * bs)
+{
+ if (!gst_bit_writer_put_bits_uint32 (bs, 1, 1))
+ goto bs_error;
+ gst_bit_writer_align_bytes_unchecked (bs, 0);
+ return TRUE;
+
+ /* ERRORS */
+bs_error:
+ {
+ GST_WARNING ("failed to write NAL unit trailing bits");
+ return FALSE;
+ }
+}
+
+/* Write profile_tier_level() */
+static gboolean
+bs_write_profile_tier_level (GstBitWriter * bs,
+ const VAEncSequenceParameterBufferHEVC * seq_param)
+{
+ guint i;
+ /* general_profile_space */
+ WRITE_UINT32 (bs, 0, 2);
+ /* general_tier_flag */
+ WRITE_UINT32 (bs, seq_param->general_tier_flag, 1);
+ /* general_profile_idc */
+ WRITE_UINT32 (bs, seq_param->general_profile_idc, 5);
+ /* general_profile_compatibility_flag[32] */
+ for (i = 0; i < 32; i++) {
+ if (i == 1 || i == 2)
+ WRITE_UINT32 (bs, 1, 1);
+ else
+ WRITE_UINT32 (bs, 0, 1);
+ }
+ /* general_progressive_source_flag */
+ WRITE_UINT32 (bs, 1, 1);
+ /* general_interlaced_source_flag */
+ WRITE_UINT32 (bs, 0, 1);
+ /* general_non_packed_constraint_flag */
+ WRITE_UINT32 (bs, 0, 1);
+ /* general_frame_only_constraint_flag */
+ WRITE_UINT32 (bs, 1, 1);
+ /* general_reserved_zero_44bits */
+ for (i = 0; i < 44; i++)
+ WRITE_UINT32 (bs, 0, 1);
+ /* general_level_idc */
+ WRITE_UINT32 (bs, seq_param->general_level_idc, 8);
+
+ return TRUE;
+
+ /* ERRORS */
+bs_error:
+ {
+ GST_WARNING ("failed to write Profile Tier Level");
+ return FALSE;
+ }
+}
+
+/* Write an VPS NAL unit */
+static gboolean
+bs_write_vps_data (GstBitWriter * bs, GstVaapiEncoderH265 * encoder,
+ GstVaapiEncPicture * picture,
+ const VAEncSequenceParameterBufferHEVC * seq_param, GstVaapiProfile profile)
+{
+ guint32 video_parameter_set_id = 0;
+ guint32 vps_max_layers_minus1 = 0;
+ guint32 vps_max_sub_layers_minus1 = 0;
+ guint32 vps_temporal_id_nesting_flag = 1;
+ guint32 vps_sub_layer_ordering_info_present_flag = 0;
+ guint32 vps_max_latency_increase_plus1 = 0;
+ guint32 vps_max_layer_id = 0;
+ guint32 vps_num_layer_sets_minus1 = 0;
+ guint32 vps_timing_info_present_flag = 0;
+ guint32 vps_extension_flag = 0;
+
+ /* video_parameter_set_id */
+ WRITE_UINT32 (bs, video_parameter_set_id, 4);
+ /* vps_reserved_three_2bits */
+ WRITE_UINT32 (bs, 3, 2);
+ /* vps_max_layers_minus1 */
+ WRITE_UINT32 (bs, vps_max_layers_minus1, 6);
+ /* vps_max_sub_layers_minus1 */
+ WRITE_UINT32 (bs, vps_max_sub_layers_minus1, 3);
+ /* vps_temporal_id_nesting_flag */
+ WRITE_UINT32 (bs, vps_temporal_id_nesting_flag, 1);
+ /* vps_reserved_0xffff_16bits */
+ WRITE_UINT32 (bs, 0xffff, 16);
+
+ /* profile_tier_level */
+ bs_write_profile_tier_level (bs, seq_param);
+
+ /* vps_sub_layer_ordering_info_present_flag */
+ WRITE_UINT32 (bs, vps_sub_layer_ordering_info_present_flag, 1);
+ /* vps_max_dec_pic_buffering_minus1 */
+ WRITE_UE (bs, encoder->max_dec_pic_buffering - 1);
+ /* vps_max_num_reorder_pics */
+ WRITE_UE (bs, encoder->max_num_reorder_pics);
+ /* vps_max_latency_increase_plus1 */
+ WRITE_UE (bs, vps_max_latency_increase_plus1);
+ /* vps_max_layer_id */
+ WRITE_UINT32 (bs, vps_max_layer_id, 6);
+ /* vps_num_layer_sets_minus1 */
+ WRITE_UE (bs, vps_num_layer_sets_minus1);
+ /* vps_timing_info_present_flag */
+ WRITE_UINT32 (bs, vps_timing_info_present_flag, 1);
+ /* vps_extension_flag */
+ WRITE_UINT32 (bs, vps_extension_flag, 1);
+
+ return TRUE;
+
+ /* ERRORS */
+bs_error:
+ {
+ GST_WARNING ("failed to write VPS NAL unit");
+ return FALSE;
+ }
+}
+
+static gboolean
+bs_write_vps (GstBitWriter * bs, GstVaapiEncoderH265 * encoder,
+ GstVaapiEncPicture * picture,
+ const VAEncSequenceParameterBufferHEVC * seq_param, GstVaapiProfile profile)
+{
+ if (!bs_write_vps_data (bs, encoder, picture, seq_param, profile))
+ return FALSE;
+
+ /* rbsp_trailing_bits */
+ bs_write_trailing_bits (bs);
+
+ return FALSE;
+}
+
+/* Write an SPS NAL unit */
+static gboolean
+bs_write_sps_data (GstBitWriter * bs, GstVaapiEncoderH265 * encoder,
+ GstVaapiEncPicture * picture,
+ const VAEncSequenceParameterBufferHEVC * seq_param, GstVaapiProfile profile)
+{
+ guint32 video_parameter_set_id = 0;
+ guint32 max_sub_layers_minus1 = 0;
+ guint32 temporal_id_nesting_flag = 1;
+ guint32 seq_parameter_set_id = 0;
+ guint32 sps_sub_layer_ordering_info_present_flag = 0;
+ guint32 sps_max_latency_increase_plus1 = 0;
+ guint32 num_short_term_ref_pic_sets = 0;
+ guint32 long_term_ref_pics_present_flag = 0;
+ guint32 sps_extension_flag = 0;
+
+ /* video_parameter_set_id */
+ WRITE_UINT32 (bs, video_parameter_set_id, 4);
+ /* max_sub_layers_minus1 */
+ WRITE_UINT32 (bs, max_sub_layers_minus1, 3);
+ /* temporal_id_nesting_flag */
+ WRITE_UINT32 (bs, temporal_id_nesting_flag, 1);
+
+ /* profile_tier_level */
+ bs_write_profile_tier_level (bs, seq_param);
+
+ /* seq_parameter_set_id */
+ WRITE_UE (bs, seq_parameter_set_id);
+ /* chroma_format_idc = 1, 4:2:0 */
+ WRITE_UE (bs, seq_param->seq_fields.bits.chroma_format_idc);
+ /* pic_width_in_luma_samples */
+ WRITE_UE (bs, seq_param->pic_width_in_luma_samples);
+ /* pic_height_in_luma_samples */
+ WRITE_UE (bs, seq_param->pic_height_in_luma_samples);
+
+ /* conformance_window_flag */
+ WRITE_UINT32 (bs, encoder->conformance_window_flag, 1);
+ if (encoder->conformance_window_flag) {
+ WRITE_UE (bs, encoder->conf_win_left_offset);
+ WRITE_UE (bs, encoder->conf_win_right_offset);
+ WRITE_UE (bs, encoder->conf_win_top_offset);
+ WRITE_UE (bs, encoder->conf_win_bottom_offset);
+ }
+
+ /* bit_depth_luma_minus8 */
+ WRITE_UE (bs, seq_param->seq_fields.bits.bit_depth_luma_minus8);
+ /* bit_depth_chroma_minus8 */
+ WRITE_UE (bs, seq_param->seq_fields.bits.bit_depth_chroma_minus8);
+ /* log2_max_pic_order_cnt_lsb_minus4 */
+ WRITE_UE (bs, encoder->log2_max_pic_order_cnt - 4);
+
+ /* sps_sub_layer_ordering_info_present_flag */
+ WRITE_UINT32 (bs, sps_sub_layer_ordering_info_present_flag, 1);
+ /* sps_max_dec_pic_buffering_minus1 */
+ WRITE_UE (bs, encoder->max_dec_pic_buffering - 1);
+ /* sps_max_num_reorder_pics */
+ WRITE_UE (bs, encoder->max_num_reorder_pics);
+ /* sps_max_latency_increase_plus1 */
+ WRITE_UE (bs, sps_max_latency_increase_plus1);
+
+ /* log2_min_luma_coding_block_size_minus3 */
+ WRITE_UE (bs, seq_param->log2_min_luma_coding_block_size_minus3);
+ /* log2_diff_max_min_luma_coding_block_size */
+ WRITE_UE (bs, seq_param->log2_diff_max_min_luma_coding_block_size);
+ /* log2_min_transform_block_size_minus2 */
+ WRITE_UE (bs, seq_param->log2_min_transform_block_size_minus2);
+ /* log2_diff_max_min_transform_block_size */
+ WRITE_UE (bs, seq_param->log2_diff_max_min_transform_block_size);
+ /* max_transform_hierarchy_depth_inter */
+ WRITE_UE (bs, seq_param->max_transform_hierarchy_depth_inter);
+ /*max_transform_hierarchy_depth_intra */
+ WRITE_UE (bs, seq_param->max_transform_hierarchy_depth_intra);
+
+ /* scaling_list_enabled_flag */
+ WRITE_UINT32 (bs, seq_param->seq_fields.bits.scaling_list_enabled_flag, 1);
+ /* amp_enabled_flag */
+ WRITE_UINT32 (bs, seq_param->seq_fields.bits.amp_enabled_flag, 1);
+ /* sample_adaptive_offset_enabled_flag */
+ WRITE_UINT32 (bs,
+ seq_param->seq_fields.bits.sample_adaptive_offset_enabled_flag, 1);
+ /* pcm_enabled_flag */
+ WRITE_UINT32 (bs, seq_param->seq_fields.bits.pcm_enabled_flag, 1);
+
+ /* num_short_term_ref_pic_sets */
+ WRITE_UE (bs, num_short_term_ref_pic_sets);
+
+ /* long_term_ref_pics_present_flag */
+ WRITE_UINT32 (bs, long_term_ref_pics_present_flag, 1);
+
+ /* sps_temporal_mvp_enabled_flag */
+ WRITE_UINT32 (bs, seq_param->seq_fields.bits.sps_temporal_mvp_enabled_flag,
+ 1);
+ /* strong_intra_smoothing_enabled_flag */
+ WRITE_UINT32 (bs,
+ seq_param->seq_fields.bits.strong_intra_smoothing_enabled_flag, 1);
+
+ /* vui_parameters_present_flag */
+ WRITE_UINT32 (bs, seq_param->vui_parameters_present_flag, 1);
+
+ /*--------------- Write VUI Parameters--------------- */
+ if (seq_param->vui_parameters_present_flag) {
+ gboolean vui_hrd_parameters_present_flag;
+ /* aspect_ratio_info_present_flag */
+ WRITE_UINT32 (bs,
+ seq_param->vui_fields.bits.aspect_ratio_info_present_flag, 1);
+ if (seq_param->vui_fields.bits.aspect_ratio_info_present_flag) {
+ WRITE_UINT32 (bs, seq_param->aspect_ratio_idc, 8);
+ if (seq_param->aspect_ratio_idc == 0xFF) {
+ WRITE_UINT32 (bs, seq_param->sar_width, 16);
+ WRITE_UINT32 (bs, seq_param->sar_height, 16);
+ }
+ }
+ /* overscan_info_present_flag */
+ WRITE_UINT32 (bs, 0, 1);
+ /* video_signal_type_present_flag */
+ WRITE_UINT32 (bs, 0, 1);
+ /* chroma_loc_info_present_flag */
+ WRITE_UINT32 (bs, 0, 1);
+ /* neutral_chroma_indication_flag */
+ WRITE_UINT32 (bs, seq_param->vui_fields.bits.neutral_chroma_indication_flag,
+ 1);
+ /* field_seq_flag */
+ WRITE_UINT32 (bs, seq_param->vui_fields.bits.field_seq_flag, 1);
+ /* frame_field_info_present_flag */
+ WRITE_UINT32 (bs, 0, 1);
+ /* default_display_window_flag */
+ WRITE_UINT32 (bs, 0, 1);
+
+ /* timing_info_present_flag */
+ WRITE_UINT32 (bs, seq_param->vui_fields.bits.vui_timing_info_present_flag,
+ 1);
+ if (seq_param->vui_fields.bits.vui_timing_info_present_flag) {
+ /* vui_num_units_in_tick */
+ WRITE_UINT32 (bs, seq_param->vui_num_units_in_tick, 32);
+ /* vui_time_scale */
+ WRITE_UINT32 (bs, seq_param->vui_time_scale, 32);
+ /* vui_poc_proportional_to_timing_flag */
+ WRITE_UINT32 (bs, 0, 1);
+
+ /* vui_hrd_parameters_present_flag */
+ vui_hrd_parameters_present_flag = seq_param->bits_per_second > 0;
+ vui_hrd_parameters_present_flag = FALSE; /* XXX: disabled for now */
+ WRITE_UINT32 (bs, vui_hrd_parameters_present_flag, 1);
+ }
+ /* bitstream_restriction_flag */
+ WRITE_UINT32 (bs, seq_param->vui_fields.bits.bitstream_restriction_flag, 1);
+ }
+ /* sps_extension_flag */
+ WRITE_UINT32 (bs, sps_extension_flag, 1);
+
+ return TRUE;
+
+ /* ERRORS */
+bs_error:
+ {
+ GST_WARNING ("failed to write SPS NAL unit");
+ return FALSE;
+ }
+}
+
+static gboolean
+bs_write_sps (GstBitWriter * bs, GstVaapiEncoderH265 * encoder,
+ GstVaapiEncPicture * picture,
+ const VAEncSequenceParameterBufferHEVC * seq_param, GstVaapiProfile profile)
+{
+ if (!bs_write_sps_data (bs, encoder, picture, seq_param, profile))
+ return FALSE;
+
+ /* rbsp_trailing_bits */
+ bs_write_trailing_bits (bs);
+
+ return FALSE;
+}
+
+/* Write a PPS NAL unit */
+static gboolean
+bs_write_pps (GstBitWriter * bs,
+ const VAEncPictureParameterBufferHEVC * pic_param)
+{
+ guint32 pic_parameter_set_id = 0;
+ guint32 seq_parameter_set_id = 0;
+ guint32 output_flag_present_flag = 0;
+ guint32 num_extra_slice_header_bits = 0;
+ guint32 cabac_init_present_flag = 0;
+ guint32 pps_slice_chroma_qp_offsets_present_flag = 0;
+ guint32 deblocking_filter_control_present_flag = 0;
+ guint32 lists_modification_present_flag = 0;
+ guint32 slice_segment_header_extension_present_flag = 0;
+ guint32 pps_extension_flag = 0;
+
+ /* pic_parameter_set_id */
+ WRITE_UE (bs, pic_parameter_set_id);
+ /* seq_parameter_set_id */
+ WRITE_UE (bs, seq_parameter_set_id);
+ /* dependent_slice_segments_enabled_flag */
+ WRITE_UINT32 (bs,
+ pic_param->pic_fields.bits.dependent_slice_segments_enabled_flag, 1);
+ /* output_flag_present_flag */
+ WRITE_UINT32 (bs, output_flag_present_flag, 1);
+ /* num_extra_slice_header_bits */
+ WRITE_UINT32 (bs, num_extra_slice_header_bits, 3);
+ /* sign_data_hiding_enabled_flag */
+ WRITE_UINT32 (bs, pic_param->pic_fields.bits.sign_data_hiding_enabled_flag,
+ 1);
+ /* cabac_init_present_flag */
+ WRITE_UINT32 (bs, cabac_init_present_flag, 1);
+ /* num_ref_idx_l0_default_active_minus1 */
+ WRITE_UE (bs, pic_param->num_ref_idx_l0_default_active_minus1);
+ /* num_ref_idx_l1_default_active_minus1 */
+ WRITE_UE (bs, pic_param->num_ref_idx_l1_default_active_minus1);
+ /* pic_init_qp_minus26 */
+ WRITE_SE (bs, pic_param->pic_init_qp - 26);
+ /* constrained_intra_pred_flag */
+ WRITE_UINT32 (bs, pic_param->pic_fields.bits.constrained_intra_pred_flag, 1);
+ /* transform_skip_enabled_flag */
+ WRITE_UINT32 (bs, pic_param->pic_fields.bits.transform_skip_enabled_flag, 1);
+ /* cu_qp_delta_enabled_flag */
+ WRITE_UINT32 (bs, pic_param->pic_fields.bits.cu_qp_delta_enabled_flag, 1);
+ /* diff_cu_qp_delta_depth */
+ if (pic_param->pic_fields.bits.cu_qp_delta_enabled_flag)
+ WRITE_UE (bs, pic_param->diff_cu_qp_delta_depth);
+
+ /* pps_cb_qp_offset */
+ WRITE_SE (bs, pic_param->pps_cb_qp_offset);
+ /* pps_cr_qp_offset */
+ WRITE_SE (bs, pic_param->pps_cr_qp_offset);
+ /* pps_slice_chroma_qp_offsets_present_flag */
+ WRITE_UINT32 (bs, pps_slice_chroma_qp_offsets_present_flag, 1);
+ /* weighted_pred_flag */
+ WRITE_UINT32 (bs, pic_param->pic_fields.bits.weighted_pred_flag, 1);
+ /* weighted_bipred_flag */
+ WRITE_UINT32 (bs, pic_param->pic_fields.bits.weighted_bipred_flag, 1);
+ /* transquant_bypass_enabled_flag */
+ WRITE_UINT32 (bs, pic_param->pic_fields.bits.transquant_bypass_enabled_flag,
+ 1);
+ /* tiles_enabled_flag */
+ WRITE_UINT32 (bs, pic_param->pic_fields.bits.tiles_enabled_flag, 1);
+ /* entropy_coding_sync_enabled_flag */
+ WRITE_UINT32 (bs, pic_param->pic_fields.bits.entropy_coding_sync_enabled_flag,
+ 1);
+ /* pps_loop_filter_across_slices_enabled_flag */
+ WRITE_UINT32 (bs,
+ pic_param->pic_fields.bits.pps_loop_filter_across_slices_enabled_flag, 1);
+ /* deblocking_filter_control_present_flag */
+ WRITE_UINT32 (bs, deblocking_filter_control_present_flag, 1);
+ /* pps_scaling_list_data_present_flag */
+ WRITE_UINT32 (bs, pic_param->pic_fields.bits.scaling_list_data_present_flag,
+ 1);
+ /* lists_modification_present_flag */
+ WRITE_UINT32 (bs, lists_modification_present_flag, 1);
+ /* log2_parallel_merge_level_minus2 */
+ WRITE_UE (bs, pic_param->log2_parallel_merge_level_minus2);
+ /* slice_segment_header_extension_present_flag */
+ WRITE_UINT32 (bs, slice_segment_header_extension_present_flag, 1);
+ /* pps_extension_flag */
+ WRITE_UINT32 (bs, pps_extension_flag, 1);
+
+ /* rbsp_trailing_bits */
+ bs_write_trailing_bits (bs);
+
+ return TRUE;
+
+ /* ERRORS */
+bs_error:
+ {
+ GST_WARNING ("failed to write PPS NAL unit");
+ return FALSE;
+ }
+}
+
+/* Write a Slice NAL unit */
+static gboolean
+bs_write_slice (GstBitWriter * bs,
+ const VAEncSliceParameterBufferHEVC * slice_param,
+ GstVaapiEncoderH265 * encoder, GstVaapiEncPicture * picture,
+ guint8 nal_unit_type)
+{
+ const VAEncPictureParameterBufferHEVC *const pic_param = picture->param;
+
+ guint8 no_output_of_prior_pics_flag = 0;
+ guint8 dependent_slice_segment_flag = 0;
+ guint8 short_term_ref_pic_set_sps_flag = 0;
+ guint8 num_ref_idx_active_override_flag = 0;
+ guint8 slice_deblocking_filter_disabled_flag = 0;
+
+ /* first_slice_segment_in_pic_flag */
+ WRITE_UINT32 (bs, encoder->first_slice_segment_in_pic_flag, 1);
+
+ /* Fixme: For all IRAP pics */
+ /* no_output_of_prior_pics_flag */
+ if (GST_VAAPI_ENC_PICTURE_IS_IDR (picture))
+ WRITE_UINT32 (bs, no_output_of_prior_pics_flag, 1);
+
+ /* slice_pic_parameter_set_id */
+ WRITE_UE (bs, slice_param->slice_pic_parameter_set_id);
+
+ /* slice_segment_address , bits_size = Ceil(Log2(PicSizeInCtbsY)) */
+ if (!encoder->first_slice_segment_in_pic_flag) {
+ guint pic_size_ctb = encoder->ctu_width * encoder->ctu_height;
+ guint bits_size = (guint) ceil ((log2 (pic_size_ctb)));
+ WRITE_UINT32 (bs, slice_param->slice_segment_address, bits_size);
+ }
+
+ if (!dependent_slice_segment_flag) {
+ /* slice_type */
+ WRITE_UE (bs, slice_param->slice_type);
+
+ if (!pic_param->pic_fields.bits.idr_pic_flag) {
+ /* slice_pic_order_cnt_lsb */
+ WRITE_UINT32 (bs, picture->poc, encoder->log2_max_pic_order_cnt);
+ /* short_term_ref_pic_set_sps_flag */
+ WRITE_UINT32 (bs, short_term_ref_pic_set_sps_flag, 1);
+
+ /*---------- Write short_term_ref_pic_set(0) ----------- */
+ {
+ guint num_positive_pics = 0, num_negative_pics = 0;
+ guint delta_poc_s0_minus1 = 0, delta_poc_s1_minus1 = 0;
+ guint used_by_curr_pic_s0_flag = 0, used_by_curr_pic_s1_flag = 0;
+
+ if (picture->type == GST_VAAPI_PICTURE_TYPE_P) {
+ num_negative_pics = 1;
+ num_positive_pics = 0;
+ delta_poc_s0_minus1 =
+ picture->poc - slice_param->ref_pic_list0[0].pic_order_cnt - 1;
+ used_by_curr_pic_s0_flag = 1;
+ delta_poc_s1_minus1 = 0;
+ used_by_curr_pic_s1_flag = 0;
+ }
+ if (picture->type == GST_VAAPI_PICTURE_TYPE_B) {
+ num_negative_pics = 1;
+ num_positive_pics = 1;
+ delta_poc_s0_minus1 =
+ picture->poc - slice_param->ref_pic_list0[0].pic_order_cnt - 1;
+ used_by_curr_pic_s0_flag = 1;
+ delta_poc_s1_minus1 =
+ slice_param->ref_pic_list1[0].pic_order_cnt - picture->poc - 1;
+ used_by_curr_pic_s1_flag = 1;
+ }
+
+ /* num_negative_pics */
+ WRITE_UE (bs, num_negative_pics);
+ /* num_positive_pics */
+ WRITE_UE (bs, num_positive_pics);
+ if (num_negative_pics) {
+ /* delta_poc_s0_minus1 */
+ WRITE_UE (bs, delta_poc_s0_minus1);
+ /* used_by_curr_pic_s0_flag */
+ WRITE_UINT32 (bs, used_by_curr_pic_s0_flag, 1);
+ }
+ if (num_positive_pics) {
+ /* delta_poc_s1_minus1 */
+ WRITE_UE (bs, delta_poc_s1_minus1);
+ /* used_by_curr_pic_s1_flag */
+ WRITE_UINT32 (bs, used_by_curr_pic_s1_flag, 1);
+ }
+ }
+
+ /* slice_temporal_mvp_enabled_flag */
+ if (encoder->sps_temporal_mvp_enabled_flag)
+ WRITE_UINT32 (bs,
+ slice_param->slice_fields.bits.slice_temporal_mvp_enabled_flag, 1);
+ }
+
+ if (encoder->sample_adaptive_offset_enabled_flag) {
+ WRITE_UINT32 (bs, slice_param->slice_fields.bits.slice_sao_luma_flag, 1);
+ WRITE_UINT32 (bs, slice_param->slice_fields.bits.slice_sao_chroma_flag,
+ 1);
+ }
+
+ if (slice_param->slice_type == GST_H265_P_SLICE ||
+ slice_param->slice_type == GST_H265_B_SLICE) {
+ /* num_ref_idx_active_override_flag */
+ WRITE_UINT32 (bs, num_ref_idx_active_override_flag, 1);
+ /* mvd_l1_zero_flag */
+ if (slice_param->slice_type == GST_H265_B_SLICE)
+ WRITE_UINT32 (bs, slice_param->slice_fields.bits.mvd_l1_zero_flag, 1);
+
+ /* cabac_init_present_flag == FALSE */
+ /* cabac_init_flag = FALSE */
+
+ /* collocated_from_l0_flag */
+ if (slice_param->slice_fields.bits.slice_temporal_mvp_enabled_flag) {
+ if (slice_param->slice_type == GST_H265_B_SLICE)
+ WRITE_UINT32 (bs,
+ slice_param->slice_fields.bits.collocated_from_l0_flag, 1);
+ }
+ /* five_minus_max_num_merge_cand */
+ WRITE_UE (bs, 5 - slice_param->max_num_merge_cand);
+ }
+
+ /* slice_qp_delta */
+ WRITE_SE (bs, slice_param->slice_qp_delta);
+ if (pic_param->pic_fields.bits.pps_loop_filter_across_slices_enabled_flag &&
+ (slice_param->slice_fields.bits.slice_sao_luma_flag
+ || slice_param->slice_fields.bits.slice_sao_chroma_flag
+ || !slice_deblocking_filter_disabled_flag))
+ WRITE_UINT32 (bs,
+ slice_param->slice_fields.
+ bits.slice_loop_filter_across_slices_enabled_flag, 1);
+
+ }
+
+ /* byte_alignment() */
+ {
+ /* alignment_bit_equal_to_one */
+ WRITE_UINT32 (bs, 1, 1);
+ while (GST_BIT_WRITER_BIT_SIZE (bs) % 8 != 0) {
+ /* alignment_bit_equal_to_zero */
+ WRITE_UINT32 (bs, 0, 1);
+ }
+ }
+
+ return TRUE;
+
+ /* ERRORS */
+bs_error:
+ {
+ GST_WARNING ("failed to write Slice NAL unit");
+ return FALSE;
+ }
+}
+
+static inline void
+_check_vps_sps_pps_status (GstVaapiEncoderH265 * encoder,
+ const guint8 * nal, guint32 size)
+{
+ guint8 nal_type;
+ gsize ret;
+ g_assert (size);
+
+ if (encoder->vps_data && encoder->sps_data && encoder->pps_data)
+ return;
+
+ nal_type = (nal[0] & 0x7E) >> 1;
+ switch (nal_type) {
+ case GST_H265_NAL_VPS:
+ encoder->vps_data = gst_buffer_new_allocate (NULL, size, NULL);
+ ret = gst_buffer_fill (encoder->vps_data, 0, nal, size);
+ g_assert (ret == size);
+ break;
+ case GST_H265_NAL_SPS:
+ encoder->sps_data = gst_buffer_new_allocate (NULL, size, NULL);
+ ret = gst_buffer_fill (encoder->sps_data, 0, nal, size);
+ g_assert (ret == size);
+ break;
+ case GST_H265_NAL_PPS:
+ encoder->pps_data = gst_buffer_new_allocate (NULL, size, NULL);
+ ret = gst_buffer_fill (encoder->pps_data, 0, nal, size);
+ g_assert (ret == size);
+ break;
+ default:
+ break;
+ }
+}
+
+/* Determines the largest supported profile by the underlying hardware */
+static gboolean
+ensure_hw_profile_limits (GstVaapiEncoderH265 * encoder)
+{
+ GstVaapiDisplay *const display = GST_VAAPI_ENCODER_DISPLAY (encoder);
+ GArray *profiles;
+ guint i, profile_idc, max_profile_idc;
+
+ if (encoder->hw_max_profile_idc)
+ return TRUE;
+
+ profiles = gst_vaapi_display_get_encode_profiles (display);
+ if (!profiles)
+ return FALSE;
+
+ max_profile_idc = 0;
+ for (i = 0; i < profiles->len; i++) {
+ const GstVaapiProfile profile =
+ g_array_index (profiles, GstVaapiProfile, i);
+ profile_idc = gst_vaapi_utils_h265_get_profile_idc (profile);
+ if (!profile_idc)
+ continue;
+ if (max_profile_idc < profile_idc)
+ max_profile_idc = profile_idc;
+ }
+ g_array_unref (profiles);
+
+ encoder->hw_max_profile_idc = max_profile_idc;
+ return TRUE;
+}
+
+/* Derives the profile supported by the underlying hardware */
+static gboolean
+ensure_hw_profile (GstVaapiEncoderH265 * encoder)
+{
+ GstVaapiDisplay *const display = GST_VAAPI_ENCODER_DISPLAY (encoder);
+ GstVaapiEntrypoint entrypoint = GST_VAAPI_ENTRYPOINT_SLICE_ENCODE;
+ GstVaapiProfile profile, profiles[4];
+ guint i, num_profiles = 0;
+
+ profiles[num_profiles++] = encoder->profile;
+ switch (encoder->profile) {
+ case GST_VAAPI_PROFILE_H265_MAIN_STILL_PICTURE:
+ profiles[num_profiles++] = GST_VAAPI_PROFILE_H265_MAIN;
+ // fall-through
+ case GST_VAAPI_PROFILE_H265_MAIN:
+ profiles[num_profiles++] = GST_VAAPI_PROFILE_H265_MAIN10;
+ break;
+ default:
+ break;
+ }
+
+ profile = GST_VAAPI_PROFILE_UNKNOWN;
+ for (i = 0; i < num_profiles; i++) {
+ if (gst_vaapi_display_has_encoder (display, profiles[i], entrypoint)) {
+ profile = profiles[i];
+ break;
+ }
+ }
+ if (profile == GST_VAAPI_PROFILE_UNKNOWN)
+ goto error_unsupported_profile;
+
+ GST_VAAPI_ENCODER_CAST (encoder)->profile = profile;
+ return TRUE;
+
+ /* ERRORS */
+error_unsupported_profile:
+ {
+ GST_ERROR ("unsupported HW profile (0x%08x)", encoder->profile);
+ return FALSE;
+ }
+}
+
+/* Check target decoder constraints */
+static gboolean
+ensure_profile_limits (GstVaapiEncoderH265 * encoder)
+{
+
+ if (!encoder->max_profile_idc
+ || encoder->profile_idc <= encoder->max_profile_idc)
+ return TRUE;
+
+ GST_WARNING
+ ("Needs to lower coding tools to meet target decoder constraints");
+ GST_WARNING ("Only supporting Main profile, reset profile to Main");
+
+ encoder->profile = GST_VAAPI_PROFILE_H265_MAIN;;
+ encoder->profile_idc =
+ gst_vaapi_utils_h265_get_profile_idc (encoder->profile);
+
+ return TRUE;
+}
+
+/* Derives the minimum profile from the active coding tools */
+static gboolean
+ensure_profile (GstVaapiEncoderH265 * encoder)
+{
+ GstVaapiProfile profile;
+
+ /* Always start from "Main" profile for maximum
+ compatibility */
+ profile = GST_VAAPI_PROFILE_H265_MAIN;
+
+ encoder->profile = profile;
+ encoder->profile_idc = gst_vaapi_utils_h265_get_profile_idc (profile);
+ return TRUE;
+}
+
+/* Derives the minimum tier from the active coding tools */
+static gboolean
+ensure_tier (GstVaapiEncoderH265 * encoder)
+{
+
+ encoder->tier = GST_VAAPI_TIER_H265_MAIN;
+ /*Fixme: Derive proper tier based on upstream caps or limits, coding tools etc */
+
+ return TRUE;
+}
+
+/* Derives the level from the currently set limits */
+static gboolean
+ensure_level (GstVaapiEncoderH265 * encoder)
+{
+ const GstVaapiH265LevelLimits *limits_table;
+ guint i, num_limits, PicSizeInSamplesY;
+
+ PicSizeInSamplesY = encoder->luma_width * encoder->luma_height;
+
+ limits_table = gst_vaapi_utils_h265_get_level_limits_table (&num_limits);
+ for (i = 0; i < num_limits; i++) {
+ const GstVaapiH265LevelLimits *const limits = &limits_table[i];
+ if (PicSizeInSamplesY <= limits->MaxLumaPs)
+ break;
+ /* Fixme: Add more constraint checking:tier (extracted from caps), cpb size,
+ * bitrate, num_tile_columns and num_tile_rows */
+ }
+ if (i == num_limits)
+ goto error_unsupported_level;
+
+ encoder->level = limits_table[i].level;
+ encoder->level_idc = limits_table[i].level_idc;
+ return TRUE;
+
+ /* ERRORS */
+error_unsupported_level:
+ {
+ GST_ERROR ("failed to find a suitable level matching codec config");
+ return FALSE;
+ }
+}
+
+/* Enable "high-compression" tuning options */
+static gboolean
+ensure_tuning_high_compression (GstVaapiEncoderH265 * encoder)
+{
+ guint8 profile_idc;
+
+ if (!ensure_hw_profile_limits (encoder))
+ return FALSE;
+
+ profile_idc = encoder->hw_max_profile_idc;
+ if (encoder->max_profile_idc && encoder->max_profile_idc < profile_idc)
+ profile_idc = encoder->max_profile_idc;
+
+ /* Tuning options */
+ if (!encoder->num_bframes)
+ encoder->num_bframes = 3;
+
+ return TRUE;
+}
+
+/* Ensure tuning options */
+static gboolean
+ensure_tuning (GstVaapiEncoderH265 * encoder)
+{
+ gboolean success;
+
+ switch (GST_VAAPI_ENCODER_TUNE (encoder)) {
+ case GST_VAAPI_ENCODER_TUNE_HIGH_COMPRESSION:
+ success = ensure_tuning_high_compression (encoder);
+ break;
+ default:
+ success = TRUE;
+ break;
+ }
+ return success;
+}
+
+/* Handle new GOP starts */
+static void
+reset_gop_start (GstVaapiEncoderH265 * encoder)
+{
+ GstVaapiH265ReorderPool *const reorder_pool = &encoder->reorder_pool;
+
+ reorder_pool->frame_index = 1;
+ reorder_pool->cur_present_index = 0;
+ ++encoder->idr_num;
+}
+
+/* Marks the supplied picture as a B-frame */
+static void
+set_b_frame (GstVaapiEncPicture * pic, GstVaapiEncoderH265 * encoder)
+{
+ g_assert (pic && encoder);
+ g_return_if_fail (pic->type == GST_VAAPI_PICTURE_TYPE_NONE);
+ pic->type = GST_VAAPI_PICTURE_TYPE_B;
+}
+
+/* Marks the supplied picture as a P-frame */
+static void
+set_p_frame (GstVaapiEncPicture * pic, GstVaapiEncoderH265 * encoder)
+{
+ g_return_if_fail (pic->type == GST_VAAPI_PICTURE_TYPE_NONE);
+ pic->type = GST_VAAPI_PICTURE_TYPE_P;
+}
+
+/* Marks the supplied picture as an I-frame */
+static void
+set_i_frame (GstVaapiEncPicture * pic, GstVaapiEncoderH265 * encoder)
+{
+ g_return_if_fail (pic->type == GST_VAAPI_PICTURE_TYPE_NONE);
+ pic->type = GST_VAAPI_PICTURE_TYPE_I;
+
+ g_assert (pic->frame);
+ GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (pic->frame);
+}
+
+/* Marks the supplied picture as an IDR frame */
+static void
+set_idr_frame (GstVaapiEncPicture * pic, GstVaapiEncoderH265 * encoder)
+{
+ g_return_if_fail (pic->type == GST_VAAPI_PICTURE_TYPE_NONE);
+ pic->type = GST_VAAPI_PICTURE_TYPE_I;
+ pic->poc = 0;
+ GST_VAAPI_ENC_PICTURE_FLAG_SET (pic, GST_VAAPI_ENC_PICTURE_FLAG_IDR);
+
+ g_assert (pic->frame);
+ GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (pic->frame);
+}
+
+/* Marks the supplied picture a a key-frame */
+static void
+set_key_frame (GstVaapiEncPicture * picture,
+ GstVaapiEncoderH265 * encoder, gboolean is_idr)
+{
+ if (is_idr) {
+ reset_gop_start (encoder);
+ set_idr_frame (picture, encoder);
+ } else
+ set_i_frame (picture, encoder);
+}
+
+/* Adds the supplied video parameter set header (VPS) to the list of packed
+ headers to pass down as-is to the encoder */
+static gboolean
+add_packed_vps_header (GstVaapiEncoderH265 * encoder,
+ GstVaapiEncPicture * picture, GstVaapiEncSequence * sequence)
+{
+ GstVaapiEncPackedHeader *packed_vps;
+ GstBitWriter bs;
+ VAEncPackedHeaderParameterBuffer packed_vps_param = { 0 };
+ const VAEncSequenceParameterBufferHEVC *const seq_param = sequence->param;
+ GstVaapiProfile profile = encoder->profile;
+
+ guint32 data_bit_size;
+ guint8 *data;
+
+ gst_bit_writer_init (&bs, 128 * 8);
+ WRITE_UINT32 (&bs, 0x00000001, 32); /* start code */
+ bs_write_nal_header (&bs, GST_H265_NAL_VPS);
+
+ bs_write_vps (&bs, encoder, picture, seq_param, profile);
+
+ g_assert (GST_BIT_WRITER_BIT_SIZE (&bs) % 8 == 0);
+ data_bit_size = GST_BIT_WRITER_BIT_SIZE (&bs);
+ data = GST_BIT_WRITER_DATA (&bs);
+
+ packed_vps_param.type = VAEncPackedHeaderSequence;
+ packed_vps_param.bit_length = data_bit_size;
+ packed_vps_param.has_emulation_bytes = 0;
+
+ packed_vps = gst_vaapi_enc_packed_header_new (GST_VAAPI_ENCODER (encoder),
+ &packed_vps_param, sizeof (packed_vps_param),
+ data, (data_bit_size + 7) / 8);
+ g_assert (packed_vps);
+
+ gst_vaapi_enc_picture_add_packed_header (picture, packed_vps);
+ gst_vaapi_codec_object_replace (&packed_vps, NULL);
+
+ /* store vps data */
+ _check_vps_sps_pps_status (encoder, data + 4, data_bit_size / 8 - 4);
+ gst_bit_writer_clear (&bs, TRUE);
+ return TRUE;
+
+ /* ERRORS */
+bs_error:
+ {
+ GST_WARNING ("failed to write VPS NAL unit");
+ gst_bit_writer_clear (&bs, TRUE);
+ return FALSE;
+ }
+}
+
+/* Adds the supplied sequence header (SPS) to the list of packed
+ headers to pass down as-is to the encoder */
+static gboolean
+add_packed_sequence_header (GstVaapiEncoderH265 * encoder,
+ GstVaapiEncPicture * picture, GstVaapiEncSequence * sequence)
+{
+ GstVaapiEncPackedHeader *packed_seq;
+ GstBitWriter bs;
+ VAEncPackedHeaderParameterBuffer packed_seq_param = { 0 };
+ const VAEncSequenceParameterBufferHEVC *const seq_param = sequence->param;
+ GstVaapiProfile profile = encoder->profile;
+
+ guint32 data_bit_size;
+ guint8 *data;
+
+ gst_bit_writer_init (&bs, 128 * 8);
+ WRITE_UINT32 (&bs, 0x00000001, 32); /* start code */
+ bs_write_nal_header (&bs, GST_H265_NAL_SPS);
+
+ bs_write_sps (&bs, encoder, picture, seq_param, profile);
+
+ g_assert (GST_BIT_WRITER_BIT_SIZE (&bs) % 8 == 0);
+ data_bit_size = GST_BIT_WRITER_BIT_SIZE (&bs);
+ data = GST_BIT_WRITER_DATA (&bs);
+
+ packed_seq_param.type = VAEncPackedHeaderSequence;
+ packed_seq_param.bit_length = data_bit_size;
+ packed_seq_param.has_emulation_bytes = 0;
+
+ packed_seq = gst_vaapi_enc_packed_header_new (GST_VAAPI_ENCODER (encoder),
+ &packed_seq_param, sizeof (packed_seq_param),
+ data, (data_bit_size + 7) / 8);
+ g_assert (packed_seq);
+
+ gst_vaapi_enc_picture_add_packed_header (picture, packed_seq);
+ gst_vaapi_codec_object_replace (&packed_seq, NULL);
+
+ /* store sps data */
+ _check_vps_sps_pps_status (encoder, data + 4, data_bit_size / 8 - 4);
+ gst_bit_writer_clear (&bs, TRUE);
+ return TRUE;
+
+ /* ERRORS */
+bs_error:
+ {
+ GST_WARNING ("failed to write SPS NAL unit");
+ gst_bit_writer_clear (&bs, TRUE);
+ return FALSE;
+ }
+}
+
+/* Adds the supplied picture header (PPS) to the list of packed
+ headers to pass down as-is to the encoder */
+static gboolean
+add_packed_picture_header (GstVaapiEncoderH265 * encoder,
+ GstVaapiEncPicture * picture)
+{
+ GstVaapiEncPackedHeader *packed_pic;
+ GstBitWriter bs;
+ VAEncPackedHeaderParameterBuffer packed_pic_param = { 0 };
+ const VAEncPictureParameterBufferHEVC *const pic_param = picture->param;
+ guint32 data_bit_size;
+ guint8 *data;
+
+ gst_bit_writer_init (&bs, 128 * 8);
+ WRITE_UINT32 (&bs, 0x00000001, 32); /* start code */
+ bs_write_nal_header (&bs, GST_H265_NAL_PPS);
+ bs_write_pps (&bs, pic_param);
+ g_assert (GST_BIT_WRITER_BIT_SIZE (&bs) % 8 == 0);
+ data_bit_size = GST_BIT_WRITER_BIT_SIZE (&bs);
+ data = GST_BIT_WRITER_DATA (&bs);
+
+ packed_pic_param.type = VAEncPackedHeaderPicture;
+ packed_pic_param.bit_length = data_bit_size;
+ packed_pic_param.has_emulation_bytes = 0;
+
+ packed_pic = gst_vaapi_enc_packed_header_new (GST_VAAPI_ENCODER (encoder),
+ &packed_pic_param, sizeof (packed_pic_param),
+ data, (data_bit_size + 7) / 8);
+ g_assert (packed_pic);
+
+ gst_vaapi_enc_picture_add_packed_header (picture, packed_pic);
+ gst_vaapi_codec_object_replace (&packed_pic, NULL);
+
+ /* store pps data */
+ _check_vps_sps_pps_status (encoder, data + 4, data_bit_size / 8 - 4);
+ gst_bit_writer_clear (&bs, TRUE);
+ return TRUE;
+
+ /* ERRORS */
+bs_error:
+ {
+ GST_WARNING ("failed to write PPS NAL unit");
+ gst_bit_writer_clear (&bs, TRUE);
+ return FALSE;
+ }
+}
+
+static gboolean
+get_nal_unit_type (GstVaapiEncPicture * picture, guint8 * nal_unit_type)
+{
+ switch (picture->type) {
+ case GST_VAAPI_PICTURE_TYPE_I:
+ if (GST_VAAPI_ENC_PICTURE_IS_IDR (picture))
+ *nal_unit_type = GST_H265_NAL_SLICE_IDR_W_RADL;
+ else
+ *nal_unit_type = GST_H265_NAL_SLICE_TRAIL_R;
+ break;
+ case GST_VAAPI_PICTURE_TYPE_P:
+ *nal_unit_type = GST_H265_NAL_SLICE_TRAIL_R;
+ break;
+ case GST_VAAPI_PICTURE_TYPE_B:
+ *nal_unit_type = GST_H265_NAL_SLICE_TRAIL_N;
+ break;
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Adds the supplied slice header to the list of packed
+ headers to pass down as-is to the encoder */
+static gboolean
+add_packed_slice_header (GstVaapiEncoderH265 * encoder,
+ GstVaapiEncPicture * picture, GstVaapiEncSlice * slice)
+{
+ GstVaapiEncPackedHeader *packed_slice;
+ GstBitWriter bs;
+ VAEncPackedHeaderParameterBuffer packed_slice_param = { 0 };
+ const VAEncSliceParameterBufferHEVC *const slice_param = slice->param;
+ guint32 data_bit_size;
+ guint8 *data;
+ guint8 nal_unit_type;
+
+ gst_bit_writer_init (&bs, 128 * 8);
+ WRITE_UINT32 (&bs, 0x00000001, 32); /* start code */
+
+ if (!get_nal_unit_type (picture, &nal_unit_type))
+ goto bs_error;
+ bs_write_nal_header (&bs, nal_unit_type);
+
+ bs_write_slice (&bs, slice_param, encoder, picture, nal_unit_type);
+ data_bit_size = GST_BIT_WRITER_BIT_SIZE (&bs);
+ data = GST_BIT_WRITER_DATA (&bs);
+
+ packed_slice_param.type = VAEncPackedHeaderSlice;
+ packed_slice_param.bit_length = data_bit_size;
+ packed_slice_param.has_emulation_bytes = 0;
+
+ packed_slice = gst_vaapi_enc_packed_header_new (GST_VAAPI_ENCODER (encoder),
+ &packed_slice_param, sizeof (packed_slice_param),
+ data, (data_bit_size + 7) / 8);
+ g_assert (packed_slice);
+
+ gst_vaapi_enc_slice_add_packed_header (slice, packed_slice);
+ gst_vaapi_codec_object_replace (&packed_slice, NULL);
+
+ gst_bit_writer_clear (&bs, TRUE);
+ return TRUE;
+
+ /* ERRORS */
+bs_error:
+ {
+ GST_WARNING ("failed to write Slice NAL unit header");
+ gst_bit_writer_clear (&bs, TRUE);
+ return FALSE;
+ }
+}
+
+/* Reference picture management */
+static void
+reference_pic_free (GstVaapiEncoderH265 * encoder, GstVaapiEncoderH265Ref * ref)
+{
+ if (!ref)
+ return;
+ if (ref->pic)
+ gst_vaapi_encoder_release_surface (GST_VAAPI_ENCODER (encoder), ref->pic);
+ g_slice_free (GstVaapiEncoderH265Ref, ref);
+}
+
+static inline GstVaapiEncoderH265Ref *
+reference_pic_create (GstVaapiEncoderH265 * encoder,
+ GstVaapiEncPicture * picture, GstVaapiSurfaceProxy * surface)
+{
+ GstVaapiEncoderH265Ref *const ref = g_slice_new0 (GstVaapiEncoderH265Ref);
+
+ ref->pic = surface;
+ ref->poc = picture->poc;
+ return ref;
+}
+
+static gboolean
+reference_list_update (GstVaapiEncoderH265 * encoder,
+ GstVaapiEncPicture * picture, GstVaapiSurfaceProxy * surface)
+{
+ GstVaapiEncoderH265Ref *ref;
+ GstVaapiH265RefPool *const ref_pool = &encoder->ref_pool;
+
+ if (GST_VAAPI_PICTURE_TYPE_B == picture->type) {
+ gst_vaapi_encoder_release_surface (GST_VAAPI_ENCODER (encoder), surface);
+ return TRUE;
+ }
+
+ if (GST_VAAPI_ENC_PICTURE_IS_IDR (picture)) {
+ while (!g_queue_is_empty (&ref_pool->ref_list))
+ reference_pic_free (encoder, g_queue_pop_head (&ref_pool->ref_list));
+ } else if (g_queue_get_length (&ref_pool->ref_list) >=
+ ref_pool->max_ref_frames) {
+ reference_pic_free (encoder, g_queue_pop_head (&ref_pool->ref_list));
+ }
+ ref = reference_pic_create (encoder, picture, surface);
+ g_queue_push_tail (&ref_pool->ref_list, ref);
+ g_assert (g_queue_get_length (&ref_pool->ref_list) <=
+ ref_pool->max_ref_frames);
+ return TRUE;
+}
+
+static gboolean
+reference_list_init (GstVaapiEncoderH265 * encoder,
+ GstVaapiEncPicture * picture,
+ GstVaapiEncoderH265Ref ** reflist_0,
+ guint * reflist_0_count,
+ GstVaapiEncoderH265Ref ** reflist_1, guint * reflist_1_count)
+{
+ GstVaapiEncoderH265Ref *tmp;
+ GstVaapiH265RefPool *const ref_pool = &encoder->ref_pool;
+ GList *iter, *list_0_start = NULL, *list_1_start = NULL;
+ guint count;
+
+ *reflist_0_count = 0;
+ *reflist_1_count = 0;
+ if (picture->type == GST_VAAPI_PICTURE_TYPE_I)
+ return TRUE;
+
+ iter = g_queue_peek_tail_link (&ref_pool->ref_list);
+ for (; iter; iter = g_list_previous (iter)) {
+ tmp = (GstVaapiEncoderH265Ref *) iter->data;
+ g_assert (tmp && tmp->poc != picture->poc);
+ if (_poc_greater_than (picture->poc, tmp->poc, encoder->max_pic_order_cnt)) {
+ list_0_start = iter;
+ list_1_start = g_list_next (iter);
+ break;
+ }
+ }
+
+ /* order reflist_0 */
+ g_assert (list_0_start);
+ iter = list_0_start;
+ count = 0;
+ for (; iter; iter = g_list_previous (iter)) {
+ reflist_0[count] = (GstVaapiEncoderH265Ref *) iter->data;
+ ++count;
+ }
+ *reflist_0_count = count;
+
+ if (picture->type != GST_VAAPI_PICTURE_TYPE_B)
+ return TRUE;
+
+ /* order reflist_1 */
+ count = 0;
+ iter = list_1_start;
+ for (; iter; iter = g_list_next (iter)) {
+ reflist_1[count] = (GstVaapiEncoderH265Ref *) iter->data;
+ ++count;
+ }
+ *reflist_1_count = count;
+ return TRUE;
+}
+
+/* Fills in VA sequence parameter buffer */
+static gboolean
+fill_sequence (GstVaapiEncoderH265 * encoder, GstVaapiEncSequence * sequence)
+{
+ VAEncSequenceParameterBufferHEVC *const seq_param = sequence->param;
+
+ memset (seq_param, 0, sizeof (VAEncSequenceParameterBufferHEVC));
+
+ seq_param->general_profile_idc = encoder->profile_idc;
+ seq_param->general_level_idc = encoder->level_idc;
+ seq_param->general_tier_flag = 0; /* Fixme: use the tier flag extracted from upstream caps or calcuted one */
+
+ seq_param->intra_period = GST_VAAPI_ENCODER_KEYFRAME_PERIOD (encoder);
+ seq_param->intra_idr_period = encoder->idr_period;
+ seq_param->ip_period = 1 + encoder->num_bframes;
+ seq_param->ip_period = seq_param->intra_period > 1 ?
+ (1 + encoder->num_bframes) : 0;
+ seq_param->bits_per_second = encoder->bitrate_bits;
+
+ seq_param->pic_width_in_luma_samples = encoder->luma_width;
+ seq_param->pic_height_in_luma_samples = encoder->luma_height;
+
+ /*sequence field values */
+ seq_param->seq_fields.value = 0;
+ seq_param->seq_fields.bits.chroma_format_idc = 1;
+ seq_param->seq_fields.bits.separate_colour_plane_flag = 0;
+ seq_param->seq_fields.bits.bit_depth_luma_minus8 = 0;
+ seq_param->seq_fields.bits.bit_depth_chroma_minus8 = 0;
+ seq_param->seq_fields.bits.scaling_list_enabled_flag = FALSE;
+ seq_param->seq_fields.bits.strong_intra_smoothing_enabled_flag = TRUE;
+ seq_param->seq_fields.bits.amp_enabled_flag = TRUE;
+ seq_param->seq_fields.bits.sample_adaptive_offset_enabled_flag =
+ encoder->sample_adaptive_offset_enabled_flag = FALSE;
+ seq_param->seq_fields.bits.pcm_enabled_flag = FALSE;
+ seq_param->seq_fields.bits.pcm_loop_filter_disabled_flag = FALSE;
+ seq_param->seq_fields.bits.sps_temporal_mvp_enabled_flag =
+ encoder->sps_temporal_mvp_enabled_flag = TRUE;
+
+ /* Based on 32x32 CTU */
+ seq_param->log2_min_luma_coding_block_size_minus3 = 0;
+ seq_param->log2_diff_max_min_luma_coding_block_size = 2;
+ seq_param->log2_min_transform_block_size_minus2 = 0;
+ seq_param->log2_diff_max_min_transform_block_size = 3;
+ seq_param->max_transform_hierarchy_depth_inter = 3;
+ seq_param->max_transform_hierarchy_depth_intra = 3;
+
+ seq_param->pcm_sample_bit_depth_luma_minus1 = 0;
+ seq_param->pcm_sample_bit_depth_chroma_minus1 = 0;
+ seq_param->log2_min_pcm_luma_coding_block_size_minus3 = 0;
+ seq_param->log2_max_pcm_luma_coding_block_size_minus3 = 0;
+
+ /* VUI parameters are always set, at least for timing_info (framerate) */
+ seq_param->vui_parameters_present_flag = TRUE;
+ if (seq_param->vui_parameters_present_flag) {
+ seq_param->vui_fields.bits.aspect_ratio_info_present_flag = TRUE;
+ if (seq_param->vui_fields.bits.aspect_ratio_info_present_flag) {
+ const GstVideoInfo *const vip = GST_VAAPI_ENCODER_VIDEO_INFO (encoder);
+ seq_param->aspect_ratio_idc = 0xff;
+ seq_param->sar_width = GST_VIDEO_INFO_PAR_N (vip);
+ seq_param->sar_height = GST_VIDEO_INFO_PAR_D (vip);
+ }
+ seq_param->vui_fields.bits.bitstream_restriction_flag = FALSE;
+ seq_param->vui_fields.bits.vui_timing_info_present_flag = TRUE;
+ if (seq_param->vui_fields.bits.vui_timing_info_present_flag) {
+ seq_param->vui_num_units_in_tick = GST_VAAPI_ENCODER_FPS_D (encoder);
+ seq_param->vui_time_scale = GST_VAAPI_ENCODER_FPS_N (encoder);
+ }
+ }
+ return TRUE;
+}
+
+/* Fills in VA picture parameter buffer */
+static gboolean
+fill_picture (GstVaapiEncoderH265 * encoder, GstVaapiEncPicture * picture,
+ GstVaapiCodedBuffer * codedbuf, GstVaapiSurfaceProxy * surface)
+{
+ VAEncPictureParameterBufferHEVC *const pic_param = picture->param;
+ GstVaapiH265RefPool *const ref_pool = &encoder->ref_pool;
+ GstVaapiEncoderH265Ref *ref_pic;
+ GList *reflist;
+ guint i;
+ guint8 nal_unit_type, no_output_of_prior_pics_flag = 0;
+
+ memset (pic_param, 0, sizeof (VAEncPictureParameterBufferHEVC));
+
+ pic_param->decoded_curr_pic.picture_id =
+ GST_VAAPI_SURFACE_PROXY_SURFACE_ID (surface);
+ pic_param->decoded_curr_pic.pic_order_cnt = picture->poc;
+ pic_param->decoded_curr_pic.flags = 0;
+
+ i = 0;
+ if (picture->type != GST_VAAPI_PICTURE_TYPE_I) {
+ for (reflist = g_queue_peek_head_link (&ref_pool->ref_list);
+ reflist; reflist = g_list_next (reflist)) {
+ ref_pic = reflist->data;
+ g_assert (ref_pic && ref_pic->pic &&
+ GST_VAAPI_SURFACE_PROXY_SURFACE_ID (ref_pic->pic) != VA_INVALID_ID);
+
+ pic_param->reference_frames[i].picture_id =
+ GST_VAAPI_SURFACE_PROXY_SURFACE_ID (ref_pic->pic);
+ ++i;
+ }
+ g_assert (i <= 15 && i <= ref_pool->max_ref_frames);
+ }
+ for (; i < 15; ++i) {
+ pic_param->reference_frames[i].picture_id = VA_INVALID_SURFACE;
+ pic_param->reference_frames[i].flags = 0;
+ }
+ pic_param->coded_buf = GST_VAAPI_OBJECT_ID (codedbuf);
+
+ /* slice_temporal_mvp_enable_flag == FALSE */
+ pic_param->collocated_ref_pic_index = 0xFF;
+
+ pic_param->last_picture = 0;
+ pic_param->pic_init_qp = encoder->init_qp;
+ pic_param->num_ref_idx_l0_default_active_minus1 =
+ (ref_pool->max_reflist0_count ? (ref_pool->max_reflist0_count - 1) : 0);
+ pic_param->num_ref_idx_l1_default_active_minus1 =
+ (ref_pool->max_reflist1_count ? (ref_pool->max_reflist1_count - 1) : 0);
+
+ get_nal_unit_type (picture, &nal_unit_type);
+ pic_param->nal_unit_type = nal_unit_type;
+
+ /* set picture fields */
+ pic_param->pic_fields.value = 0;
+ pic_param->pic_fields.bits.idr_pic_flag =
+ GST_VAAPI_ENC_PICTURE_IS_IDR (picture);
+ pic_param->pic_fields.bits.coding_type = picture->type;
+ if (picture->type != GST_VAAPI_PICTURE_TYPE_B)
+ pic_param->pic_fields.bits.reference_pic_flag = TRUE;
+ pic_param->pic_fields.bits.sign_data_hiding_enabled_flag = FALSE;
+ pic_param->pic_fields.bits.transform_skip_enabled_flag = TRUE;
+ pic_param->pic_fields.bits.pps_loop_filter_across_slices_enabled_flag = TRUE;
+
+ if (GST_VAAPI_ENC_PICTURE_IS_IDR (picture))
+ no_output_of_prior_pics_flag = 1;
+ pic_param->pic_fields.bits.no_output_of_prior_pics_flag =
+ no_output_of_prior_pics_flag;
+
+ return TRUE;
+}
+
+/* Adds slice headers to picture */
+static gboolean
+add_slice_headers (GstVaapiEncoderH265 * encoder, GstVaapiEncPicture * picture,
+ GstVaapiEncoderH265Ref ** reflist_0, guint reflist_0_count,
+ GstVaapiEncoderH265Ref ** reflist_1, guint reflist_1_count)
+{
+ VAEncSliceParameterBufferHEVC *slice_param;
+ GstVaapiEncSlice *slice;
+ guint slice_of_ctus, slice_mod_ctus, cur_slice_ctus;
+ guint ctu_size;
+ guint last_ctu_index;
+ guint i_slice, i_ref;
+
+ g_assert (picture);
+
+ ctu_size = encoder->ctu_width * encoder->ctu_height;
+
+ g_assert (encoder->num_slices && encoder->num_slices < ctu_size);
+ slice_of_ctus = ctu_size / encoder->num_slices;
+ slice_mod_ctus = ctu_size % encoder->num_slices;
+ last_ctu_index = 0;
+ for (i_slice = 0; i_slice < encoder->num_slices; ++i_slice) {
+ cur_slice_ctus = slice_of_ctus;
+ if (slice_mod_ctus) {
+ ++cur_slice_ctus;
+ --slice_mod_ctus;
+ }
+ slice = GST_VAAPI_ENC_SLICE_NEW (HEVC, encoder);
+ g_assert (slice && slice->param_id != VA_INVALID_ID);
+ slice_param = slice->param;
+
+ memset (slice_param, 0, sizeof (VAEncSliceParameterBufferHEVC));
+ if (i_slice == 0) {
+ encoder->first_slice_segment_in_pic_flag = TRUE;
+ slice_param->slice_segment_address = 0;
+ } else {
+ encoder->first_slice_segment_in_pic_flag = FALSE;
+ slice_param->slice_segment_address = last_ctu_index;
+ }
+ slice_param->num_ctu_in_slice = cur_slice_ctus;
+ slice_param->slice_type = h265_get_slice_type (picture->type);
+ slice_param->slice_pic_parameter_set_id = 0;
+
+ if (picture->type != GST_VAAPI_PICTURE_TYPE_I && reflist_0_count > 0)
+ slice_param->num_ref_idx_l0_active_minus1 = reflist_0_count - 1;
+ else
+ slice_param->num_ref_idx_l0_active_minus1 = 0;
+ if (picture->type == GST_VAAPI_PICTURE_TYPE_B && reflist_1_count > 0)
+ slice_param->num_ref_idx_l1_active_minus1 = reflist_1_count - 1;
+ else
+ slice_param->num_ref_idx_l1_active_minus1 = 0;
+ g_assert (slice_param->num_ref_idx_l0_active_minus1 == 0);
+ g_assert (slice_param->num_ref_idx_l1_active_minus1 == 0);
+
+ i_ref = 0;
+ if (picture->type != GST_VAAPI_PICTURE_TYPE_I) {
+ for (; i_ref < reflist_0_count; ++i_ref) {
+ slice_param->ref_pic_list0[i_ref].picture_id =
+ GST_VAAPI_SURFACE_PROXY_SURFACE_ID (reflist_0[i_ref]->pic);
+ slice_param->ref_pic_list0[i_ref].pic_order_cnt = reflist_0[i_ref]->poc;
+ }
+ g_assert (i_ref == 1);
+ }
+ for (; i_ref < G_N_ELEMENTS (slice_param->ref_pic_list0); ++i_ref) {
+ slice_param->ref_pic_list0[i_ref].picture_id = VA_INVALID_SURFACE;
+ slice_param->ref_pic_list0[i_ref].flags = 0;
+ }
+
+ i_ref = 0;
+ if (picture->type == GST_VAAPI_PICTURE_TYPE_B) {
+ for (; i_ref < reflist_1_count; ++i_ref) {
+ slice_param->ref_pic_list1[i_ref].picture_id =
+ GST_VAAPI_SURFACE_PROXY_SURFACE_ID (reflist_1[i_ref]->pic);
+ slice_param->ref_pic_list1[i_ref].pic_order_cnt = reflist_1[i_ref]->poc;
+ }
+ g_assert (i_ref == 1);
+ }
+ for (; i_ref < G_N_ELEMENTS (slice_param->ref_pic_list1); ++i_ref) {
+ slice_param->ref_pic_list1[i_ref].picture_id = VA_INVALID_SURFACE;
+ slice_param->ref_pic_list1[i_ref].flags = 0;
+ }
+
+ slice_param->max_num_merge_cand = 5; /* MaxNumMergeCand */
+ slice_param->slice_qp_delta = encoder->init_qp - encoder->min_qp;
+
+ slice_param->slice_fields.value = 0;
+ if (i_slice == encoder->num_slices - 1)
+ slice_param->slice_fields.bits.last_slice_of_pic_flag = 1;
+
+ slice_param->slice_fields.
+ bits.slice_loop_filter_across_slices_enabled_flag = TRUE;
+ /* set calculation for next slice */
+ last_ctu_index += cur_slice_ctus;
+
+ if ((GST_VAAPI_ENCODER_PACKED_HEADERS (encoder) &
+ VAEncPackedHeaderHEVC_Slice)
+ && !add_packed_slice_header (encoder, picture, slice))
+ goto error_create_packed_slice_hdr;
+
+ gst_vaapi_enc_picture_add_slice (picture, slice);
+ gst_vaapi_codec_object_replace (&slice, NULL);
+ }
+ g_assert (last_ctu_index == ctu_size);
+ return TRUE;
+
+error_create_packed_slice_hdr:
+ {
+ GST_ERROR ("failed to create packed slice header buffer");
+ gst_vaapi_codec_object_replace (&slice, NULL);
+ return FALSE;
+ }
+}
+
+/* Generates and submits SPS header accordingly into the bitstream */
+static gboolean
+ensure_sequence (GstVaapiEncoderH265 * encoder, GstVaapiEncPicture * picture)
+{
+ GstVaapiEncSequence *sequence = NULL;
+
+ /* submit an SPS header before every new I-frame, if codec config changed */
+ if (!encoder->config_changed || picture->type != GST_VAAPI_PICTURE_TYPE_I)
+ return TRUE;
+
+ sequence = GST_VAAPI_ENC_SEQUENCE_NEW (HEVC, encoder);
+ if (!sequence || !fill_sequence (encoder, sequence))
+ goto error_create_seq_param;
+
+ /* add packed vps and sps headers */
+ if ((GST_VAAPI_ENCODER_PACKED_HEADERS (encoder) & VAEncPackedHeaderHEVC_SPS)
+ && !(add_packed_vps_header (encoder, picture, sequence)
+ && add_packed_sequence_header (encoder, picture, sequence))) {
+ goto error_create_packed_seq_hdr;
+ }
+
+ if (sequence) {
+ gst_vaapi_enc_picture_set_sequence (picture, sequence);
+ gst_vaapi_codec_object_replace (&sequence, NULL);
+ }
+
+ encoder->config_changed = FALSE;
+ return TRUE;
+
+ /* ERRORS */
+error_create_seq_param:
+ {
+ GST_ERROR ("failed to create sequence parameter buffer (SPS)");
+ gst_vaapi_codec_object_replace (&sequence, NULL);
+ return FALSE;
+ }
+error_create_packed_seq_hdr:
+ {
+ GST_ERROR ("failed to create packed sequence header buffer");
+ gst_vaapi_codec_object_replace (&sequence, NULL);
+ return FALSE;
+ }
+}
+
+/* Generates and submits PPS header accordingly into the bitstream */
+static gboolean
+ensure_picture (GstVaapiEncoderH265 * encoder, GstVaapiEncPicture * picture,
+ GstVaapiCodedBufferProxy * codedbuf_proxy, GstVaapiSurfaceProxy * surface)
+{
+ GstVaapiCodedBuffer *const codedbuf =
+ GST_VAAPI_CODED_BUFFER_PROXY_BUFFER (codedbuf_proxy);
+ gboolean res = FALSE;
+
+ res = fill_picture (encoder, picture, codedbuf, surface);
+
+ if (!res)
+ return FALSE;
+
+ if (picture->type == GST_VAAPI_PICTURE_TYPE_I &&
+ (GST_VAAPI_ENCODER_PACKED_HEADERS (encoder) & VAEncPackedHeaderHEVC_PPS)
+ && !add_packed_picture_header (encoder, picture)) {
+ GST_ERROR ("set picture packed header failed");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Generates slice headers */
+static gboolean
+ensure_slices (GstVaapiEncoderH265 * encoder, GstVaapiEncPicture * picture)
+{
+ GstVaapiEncoderH265Ref *reflist_0[15];
+ GstVaapiEncoderH265Ref *reflist_1[15];
+ GstVaapiH265RefPool *const ref_pool = &encoder->ref_pool;
+ guint reflist_0_count = 0, reflist_1_count = 0;
+
+ g_assert (picture);
+
+ if (picture->type != GST_VAAPI_PICTURE_TYPE_I &&
+ !reference_list_init (encoder, picture,
+ reflist_0, &reflist_0_count, reflist_1, &reflist_1_count)) {
+ GST_ERROR ("reference list reorder failed");
+ return FALSE;
+ }
+
+ g_assert (reflist_0_count + reflist_1_count <= ref_pool->max_ref_frames);
+ if (reflist_0_count > ref_pool->max_reflist0_count)
+ reflist_0_count = ref_pool->max_reflist0_count;
+ if (reflist_1_count > ref_pool->max_reflist1_count)
+ reflist_1_count = ref_pool->max_reflist1_count;
+
+ if (!add_slice_headers (encoder, picture,
+ reflist_0, reflist_0_count, reflist_1, reflist_1_count))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Estimates a good enough bitrate if none was supplied */
+static void
+ensure_bitrate (GstVaapiEncoderH265 * encoder)
+{
+ GstVaapiEncoder *const base_encoder = GST_VAAPI_ENCODER_CAST (encoder);
+
+ switch (GST_VAAPI_ENCODER_RATE_CONTROL (encoder)) {
+ case GST_VAAPI_RATECONTROL_CBR:
+ case GST_VAAPI_RATECONTROL_VBR:
+ case GST_VAAPI_RATECONTROL_VBR_CONSTRAINED:
+ if (!base_encoder->bitrate) {
+ /* Fixme: Provide better estimation */
+ /* Using a 1/6 compression ratio */
+ /* 12 bits per pixel fro yuv420 */
+ base_encoder->bitrate =
+ (encoder->luma_width * encoder->luma_height * 12 / 6) *
+ GST_VAAPI_ENCODER_FPS_N (encoder) /
+ GST_VAAPI_ENCODER_FPS_D (encoder) / 1000;
+ GST_INFO ("target bitrate computed to %u kbps", base_encoder->bitrate);
+ }
+ break;
+ default:
+ base_encoder->bitrate = 0;
+ break;
+ }
+}
+
+/* Constructs profile, tier and level information based on user-defined limits */
+static GstVaapiEncoderStatus
+ensure_profile_tier_level (GstVaapiEncoderH265 * encoder)
+{
+ const GstVaapiProfile profile = encoder->profile;
+ const GstVaapiTierH265 tier = encoder->tier;
+ const GstVaapiLevelH265 level = encoder->level;
+
+ ensure_tuning (encoder);
+
+ if (!ensure_profile (encoder) || !ensure_profile_limits (encoder))
+ return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
+
+ /* Check HW constraints */
+ if (!ensure_hw_profile_limits (encoder))
+ return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
+ if (encoder->profile_idc > encoder->hw_max_profile_idc)
+ return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
+
+ /* ensure tier */
+ if (!ensure_tier (encoder))
+ return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
+
+ if (!ensure_level (encoder))
+ /* Ensure bitrate if not set already and derive the right level to use */
+ ensure_bitrate (encoder);
+ if (!ensure_level (encoder))
+ return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
+
+ if (encoder->profile != profile || encoder->level != level
+ || encoder->tier != tier) {
+ GST_DEBUG ("selected %s profile at tier %s and level %s",
+ gst_vaapi_utils_h265_get_profile_string (encoder->profile),
+ gst_vaapi_utils_h265_get_tier_string (encoder->tier),
+ gst_vaapi_utils_h265_get_level_string (encoder->level));
+ encoder->config_changed = TRUE;
+ }
+ return GST_VAAPI_ENCODER_STATUS_SUCCESS;
+}
+
+static void
+reset_properties (GstVaapiEncoderH265 * encoder)
+{
+ GstVaapiEncoder *const base_encoder = GST_VAAPI_ENCODER_CAST (encoder);
+ GstVaapiH265ReorderPool *reorder_pool;
+ GstVaapiH265RefPool *ref_pool;
+ guint ctu_size;
+
+ if (encoder->idr_period < base_encoder->keyframe_period)
+ encoder->idr_period = base_encoder->keyframe_period;
+ if (encoder->idr_period > MAX_IDR_PERIOD)
+ encoder->idr_period = MAX_IDR_PERIOD;
+
+ /*Fixme: provide user control for idr_period ?? */
+ encoder->idr_period = base_encoder->keyframe_period * 2;
+
+ if (encoder->min_qp > encoder->init_qp ||
+ (GST_VAAPI_ENCODER_RATE_CONTROL (encoder) == GST_VAAPI_RATECONTROL_CQP &&
+ encoder->min_qp < encoder->init_qp))
+ encoder->min_qp = encoder->init_qp;
+
+ ctu_size = encoder->ctu_width * encoder->ctu_height;
+ if (encoder->num_slices > (ctu_size + 1) / 2)
+ encoder->num_slices = (ctu_size + 1) / 2;
+ g_assert (encoder->num_slices);
+
+ if (encoder->num_bframes > (base_encoder->keyframe_period + 1) / 2)
+ encoder->num_bframes = (base_encoder->keyframe_period + 1) / 2;
+
+ if (encoder->num_bframes)
+ encoder->cts_offset = GST_SECOND * GST_VAAPI_ENCODER_FPS_D (encoder) /
+ GST_VAAPI_ENCODER_FPS_N (encoder);
+ else
+ encoder->cts_offset = 0;
+
+ /* init max_poc */
+ encoder->log2_max_pic_order_cnt =
+ h265_get_log2_max_pic_order_cnt (encoder->idr_period);
+ g_assert (encoder->log2_max_pic_order_cnt >= 4);
+ encoder->max_pic_order_cnt = (1 << encoder->log2_max_pic_order_cnt);
+ encoder->idr_num = 0;
+
+ /* Only Supporting a maximum of two reference frames */
+ if (encoder->num_bframes) {
+ encoder->max_dec_pic_buffering = 3;
+ encoder->max_num_reorder_pics = 1;
+ } else {
+ encoder->max_dec_pic_buffering =
+ (GST_VAAPI_ENCODER_KEYFRAME_PERIOD (encoder) == 1) ? 1 : 2;
+ encoder->max_num_reorder_pics = 0;
+ }
+
+ ref_pool = &encoder->ref_pool;
+ ref_pool->max_reflist0_count = 1;
+ ref_pool->max_reflist1_count = encoder->num_bframes > 0;
+ ref_pool->max_ref_frames = ref_pool->max_reflist0_count
+ + ref_pool->max_reflist1_count;
+
+ reorder_pool = &encoder->reorder_pool;
+ reorder_pool->frame_index = 0;
+}
+
+static GstVaapiEncoderStatus
+gst_vaapi_encoder_h265_encode (GstVaapiEncoder * base_encoder,
+ GstVaapiEncPicture * picture, GstVaapiCodedBufferProxy * codedbuf)
+{
+ GstVaapiEncoderH265 *const encoder =
+ GST_VAAPI_ENCODER_H265_CAST (base_encoder);
+ GstVaapiEncoderStatus ret = GST_VAAPI_ENCODER_STATUS_ERROR_UNKNOWN;
+ GstVaapiSurfaceProxy *reconstruct = NULL;
+
+ reconstruct = gst_vaapi_encoder_create_surface (base_encoder);
+
+ g_assert (GST_VAAPI_SURFACE_PROXY_SURFACE (reconstruct));
+
+ if (!ensure_sequence (encoder, picture))
+ goto error;
+ if (!ensure_picture (encoder, picture, codedbuf, reconstruct))
+ goto error;
+ if (!ensure_slices (encoder, picture))
+ goto error;
+ if (!gst_vaapi_enc_picture_encode (picture))
+ goto error;
+
+ if (!reference_list_update (encoder, picture, reconstruct))
+ goto error;
+
+ return GST_VAAPI_ENCODER_STATUS_SUCCESS;
+error:
+ if (reconstruct)
+ gst_vaapi_encoder_release_surface (GST_VAAPI_ENCODER (encoder),
+ reconstruct);
+ return ret;
+}
+
+static GstVaapiEncoderStatus
+gst_vaapi_encoder_h265_flush (GstVaapiEncoder * base_encoder)
+{
+ GstVaapiEncoderH265 *const encoder =
+ GST_VAAPI_ENCODER_H265_CAST (base_encoder);
+ GstVaapiH265ReorderPool *reorder_pool;
+ GstVaapiEncPicture *pic;
+
+ reorder_pool = &encoder->reorder_pool;
+ reorder_pool->frame_index = 0;
+ reorder_pool->cur_present_index = 0;
+
+ while (!g_queue_is_empty (&reorder_pool->reorder_frame_list)) {
+ pic = (GstVaapiEncPicture *)
+ g_queue_pop_head (&reorder_pool->reorder_frame_list);
+ gst_vaapi_enc_picture_unref (pic);
+ }
+ g_queue_clear (&reorder_pool->reorder_frame_list);
+
+ return GST_VAAPI_ENCODER_STATUS_SUCCESS;
+}
+
+/* Generate "codec-data" buffer */
+static GstVaapiEncoderStatus
+gst_vaapi_encoder_h265_get_codec_data (GstVaapiEncoder * base_encoder,
+ GstBuffer ** out_buffer_ptr)
+{
+ GstVaapiEncoderH265 *const encoder =
+ GST_VAAPI_ENCODER_H265_CAST (base_encoder);
+ const guint32 configuration_version = 0x01;
+ const guint32 nal_length_size = 4;
+ GstMapInfo vps_info, sps_info, pps_info;
+ GstBitWriter bs;
+ GstBuffer *buffer;
+ guint min_spatial_segmentation_idc = 0;
+ guint num_arrays = 3;
+
+ if (!encoder->vps_data || !encoder->sps_data || !encoder->pps_data)
+ return GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_HEADER;
+ if (gst_buffer_get_size (encoder->sps_data) < 4)
+ return GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_HEADER;
+
+ if (!gst_buffer_map (encoder->vps_data, &vps_info, GST_MAP_READ))
+ goto error_map_vps_buffer;
+
+ if (!gst_buffer_map (encoder->sps_data, &sps_info, GST_MAP_READ))
+ goto error_map_sps_buffer;
+
+ if (!gst_buffer_map (encoder->pps_data, &pps_info, GST_MAP_READ))
+ goto error_map_pps_buffer;
+
+ /* Header */
+ gst_bit_writer_init (&bs,
+ (vps_info.size + sps_info.size + pps_info.size + 64) * 8);
+ WRITE_UINT32 (&bs, configuration_version, 8);
+ WRITE_UINT32 (&bs, sps_info.data[4], 8); /* profile_space | tier_flag | profile_idc */
+ WRITE_UINT32 (&bs, sps_info.data[5], 32); /* profile_compatibility_flag [0-31] */
+ /* progressive_source_flag | interlaced_source_flag | non_packed_constraint_flag |
+ * frame_only_constraint_flag | reserved_zero_bits[0-27] */
+ WRITE_UINT32 (&bs, sps_info.data[9], 32);
+ WRITE_UINT32 (&bs, sps_info.data[13], 16); /* reserved_zero_bits [28-43] */
+ WRITE_UINT32 (&bs, sps_info.data[15], 8); /* level_idc */
+ WRITE_UINT32 (&bs, 0x0f, 4); /* 1111 */
+ WRITE_UINT32 (&bs, min_spatial_segmentation_idc, 12); /* min_spatial_segmentation_idc */
+ WRITE_UINT32 (&bs, 0x3f, 6); /* 111111 */
+ WRITE_UINT32 (&bs, 0x00, 2); /* parallelismType */
+ WRITE_UINT32 (&bs, 0x3f, 6); /* 111111 */
+ WRITE_UINT32 (&bs, 0x01, 2); /* chroma_format_idc */
+ WRITE_UINT32 (&bs, 0x3f, 6); /* 111111 */
+ WRITE_UINT32 (&bs, 0x01, 3); /* bit_depth_luma_minus8 */
+ WRITE_UINT32 (&bs, 0x3f, 6); /* 111111 */
+ WRITE_UINT32 (&bs, 0x01, 3); /* bit_depth_chroma_minus8 */
+ WRITE_UINT32 (&bs, 0x00, 16); /* avgFramerate */
+ WRITE_UINT32 (&bs, 0x00, 2); /* constatnFramerate */
+ WRITE_UINT32 (&bs, 0x00, 3); /* numTemporalLayers */
+ WRITE_UINT32 (&bs, 0x00, 1); /* temporalIdNested */
+ WRITE_UINT32 (&bs, nal_length_size - 1, 2); /* lengthSizeMinusOne */
+ WRITE_UINT32 (&bs, 0x00, 8); /* numOfArrays */
+
+ WRITE_UINT32 (&bs, num_arrays, 8); /* numOfArrays */
+
+ /* Write VPS */
+ WRITE_UINT32 (&bs, 0x00, 1); /* array_completeness */
+ WRITE_UINT32 (&bs, 0x00, 1); /* reserved zero */
+ WRITE_UINT32 (&bs, GST_H265_NAL_VPS, 6); /* Nal_unit_type */
+ WRITE_UINT32 (&bs, 0x01, 16); /* numNalus, VPS count = 1 */
+ g_assert (GST_BIT_WRITER_BIT_SIZE (&bs) % 8 == 0);
+ WRITE_UINT32 (&bs, vps_info.size, 16); /* VPS nalUnitLength */
+ gst_bit_writer_put_bytes (&bs, vps_info.data, vps_info.size);
+
+ /* Write SPS */
+ WRITE_UINT32 (&bs, 0x00, 1); /* array_completeness */
+ WRITE_UINT32 (&bs, 0x00, 1); /* reserved zero */
+ WRITE_UINT32 (&bs, GST_H265_NAL_SPS, 6); /* Nal_unit_type */
+ WRITE_UINT32 (&bs, 0x01, 16); /* numNalus, SPS count = 1 */
+ g_assert (GST_BIT_WRITER_BIT_SIZE (&bs) % 8 == 0);
+ WRITE_UINT32 (&bs, sps_info.size, 16); /* SPS nalUnitLength */
+ gst_bit_writer_put_bytes (&bs, sps_info.data, sps_info.size);
+
+ /* Write PPS */
+ WRITE_UINT32 (&bs, 0x00, 1); /* array_completeness */
+ WRITE_UINT32 (&bs, 0x00, 1); /* reserved zero */
+ WRITE_UINT32 (&bs, GST_H265_NAL_PPS, 6); /* Nal_unit_type */
+ WRITE_UINT32 (&bs, 0x01, 16); /* numNalus, PPS count = 1 */
+ WRITE_UINT32 (&bs, pps_info.size, 16); /* PPS nalUnitLength */
+ gst_bit_writer_put_bytes (&bs, pps_info.data, pps_info.size);
+
+ gst_buffer_unmap (encoder->pps_data, &pps_info);
+ gst_buffer_unmap (encoder->sps_data, &sps_info);
+ gst_buffer_unmap (encoder->vps_data, &vps_info);
+
+ buffer = gst_buffer_new_wrapped (GST_BIT_WRITER_DATA (&bs),
+ GST_BIT_WRITER_BIT_SIZE (&bs) / 8);
+ if (!buffer)
+ goto error_alloc_buffer;
+ *out_buffer_ptr = buffer;
+
+ gst_bit_writer_clear (&bs, FALSE);
+ return GST_VAAPI_ENCODER_STATUS_SUCCESS;
+
+ /* ERRORS */
+bs_error:
+ {
+ GST_ERROR ("failed to write codec-data");
+ gst_buffer_unmap (encoder->vps_data, &vps_info);
+ gst_buffer_unmap (encoder->sps_data, &sps_info);
+ gst_buffer_unmap (encoder->pps_data, &pps_info);
+ gst_bit_writer_clear (&bs, TRUE);
+ return FALSE;
+ }
+error_map_vps_buffer:
+ {
+ GST_ERROR ("failed to map VPS packed header");
+ return GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
+ }
+error_map_sps_buffer:
+ {
+ GST_ERROR ("failed to map SPS packed header");
+ return GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
+ }
+error_map_pps_buffer:
+ {
+ GST_ERROR ("failed to map PPS packed header");
+ gst_buffer_unmap (encoder->sps_data, &sps_info);
+ return GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
+ }
+error_alloc_buffer:
+ {
+ GST_ERROR ("failed to allocate codec-data buffer");
+ gst_bit_writer_clear (&bs, TRUE);
+ return GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
+ }
+}
+
+/* TODO */
+/* The re-ordering algorithm is similar to what we implemented for
+ * h264 encoder. But We could have a better algorithm for hevc encoder
+ * by having B-frames as reference pictures */
+static GstVaapiEncoderStatus
+gst_vaapi_encoder_h265_reordering (GstVaapiEncoder * base_encoder,
+ GstVideoCodecFrame * frame, GstVaapiEncPicture ** output)
+{
+ GstVaapiEncoderH265 *const encoder =
+ GST_VAAPI_ENCODER_H265_CAST (base_encoder);
+ GstVaapiH265ReorderPool *reorder_pool = NULL;
+ GstVaapiEncPicture *picture;
+ gboolean is_idr = FALSE;
+
+ *output = NULL;
+
+ reorder_pool = &encoder->reorder_pool;
+
+ if (!frame) {
+ if (reorder_pool->reorder_state != GST_VAAPI_ENC_H265_REORD_DUMP_FRAMES)
+ return GST_VAAPI_ENCODER_STATUS_NO_SURFACE;
+
+ /* reorder_state = GST_VAAPI_ENC_H265_REORD_DUMP_FRAMES
+ dump B frames from queue, sometime, there may also have P frame or I frame */
+ g_assert (encoder->num_bframes > 0);
+ g_return_val_if_fail (!g_queue_is_empty (&reorder_pool->reorder_frame_list),
+ GST_VAAPI_ENCODER_STATUS_ERROR_UNKNOWN);
+ picture = g_queue_pop_head (&reorder_pool->reorder_frame_list);
+ g_assert (picture);
+ if (g_queue_is_empty (&reorder_pool->reorder_frame_list)) {
+ reorder_pool->reorder_state = GST_VAAPI_ENC_H265_REORD_WAIT_FRAMES;
+ }
+ goto end;
+ }
+
+ /* new frame coming */
+ picture = GST_VAAPI_ENC_PICTURE_NEW (HEVC, encoder, frame);
+ if (!picture) {
+ GST_WARNING ("create H265 picture failed, frame timestamp:%"
+ GST_TIME_FORMAT, GST_TIME_ARGS (frame->pts));
+ return GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
+ }
+ ++reorder_pool->cur_present_index;
+ picture->poc = ((reorder_pool->cur_present_index * 1) %
+ encoder->max_pic_order_cnt);
+
+ is_idr = (reorder_pool->frame_index == 0 ||
+ reorder_pool->frame_index >= encoder->idr_period);
+
+ /* check key frames */
+ if (is_idr || GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame) ||
+ (reorder_pool->frame_index %
+ GST_VAAPI_ENCODER_KEYFRAME_PERIOD (encoder)) == 0) {
+ ++reorder_pool->frame_index;
+
+ /* b frame enabled, check queue of reorder_frame_list */
+ if (encoder->num_bframes
+ && !g_queue_is_empty (&reorder_pool->reorder_frame_list)) {
+ GstVaapiEncPicture *p_pic;
+
+ p_pic = g_queue_pop_tail (&reorder_pool->reorder_frame_list);
+ set_p_frame (p_pic, encoder);
+ g_queue_foreach (&reorder_pool->reorder_frame_list,
+ (GFunc) set_b_frame, encoder);
+ set_key_frame (picture, encoder, is_idr);
+ g_queue_push_tail (&reorder_pool->reorder_frame_list, picture);
+ picture = p_pic;
+ reorder_pool->reorder_state = GST_VAAPI_ENC_H265_REORD_DUMP_FRAMES;
+ } else { /* no b frames in queue */
+ set_key_frame (picture, encoder, is_idr);
+ g_assert (g_queue_is_empty (&reorder_pool->reorder_frame_list));
+ if (encoder->num_bframes)
+ reorder_pool->reorder_state = GST_VAAPI_ENC_H265_REORD_WAIT_FRAMES;
+ }
+ goto end;
+ }
+
+ /* new p/b frames coming */
+ ++reorder_pool->frame_index;
+ if (reorder_pool->reorder_state == GST_VAAPI_ENC_H265_REORD_WAIT_FRAMES &&
+ g_queue_get_length (&reorder_pool->reorder_frame_list) <
+ encoder->num_bframes) {
+ g_queue_push_tail (&reorder_pool->reorder_frame_list, picture);
+ return GST_VAAPI_ENCODER_STATUS_NO_SURFACE;
+ }
+
+ set_p_frame (picture, encoder);
+
+ if (reorder_pool->reorder_state == GST_VAAPI_ENC_H265_REORD_WAIT_FRAMES) {
+ g_queue_foreach (&reorder_pool->reorder_frame_list, (GFunc) set_b_frame,
+ encoder);
+ reorder_pool->reorder_state = GST_VAAPI_ENC_H265_REORD_DUMP_FRAMES;
+ g_assert (!g_queue_is_empty (&reorder_pool->reorder_frame_list));
+ }
+
+end:
+ g_assert (picture);
+ frame = picture->frame;
+ if (GST_CLOCK_TIME_IS_VALID (frame->pts))
+ frame->pts += encoder->cts_offset;
+ *output = picture;
+
+ return GST_VAAPI_ENCODER_STATUS_SUCCESS;
+}
+
+static GstVaapiEncoderStatus
+set_context_info (GstVaapiEncoder * base_encoder)
+{
+ GstVaapiEncoderH265 *const encoder =
+ GST_VAAPI_ENCODER_H265_CAST (base_encoder);
+ GstVideoInfo *const vip = GST_VAAPI_ENCODER_VIDEO_INFO (encoder);
+ const guint DEFAULT_SURFACES_COUNT = 3;
+
+ /* Fixme: Using only a rough approximation for bitstream headers..
+ * Fixme: Not taken into account: ScalingList, RefPicListModification, PredWeightTable */
+ /* Maximum sizes for common headers (in bits) */
+ enum
+ {
+ MAX_PROFILE_TIER_LEVEL_SIZE = 684,
+ MAX_VPS_HDR_SIZE = 13781,
+ MAX_SPS_HDR_SIZE = 615,
+ MAX_SHORT_TERM_REFPICSET_SIZE = 55,
+ MAX_VUI_PARAMS_SIZE = 267,
+ MAX_HRD_PARAMS_SIZE = 8196,
+ MAX_PPS_HDR_SIZE = 274,
+ MAX_SLICE_HDR_SIZE = 33660
+ };
+
+ /* Account for VPS header */
+ base_encoder->codedbuf_size += 4 + GST_ROUND_UP_8 (MAX_VPS_HDR_SIZE +
+ MAX_PROFILE_TIER_LEVEL_SIZE + MAX_HRD_PARAMS_SIZE) / 8;
+
+ /* Account for SPS header */
+ base_encoder->codedbuf_size += 4 + GST_ROUND_UP_8 (MAX_SPS_HDR_SIZE +
+ MAX_PROFILE_TIER_LEVEL_SIZE + 64 * MAX_SHORT_TERM_REFPICSET_SIZE +
+ MAX_VUI_PARAMS_SIZE + MAX_HRD_PARAMS_SIZE) / 8;
+
+ /* Account for PPS header */
+ base_encoder->codedbuf_size += 4 + GST_ROUND_UP_8 (MAX_PPS_HDR_SIZE) / 8;
+
+ /* Account for slice header */
+ base_encoder->codedbuf_size += encoder->num_slices * (4 +
+ GST_ROUND_UP_8 (MAX_SLICE_HDR_SIZE + MAX_SHORT_TERM_REFPICSET_SIZE) / 8);
+
+ if (!ensure_hw_profile (encoder))
+ return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
+
+ base_encoder->num_ref_frames =
+ ((encoder->num_bframes ? 2 : 1) + DEFAULT_SURFACES_COUNT);
+
+ /* Take an approximation of 6 times compression ratio */
+ base_encoder->codedbuf_size = ((GST_ROUND_UP_32 (vip->width) *
+ GST_ROUND_UP_32 (vip->height) * 12 / 6)) / 8;
+
+ return GST_VAAPI_ENCODER_STATUS_SUCCESS;
+}
+
+static GstVaapiEncoderStatus
+gst_vaapi_encoder_h265_reconfigure (GstVaapiEncoder * base_encoder)
+{
+ GstVaapiEncoderH265 *const encoder =
+ GST_VAAPI_ENCODER_H265_CAST (base_encoder);
+ GstVaapiEncoderStatus status;
+ guint luma_width, luma_height;
+
+ luma_width = GST_VAAPI_ENCODER_WIDTH (encoder);
+ luma_height = GST_VAAPI_ENCODER_HEIGHT (encoder);
+
+ if (luma_width != encoder->luma_width || luma_height != encoder->luma_height) {
+ GST_DEBUG ("resolution: %d %d", GST_VAAPI_ENCODER_WIDTH (encoder),
+ GST_VAAPI_ENCODER_HEIGHT (encoder));
+ encoder->luma_width = GST_ROUND_UP_32 (luma_width);
+ encoder->luma_height = GST_ROUND_UP_32 (luma_height);
+ encoder->ctu_width = (encoder->luma_width + 31) / 32;
+ encoder->ctu_height = (encoder->luma_height + 31) / 32;
+ encoder->config_changed = TRUE;
+
+ /* Frame Cropping */
+ if ((GST_VAAPI_ENCODER_WIDTH (encoder) & 31) ||
+ (GST_VAAPI_ENCODER_HEIGHT (encoder) & 31)) {
+ static const guint SubWidthC[] = { 1, 2, 2, 1 };
+ static const guint SubHeightC[] = { 1, 2, 1, 1 };
+ encoder->conformance_window_flag = 1;
+ encoder->conf_win_left_offset = 0;
+ encoder->conf_win_right_offset =
+ (encoder->luma_width -
+ GST_VAAPI_ENCODER_WIDTH (encoder)) / SubWidthC[1];
+ encoder->conf_win_top_offset = 0;
+ encoder->conf_win_bottom_offset =
+ (encoder->luma_height -
+ GST_VAAPI_ENCODER_HEIGHT (encoder)) / SubHeightC[1];
+ }
+ }
+
+ status = ensure_profile_tier_level (encoder);
+ if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
+ return status;
+
+ reset_properties (encoder);
+ return set_context_info (base_encoder);
+}
+
+static gboolean
+gst_vaapi_encoder_h265_init (GstVaapiEncoder * base_encoder)
+{
+ GstVaapiEncoderH265 *const encoder =
+ GST_VAAPI_ENCODER_H265_CAST (base_encoder);
+ GstVaapiH265ReorderPool *reorder_pool;
+ GstVaapiH265RefPool *ref_pool;
+
+ encoder->conformance_window_flag = 0;
+ encoder->num_slices = 1;
+
+ /* re-ordering list initialize */
+ reorder_pool = &encoder->reorder_pool;
+ g_queue_init (&reorder_pool->reorder_frame_list);
+ reorder_pool->reorder_state = GST_VAAPI_ENC_H265_REORD_NONE;
+ reorder_pool->frame_index = 0;
+ reorder_pool->cur_present_index = 0;
+
+ /* reference list info initialize */
+ ref_pool = &encoder->ref_pool;
+ g_queue_init (&ref_pool->ref_list);
+ ref_pool->max_ref_frames = 0;
+ ref_pool->max_reflist0_count = 1;
+ ref_pool->max_reflist1_count = 1;
+
+ return TRUE;
+}
+
+static void
+gst_vaapi_encoder_h265_finalize (GstVaapiEncoder * base_encoder)
+{
+ /*free private buffers */
+ GstVaapiEncoderH265 *const encoder =
+ GST_VAAPI_ENCODER_H265_CAST (base_encoder);
+ GstVaapiEncPicture *pic;
+ GstVaapiEncoderH265Ref *ref;
+ GstVaapiH265RefPool *ref_pool;
+ GstVaapiH265ReorderPool *reorder_pool;
+
+ gst_buffer_replace (&encoder->vps_data, NULL);
+ gst_buffer_replace (&encoder->sps_data, NULL);
+ gst_buffer_replace (&encoder->pps_data, NULL);
+
+ /* reference list info de-init */
+ ref_pool = &encoder->ref_pool;
+ while (!g_queue_is_empty (&ref_pool->ref_list)) {
+ ref = (GstVaapiEncoderH265Ref *) g_queue_pop_head (&ref_pool->ref_list);
+ reference_pic_free (encoder, ref);
+ }
+ g_queue_clear (&ref_pool->ref_list);
+
+ /* re-ordering list initialize */
+ reorder_pool = &encoder->reorder_pool;
+ while (!g_queue_is_empty (&reorder_pool->reorder_frame_list)) {
+ pic = (GstVaapiEncPicture *)
+ g_queue_pop_head (&reorder_pool->reorder_frame_list);
+ gst_vaapi_enc_picture_unref (pic);
+ }
+ g_queue_clear (&reorder_pool->reorder_frame_list);
+}
+
+static GstVaapiEncoderStatus
+gst_vaapi_encoder_h265_set_property (GstVaapiEncoder * base_encoder,
+ gint prop_id, const GValue * value)
+{
+ GstVaapiEncoderH265 *const encoder =
+ GST_VAAPI_ENCODER_H265_CAST (base_encoder);
+
+ switch (prop_id) {
+ case GST_VAAPI_ENCODER_H265_PROP_MAX_BFRAMES:
+ encoder->num_bframes = g_value_get_uint (value);
+ break;
+ case GST_VAAPI_ENCODER_H265_PROP_INIT_QP:
+ encoder->init_qp = g_value_get_uint (value);
+ break;
+ case GST_VAAPI_ENCODER_H265_PROP_MIN_QP:
+ encoder->min_qp = g_value_get_uint (value);
+ break;
+ case GST_VAAPI_ENCODER_H265_PROP_NUM_SLICES:
+ encoder->num_slices = g_value_get_uint (value);
+ if (encoder->num_slices > 1)
+ GST_ERROR ("Mulit-slice encoding is not yet functional!");
+ break;
+ default:
+ return GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_PARAMETER;
+ }
+ return GST_VAAPI_ENCODER_STATUS_SUCCESS;
+}
+
+GST_VAAPI_ENCODER_DEFINE_CLASS_DATA (H265);
+
+static inline const GstVaapiEncoderClass *
+gst_vaapi_encoder_h265_class (void)
+{
+ static const GstVaapiEncoderClass GstVaapiEncoderH265Class = {
+ GST_VAAPI_ENCODER_CLASS_INIT (H265, h265),
+ .set_property = gst_vaapi_encoder_h265_set_property,
+ .get_codec_data = gst_vaapi_encoder_h265_get_codec_data
+ };
+ return &GstVaapiEncoderH265Class;
+}
+
+/**
+ * gst_vaapi_encoder_h265_new:
+ * @display: a #GstVaapiDisplay
+ *
+ * Creates a new #GstVaapiEncoder for H.265 encoding. Note that the
+ * only supported output stream format is "byte-stream" format.
+ *
+ * Return value: the newly allocated #GstVaapiEncoder object
+ */
+GstVaapiEncoder *
+gst_vaapi_encoder_h265_new (GstVaapiDisplay * display)
+{
+ return gst_vaapi_encoder_new (gst_vaapi_encoder_h265_class (), display);
+}
+
+/**
+ * gst_vaapi_encoder_h265_get_default_properties:
+ *
+ * Determines the set of common and H.265 specific encoder properties.
+ * The caller owns an extra reference to the resulting array of
+ * #GstVaapiEncoderPropInfo elements, so it shall be released with
+ * g_ptr_array_unref() after usage.
+ *
+ * Return value: the set of encoder properties for #GstVaapiEncoderH265,
+ * or %NULL if an error occurred.
+ */
+GPtrArray *
+gst_vaapi_encoder_h265_get_default_properties (void)
+{
+ const GstVaapiEncoderClass *const klass = gst_vaapi_encoder_h265_class ();
+ GPtrArray *props;
+
+ props = gst_vaapi_encoder_properties_get_default (klass);
+ if (!props)
+ return NULL;
+
+ /**
+ * GstVaapiEncoderH265:max-bframes:
+ *
+ * The number of B-frames between I and P.
+ */
+ GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
+ GST_VAAPI_ENCODER_H265_PROP_MAX_BFRAMES,
+ g_param_spec_uint ("max-bframes",
+ "Max B-Frames", "Number of B-frames between I and P", 0, 10, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstVaapiEncoderH265:init-qp:
+ *
+ * The initial quantizer value.
+ */
+ GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
+ GST_VAAPI_ENCODER_H265_PROP_INIT_QP,
+ g_param_spec_uint ("init-qp",
+ "Initial QP", "Initial quantizer value", 1, 51, 26,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstVaapiEncoderH265:min-qp:
+ *
+ * The minimum quantizer value.
+ */
+ GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
+ GST_VAAPI_ENCODER_H265_PROP_MIN_QP,
+ g_param_spec_uint ("min-qp",
+ "Minimum QP", "Minimum quantizer value", 1, 51, 1,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /* Fixme: there seems to be issues with multi-slice encoding */
+ /**
+ * GstVaapiEncoderH265:num-slices:
+ *
+ * The number of slices per frame.
+ */
+ GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
+ GST_VAAPI_ENCODER_H265_PROP_NUM_SLICES,
+ g_param_spec_uint ("num-slices",
+ "Number of Slices",
+ "Number of slices per frame",
+ 1, 200, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ return props;
+}
+
+/**
+ * gst_vaapi_encoder_h265_set_max_profile:
+ * @encoder: a #GstVaapiEncoderH265
+ * @profile: an H.265 #GstVaapiProfile
+ *
+ * Notifies the @encoder to use coding tools from the supplied
+ * @profile at most.
+ *
+ * This means that if the minimal profile derived to
+ * support the specified coding tools is greater than this @profile,
+ * then an error is returned when the @encoder is configured.
+ *
+ * Return value: %TRUE on success
+ */
+gboolean
+gst_vaapi_encoder_h265_set_max_profile (GstVaapiEncoderH265 * encoder,
+ GstVaapiProfile profile)
+{
+ guint8 profile_idc;
+
+ g_return_val_if_fail (encoder != NULL, FALSE);
+ g_return_val_if_fail (profile != GST_VAAPI_PROFILE_UNKNOWN, FALSE);
+
+ if (gst_vaapi_profile_get_codec (profile) != GST_VAAPI_CODEC_H265)
+ return FALSE;
+
+ profile_idc = gst_vaapi_utils_h265_get_profile_idc (profile);
+ if (!profile_idc)
+ return FALSE;
+
+ encoder->max_profile_idc = profile_idc;
+ return TRUE;
+}
+
+/**
+ * gst_vaapi_encoder_h265_get_profile_tier_level:
+ * @encoder: a #GstVaapiEncoderH265
+ * @out_profile_ptr: return location for the #GstVaapiProfile
+ * @out_level_ptr: return location for the #GstVaapiLevelH265
+ * @out_tier_ptr: return location for the #GstVaapiTierH265
+ *
+ * Queries the H.265 @encoder for the active profile and level. That
+ * information is only constructed and valid after the encoder is
+ * configured, i.e. after the gst_vaapi_encoder_set_codec_state()
+ * function is called.
+ *
+ * Return value: %TRUE on success
+ */
+gboolean
+gst_vaapi_encoder_h265_get_profile_tier_level (GstVaapiEncoderH265 * encoder,
+ GstVaapiProfile * out_profile_ptr, GstVaapiTierH265 * out_tier_ptr,
+ GstVaapiLevelH265 * out_level_ptr)
+{
+ g_return_val_if_fail (encoder != NULL, FALSE);
+
+ if (!encoder->profile || !encoder->tier || !encoder->level)
+ return FALSE;
+
+ if (out_profile_ptr)
+ *out_profile_ptr = encoder->profile;
+ if (out_level_ptr)
+ *out_level_ptr = encoder->level;
+ if (out_tier_ptr)
+ *out_tier_ptr = encoder->tier;
+
+ return TRUE;
+}