HEVC_Encode: Add HEVC(h265) encoder to core libgstvaapi
authorSreerenj Balachandran <sreerenj.balachandran@intel.com>
Mon, 25 May 2015 08:38:34 +0000 (11:38 +0300)
committerSreerenj Balachandran <sreerenj.balachandran@intel.com>
Mon, 25 May 2015 08:42:33 +0000 (11:42 +0300)
https://bugzilla.gnome.org/show_bug.cgi?id=748874

Signed-off-by: Sreerenj Balachandran <sreerenj.balachandran@intel.com>
gst-libs/gst/vaapi/Makefile.am
gst-libs/gst/vaapi/gstvaapiencoder_h265.c [new file with mode: 0644]
gst-libs/gst/vaapi/gstvaapiencoder_h265.h [new file with mode: 0644]

index 348e0c1..1c65a7b 100644 (file)
@@ -222,6 +222,13 @@ libgstvaapi_source_c += $(libgstvaapi_vp8enc_source_c)
 libgstvaapi_source_h += $(libgstvaapi_vp8enc_source_h)
 endif
 
+libgstvaapi_h265enc_source_c = gstvaapiencoder_h265.c
+libgstvaapi_h265enc_source_h = gstvaapiencoder_h265.h
+if USE_H265_ENCODER
+libgstvaapi_source_c += $(libgstvaapi_h265enc_source_c)
+libgstvaapi_source_h += $(libgstvaapi_h265enc_source_h)
+endif
+
 libgstvaapi_drm_source_c =                     \
        gstvaapidisplay_drm.c                   \
        gstvaapiwindow_drm.c                    \
@@ -590,6 +597,8 @@ EXTRA_DIST += \
        $(libgstvaapi_jpegenc_source_c)         \
        $(libgstvaapi_vp8enc_source_h)          \
        $(libgstvaapi_vp8enc_source_c)          \
+       $(libgstvaapi_h265enc_source_h)         \
+       $(libgstvaapi_h265enc_source_c)         \
        $(libgstvaapi_egl_source_c)             \
        $(libgstvaapi_egl_source_h)             \
        $(libgstvaapi_egl_source_priv_h)        \
diff --git a/gst-libs/gst/vaapi/gstvaapiencoder_h265.c b/gst-libs/gst/vaapi/gstvaapiencoder_h265.c
new file mode 100644 (file)
index 0000000..0be492b
--- /dev/null
@@ -0,0 +1,2562 @@
+/*
+ *  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;
+}
diff --git a/gst-libs/gst/vaapi/gstvaapiencoder_h265.h b/gst-libs/gst/vaapi/gstvaapiencoder_h265.h
new file mode 100644 (file)
index 0000000..ec8483e
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ *  gstvaapiencoder_h265.h - 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
+ */
+
+#ifndef GST_VAAPI_ENCODER_H265_H
+#define GST_VAAPI_ENCODER_H265_H
+
+#include <gst/vaapi/gstvaapiencoder.h>
+#include <gst/vaapi/gstvaapiutils_h265.h>
+
+G_BEGIN_DECLS
+
+#define GST_VAAPI_ENCODER_H265(encoder) \
+  ((GstVaapiEncoderH265 *) (encoder))
+
+typedef struct _GstVaapiEncoderH265 GstVaapiEncoderH265;
+
+/**
+ * GstVaapiEncoderH265Prop:
+ * @GST_VAAPI_ENCODER_H265_PROP_MAX_BFRAMES: Number of B-frames between I
+ *   and P (uint).
+ * @GST_VAAPI_ENCODER_H265_PROP_INIT_QP: Initial quantizer value (uint).
+ * @GST_VAAPI_ENCODER_H265_PROP_MIN_QP: Minimal quantizer value (uint).
+ * @GST_VAAPI_ENCODER_H265_PROP_NUM_SLICES: Number of slices per frame (uint).
+ * @GST_VAAPI_ENCODER_H265_PROP_CPB_LENGTH: Length of the CPB buffer
+ *   in milliseconds (uint).
+ *
+ * The set of H.265 encoder specific configurable properties.
+ */
+typedef enum {
+  GST_VAAPI_ENCODER_H265_PROP_MAX_BFRAMES = -1,
+  GST_VAAPI_ENCODER_H265_PROP_INIT_QP = -2,
+  GST_VAAPI_ENCODER_H265_PROP_MIN_QP = -3,
+  GST_VAAPI_ENCODER_H265_PROP_NUM_SLICES = -4,
+  GST_VAAPI_ENCODER_H265_PROP_CPB_LENGTH = -7
+} GstVaapiEncoderH265Prop;
+
+GstVaapiEncoder *
+gst_vaapi_encoder_h265_new (GstVaapiDisplay * display);
+
+GPtrArray *
+gst_vaapi_encoder_h265_get_default_properties (void);
+
+gboolean
+gst_vaapi_encoder_h265_set_max_profile (GstVaapiEncoderH265 * encoder,
+    GstVaapiProfile profile);
+
+gboolean
+gst_vaapi_encoder_h265_get_profile_tier_level (GstVaapiEncoderH265 * encoder,
+    GstVaapiProfile * out_profile_ptr, GstVaapiTierH265 *out_tier_ptr, GstVaapiLevelH265 * out_level_ptr);
+
+G_END_DECLS
+
+#endif /*GST_VAAPI_ENCODER_H265_H */