encoder: h264: fix multiple slices support in packed headers mode.
[platform/upstream/gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapiencoder_h264.c
index cfa6547..0af8e94 100644 (file)
@@ -1,7 +1,9 @@
 /*
  *  gstvaapiencoder_h264.c - H.264 encoder
  *
- *  Copyright (C) 2012-2013 Intel Corporation
+ *  Copyright (C) 2012-2014 Intel Corporation
+ *    Author: Wind Yuan <feng.yuan@intel.com>
+ *    Author: Gwenole Beauchesne <gwenole.beauchesne@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
 #include <va/va.h>
 #include <va/va_enc_h264.h>
 #include <gst/base/gstbitwriter.h>
+#include <gst/codecparsers/gsth264parser.h>
 #include "gstvaapicompat.h"
+#include "gstvaapiencoder_priv.h"
 #include "gstvaapiencoder_h264.h"
-#include "gstvaapiencoder_h264_priv.h"
+#include "gstvaapiutils_h264.h"
 #include "gstvaapiutils_h264_priv.h"
 #include "gstvaapicodedbufferproxy_priv.h"
 #include "gstvaapisurface.h"
 #define DEBUG 1
 #include "gstvaapidebug.h"
 
+/* Define the maximum number of views supported */
+#define MAX_NUM_VIEWS 2
+
+/* Define the maximum IDR period */
+#define MAX_IDR_PERIOD 512
+
+/* Default CPB length (in milliseconds) */
+#define DEFAULT_CPB_LENGTH 1500
+
+/* Scale factor for CPB size (HRD cpb_size_scale: min = 4) */
+#define SX_CPB_SIZE 4
+
+/* Scale factor for bitrate (HRD bit_rate_scale: min = 6) */
+#define SX_BITRATE 6
+
 /* 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 (NONE) |                  \
-   GST_VAAPI_RATECONTROL_MASK (CQP)  |                  \
+  (GST_VAAPI_RATECONTROL_MASK (CQP)  |                  \
    GST_VAAPI_RATECONTROL_MASK (CBR)  |                  \
    GST_VAAPI_RATECONTROL_MASK (VBR)  |                  \
    GST_VAAPI_RATECONTROL_MASK (VBR_CONSTRAINED))
   (GST_VAAPI_ENCODER_TUNE_MASK (NONE) |                 \
    GST_VAAPI_ENCODER_TUNE_MASK (HIGH_COMPRESSION))
 
-#define GST_VAAPI_ENCODER_H264_NAL_REF_IDC_NONE        0
-#define GST_VAAPI_ENCODER_H264_NAL_REF_IDC_LOW         1
-#define GST_VAAPI_ENCODER_H264_NAL_REF_IDC_MEDIUM      2
-#define GST_VAAPI_ENCODER_H264_NAL_REF_IDC_HIGH        3
+/* 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    |              \
+   VA_ENC_PACKED_HEADER_RAW_DATA)
 
-typedef enum
-{
-  GST_VAAPI_ENCODER_H264_NAL_UNKNOWN = 0,
-  GST_VAAPI_ENCODER_H264_NAL_NON_IDR = 1,
-  GST_VAAPI_ENCODER_H264_NAL_IDR = 5,   /* ref_idc != 0 */
-  GST_VAAPI_ENCODER_H264_NAL_SEI = 6,   /* ref_idc == 0 */
-  GST_VAAPI_ENCODER_H264_NAL_SPS = 7,
-  GST_VAAPI_ENCODER_H264_NAL_PPS = 8
-} GstVaapiEncoderH264NalType;
-
-typedef enum
-{
-  SLICE_TYPE_P = 0,
-  SLICE_TYPE_B = 1,
-  SLICE_TYPE_I = 2
-} H264_SLICE_TYPE;
+#define GST_H264_NAL_REF_IDC_NONE        0
+#define GST_H264_NAL_REF_IDC_LOW         1
+#define GST_H264_NAL_REF_IDC_MEDIUM      2
+#define GST_H264_NAL_REF_IDC_HIGH        3
 
 typedef struct
 {
@@ -85,6 +93,23 @@ typedef enum
   GST_VAAPI_ENC_H264_REORD_WAIT_FRAMES = 2
 } GstVaapiEncH264ReorderState;
 
+typedef struct _GstVaapiH264ViewRefPool
+{
+  GQueue ref_list;
+  guint max_ref_frames;
+  guint max_reflist0_count;
+  guint max_reflist1_count;
+} GstVaapiH264ViewRefPool;
+
+typedef struct _GstVaapiH264ViewReorderPool
+{
+  GQueue reorder_frame_list;
+  guint reorder_state;
+  guint frame_index;
+  guint cur_frame_num;
+  guint cur_present_index;
+} GstVaapiH264ViewReorderPool;
+
 static inline gboolean
 _poc_greater_than (guint poc1, guint poc2, guint max_poc)
 {
@@ -97,33 +122,759 @@ h264_get_slice_type (GstVaapiPictureType type)
 {
   switch (type) {
     case GST_VAAPI_PICTURE_TYPE_I:
-      return 2;
+      return GST_H264_I_SLICE;
     case GST_VAAPI_PICTURE_TYPE_P:
-      return 0;
+      return GST_H264_P_SLICE;
     case GST_VAAPI_PICTURE_TYPE_B:
-      return 1;
+      return GST_H264_B_SLICE;
+    default:
+      break;
+  }
+  return -1;
+}
+
+/* Get log2_max_frame_num value for H.264 specification */
+static guint
+h264_get_log2_max_frame_num (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;
+}
+
+/* Determines the cpbBrNalFactor based on the supplied profile */
+static guint
+h264_get_cpb_nal_factor (GstVaapiProfile profile)
+{
+  guint f;
+
+  /* Table A-2 */
+  switch (profile) {
+    case GST_VAAPI_PROFILE_H264_HIGH:
+      f = 1500;
+      break;
+    case GST_VAAPI_PROFILE_H264_HIGH10:
+      f = 3600;
+      break;
+    case GST_VAAPI_PROFILE_H264_HIGH_422:
+    case GST_VAAPI_PROFILE_H264_HIGH_444:
+      f = 4800;
+      break;
     default:
-      return -1;
+      f = 1200;
+      break;
+  }
+  return f;
+}
+
+/* ------------------------------------------------------------------------- */
+/* --- H.264 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_ref_idc,
+    guint32 nal_unit_type)
+{
+  WRITE_UINT32 (bs, 0, 1);
+  WRITE_UINT32 (bs, nal_ref_idc, 2);
+  WRITE_UINT32 (bs, nal_unit_type, 5);
+  return TRUE;
+
+  /* ERRORS */
+bs_error:
+  {
+    GST_WARNING ("failed to write NAL unit header");
+    return FALSE;
+  }
+}
+
+/* Write the MVC NAL unit header extension */
+static gboolean
+bs_write_nal_header_mvc_extension (GstBitWriter * bs,
+    GstVaapiEncPicture * picture, guint32 view_id)
+{
+  guint32 svc_extension_flag = 0;
+  guint32 non_idr_flag = 1;
+  guint32 priority_id = 0;
+  guint32 temporal_id = 0;
+  guint32 anchor_pic_flag = 0;
+  guint32 inter_view_flag = 0;
+
+  if (GST_VAAPI_ENC_PICTURE_IS_IDR (picture))
+    non_idr_flag = 0;
+
+  if (picture->type == GST_VAAPI_PICTURE_TYPE_I)
+    anchor_pic_flag = 1;
+  /* svc_extension_flag == 0 for mvc stream */
+  WRITE_UINT32 (bs, svc_extension_flag, 1);
+
+  WRITE_UINT32 (bs, non_idr_flag, 1);
+  WRITE_UINT32 (bs, priority_id, 6);
+  WRITE_UINT32 (bs, view_id, 10);
+  WRITE_UINT32 (bs, temporal_id, 3);
+  WRITE_UINT32 (bs, anchor_pic_flag, 1);
+  WRITE_UINT32 (bs, inter_view_flag, 1);
+  WRITE_UINT32 (bs, 1, 1);
+
+  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 an SPS NAL unit */
+static gboolean
+bs_write_sps_data (GstBitWriter * bs,
+    const VAEncSequenceParameterBufferH264 * seq_param, GstVaapiProfile profile,
+    const VAEncMiscParameterHRD * hrd_params)
+{
+  guint8 profile_idc;
+  guint32 constraint_set0_flag, constraint_set1_flag;
+  guint32 constraint_set2_flag, constraint_set3_flag;
+  guint32 gaps_in_frame_num_value_allowed_flag = 0;     // ??
+  gboolean nal_hrd_parameters_present_flag;
+
+  guint32 b_qpprime_y_zero_transform_bypass = 0;
+  guint32 residual_color_transform_flag = 0;
+  guint32 pic_height_in_map_units =
+      (seq_param->seq_fields.bits.frame_mbs_only_flag ?
+      seq_param->picture_height_in_mbs : seq_param->picture_height_in_mbs / 2);
+  guint32 mb_adaptive_frame_field =
+      !seq_param->seq_fields.bits.frame_mbs_only_flag;
+  guint32 i = 0;
+
+  profile_idc = gst_vaapi_utils_h264_get_profile_idc (profile);
+  constraint_set0_flag =        /* A.2.1 (baseline profile constraints) */
+      profile == GST_VAAPI_PROFILE_H264_BASELINE ||
+      profile == GST_VAAPI_PROFILE_H264_CONSTRAINED_BASELINE;
+  constraint_set1_flag =        /* A.2.2 (main profile constraints) */
+      profile == GST_VAAPI_PROFILE_H264_MAIN ||
+      profile == GST_VAAPI_PROFILE_H264_CONSTRAINED_BASELINE;
+  constraint_set2_flag = 0;
+  constraint_set3_flag = 0;
+
+  /* profile_idc */
+  WRITE_UINT32 (bs, profile_idc, 8);
+  /* constraint_set0_flag */
+  WRITE_UINT32 (bs, constraint_set0_flag, 1);
+  /* constraint_set1_flag */
+  WRITE_UINT32 (bs, constraint_set1_flag, 1);
+  /* constraint_set2_flag */
+  WRITE_UINT32 (bs, constraint_set2_flag, 1);
+  /* constraint_set3_flag */
+  WRITE_UINT32 (bs, constraint_set3_flag, 1);
+  /* reserved_zero_4bits */
+  WRITE_UINT32 (bs, 0, 4);
+  /* level_idc */
+  WRITE_UINT32 (bs, seq_param->level_idc, 8);
+  /* seq_parameter_set_id */
+  WRITE_UE (bs, seq_param->seq_parameter_set_id);
+
+  if (profile == GST_VAAPI_PROFILE_H264_HIGH ||
+      profile == GST_VAAPI_PROFILE_H264_MULTIVIEW_HIGH ||
+      profile == GST_VAAPI_PROFILE_H264_STEREO_HIGH) {
+    /* for high profile */
+    /* chroma_format_idc  = 1, 4:2:0 */
+    WRITE_UE (bs, seq_param->seq_fields.bits.chroma_format_idc);
+    if (3 == seq_param->seq_fields.bits.chroma_format_idc) {
+      WRITE_UINT32 (bs, residual_color_transform_flag, 1);
+    }
+    /* bit_depth_luma_minus8 */
+    WRITE_UE (bs, seq_param->bit_depth_luma_minus8);
+    /* bit_depth_chroma_minus8 */
+    WRITE_UE (bs, seq_param->bit_depth_chroma_minus8);
+    /* b_qpprime_y_zero_transform_bypass */
+    WRITE_UINT32 (bs, b_qpprime_y_zero_transform_bypass, 1);
+
+    /* seq_scaling_matrix_present_flag  */
+    g_assert (seq_param->seq_fields.bits.seq_scaling_matrix_present_flag == 0);
+    WRITE_UINT32 (bs,
+        seq_param->seq_fields.bits.seq_scaling_matrix_present_flag, 1);
+
+#if 0
+    if (seq_param->seq_fields.bits.seq_scaling_matrix_present_flag) {
+      for (i = 0;
+          i < (seq_param->seq_fields.bits.chroma_format_idc != 3 ? 8 : 12);
+          i++) {
+        gst_bit_writer_put_bits_uint8 (bs,
+            seq_param->seq_fields.bits.seq_scaling_list_present_flag, 1);
+        if (seq_param->seq_fields.bits.seq_scaling_list_present_flag) {
+          g_assert (0);
+          /* FIXME, need write scaling list if seq_scaling_matrix_present_flag ==1 */
+        }
+      }
+    }
+#endif
+  }
+
+  /* log2_max_frame_num_minus4 */
+  WRITE_UE (bs, seq_param->seq_fields.bits.log2_max_frame_num_minus4);
+  /* pic_order_cnt_type */
+  WRITE_UE (bs, seq_param->seq_fields.bits.pic_order_cnt_type);
+
+  if (seq_param->seq_fields.bits.pic_order_cnt_type == 0) {
+    /* log2_max_pic_order_cnt_lsb_minus4 */
+    WRITE_UE (bs, seq_param->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4);
+  } else if (seq_param->seq_fields.bits.pic_order_cnt_type == 1) {
+    g_assert (0 && "only POC type 0 is supported");
+    WRITE_UINT32 (bs,
+        seq_param->seq_fields.bits.delta_pic_order_always_zero_flag, 1);
+    WRITE_SE (bs, seq_param->offset_for_non_ref_pic);
+    WRITE_SE (bs, seq_param->offset_for_top_to_bottom_field);
+    WRITE_UE (bs, seq_param->num_ref_frames_in_pic_order_cnt_cycle);
+    for (i = 0; i < seq_param->num_ref_frames_in_pic_order_cnt_cycle; i++) {
+      WRITE_SE (bs, seq_param->offset_for_ref_frame[i]);
+    }
+  }
+
+  /* num_ref_frames */
+  WRITE_UE (bs, seq_param->max_num_ref_frames);
+  /* gaps_in_frame_num_value_allowed_flag */
+  WRITE_UINT32 (bs, gaps_in_frame_num_value_allowed_flag, 1);
+
+  /* pic_width_in_mbs_minus1 */
+  WRITE_UE (bs, seq_param->picture_width_in_mbs - 1);
+  /* pic_height_in_map_units_minus1 */
+  WRITE_UE (bs, pic_height_in_map_units - 1);
+  /* frame_mbs_only_flag */
+  WRITE_UINT32 (bs, seq_param->seq_fields.bits.frame_mbs_only_flag, 1);
+
+  if (!seq_param->seq_fields.bits.frame_mbs_only_flag) {        //ONLY mbs
+    g_assert (0 && "only progressive frames encoding is supported");
+    WRITE_UINT32 (bs, mb_adaptive_frame_field, 1);
+  }
+
+  /* direct_8x8_inference_flag */
+  WRITE_UINT32 (bs, 0, 1);
+  /* frame_cropping_flag */
+  WRITE_UINT32 (bs, seq_param->frame_cropping_flag, 1);
+
+  if (seq_param->frame_cropping_flag) {
+    /* frame_crop_left_offset */
+    WRITE_UE (bs, seq_param->frame_crop_left_offset);
+    /* frame_crop_right_offset */
+    WRITE_UE (bs, seq_param->frame_crop_right_offset);
+    /* frame_crop_top_offset */
+    WRITE_UE (bs, seq_param->frame_crop_top_offset);
+    /* frame_crop_bottom_offset */
+    WRITE_UE (bs, seq_param->frame_crop_bottom_offset);
+  }
+
+  /* vui_parameters_present_flag */
+  WRITE_UINT32 (bs, seq_param->vui_parameters_present_flag, 1);
+  if (seq_param->vui_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);
+
+    /* timing_info_present_flag */
+    WRITE_UINT32 (bs, seq_param->vui_fields.bits.timing_info_present_flag, 1);
+    if (seq_param->vui_fields.bits.timing_info_present_flag) {
+      WRITE_UINT32 (bs, seq_param->num_units_in_tick, 32);
+      WRITE_UINT32 (bs, seq_param->time_scale, 32);
+      WRITE_UINT32 (bs, 1, 1);  /* fixed_frame_rate_flag */
+    }
+
+    /* nal_hrd_parameters_present_flag */
+    nal_hrd_parameters_present_flag = seq_param->bits_per_second > 0;
+    nal_hrd_parameters_present_flag = FALSE;    /* XXX: disabled for now */
+    WRITE_UINT32 (bs, nal_hrd_parameters_present_flag, 1);
+    if (nal_hrd_parameters_present_flag) {
+      /* hrd_parameters */
+      /* cpb_cnt_minus1 */
+      WRITE_UE (bs, 0);
+      WRITE_UINT32 (bs, SX_BITRATE - 6, 4);     /* bit_rate_scale */
+      WRITE_UINT32 (bs, SX_CPB_SIZE - 4, 4);    /* cpb_size_scale */
+
+      for (i = 0; i < 1; ++i) {
+        /* bit_rate_value_minus1[0] */
+        WRITE_UE (bs, (seq_param->bits_per_second >> SX_BITRATE) - 1);
+        /* cpb_size_value_minus1[0] */
+        WRITE_UE (bs, (hrd_params->buffer_size >> SX_CPB_SIZE) - 1);
+        /* cbr_flag[0] */
+        WRITE_UINT32 (bs, 1, 1);
+      }
+      /* initial_cpb_removal_delay_length_minus1 */
+      WRITE_UINT32 (bs, 23, 5);
+      /* cpb_removal_delay_length_minus1 */
+      WRITE_UINT32 (bs, 23, 5);
+      /* dpb_output_delay_length_minus1 */
+      WRITE_UINT32 (bs, 23, 5);
+      /* time_offset_length  */
+      WRITE_UINT32 (bs, 23, 5);
+    }
+
+    /* vcl_hrd_parameters_present_flag */
+    WRITE_UINT32 (bs, 0, 1);
+
+    if (nal_hrd_parameters_present_flag
+        || 0 /*vcl_hrd_parameters_present_flag */ ) {
+      /* low_delay_hrd_flag */
+      WRITE_UINT32 (bs, 0, 1);
+    }
+    /* pic_struct_present_flag */
+    WRITE_UINT32 (bs, 0, 1);
+    /* bs_restriction_flag */
+    WRITE_UINT32 (bs, 0, 1);
+  }
+  return TRUE;
+
+  /* ERRORS */
+bs_error:
+  {
+    GST_WARNING ("failed to write SPS NAL unit");
+    return FALSE;
+  }
+}
+
+static gboolean
+bs_write_sps (GstBitWriter * bs,
+    const VAEncSequenceParameterBufferH264 * seq_param, GstVaapiProfile profile,
+    const VAEncMiscParameterHRD * hrd_params)
+{
+  if (!bs_write_sps_data (bs, seq_param, profile, hrd_params))
+    return FALSE;
+
+  /* rbsp_trailing_bits */
+  bs_write_trailing_bits (bs);
+
+  return FALSE;
+}
+
+static gboolean
+bs_write_subset_sps (GstBitWriter * bs,
+    const VAEncSequenceParameterBufferH264 * seq_param, GstVaapiProfile profile,
+    guint num_views, const VAEncMiscParameterHRD * hrd_params)
+{
+  guint32 i, j, k;
+
+  if (!bs_write_sps_data (bs, seq_param, profile, hrd_params))
+    return FALSE;
+
+  if (profile == GST_VAAPI_PROFILE_H264_STEREO_HIGH ||
+      profile == GST_VAAPI_PROFILE_H264_MULTIVIEW_HIGH) {
+    guint32 num_views_minus1, num_level_values_signalled_minus1;
+
+    num_views_minus1 = num_views - 1;
+    g_assert (num_views_minus1 < 1024);
+
+    /* bit equal to one */
+    WRITE_UINT32 (bs, 1, 1);
+
+    WRITE_UE (bs, num_views_minus1);
+
+    for (i = 0; i <= num_views_minus1; i++)
+      WRITE_UE (bs, i);
+
+    for (i = 1; i <= num_views_minus1; i++) {
+      guint32 num_anchor_refs_l0 = 15;
+      WRITE_UE (bs, num_anchor_refs_l0);
+      for (j = 0; j < num_anchor_refs_l0; j++)
+        WRITE_UE (bs, 0);
+
+      guint32 num_anchor_refs_l1 = 15;
+      WRITE_UE (bs, num_anchor_refs_l1);
+      for (j = 0; j < num_anchor_refs_l1; j++)
+        WRITE_UE (bs, 0);
+    }
+
+    for (i = 1; i <= num_views_minus1; i++) {
+      guint num_non_anchor_refs_l0 = 15;
+      WRITE_UE (bs, num_non_anchor_refs_l0);
+      for (j = 0; j < num_non_anchor_refs_l0; j++)
+        WRITE_UE (bs, 0);
+
+      guint num_non_anchor_refs_l1 = 15;
+      WRITE_UE (bs, num_non_anchor_refs_l1);
+      for (j = 0; j < num_non_anchor_refs_l1; j++)
+        WRITE_UE (bs, 0);
+    }
+
+    /* num level values signalled minus1 */
+    num_level_values_signalled_minus1 = 0;
+    g_assert (num_level_values_signalled_minus1 < 64);
+
+    for (i = 0; i <= num_level_values_signalled_minus1; i++) {
+      guint16 num_applicable_ops_minus1 = 0;
+      g_assert (num_applicable_ops_minus1 < 1024);
+
+      WRITE_UINT32 (bs, seq_param->level_idc, 8);
+      WRITE_UE (bs, num_applicable_ops_minus1);
+
+      for (j = 0; j <= num_applicable_ops_minus1; j++) {
+        guint8 temporal_id = 0;
+        guint16 num_target_views_minus1 = 1;
+
+        WRITE_UINT32 (bs, temporal_id, 3);
+        WRITE_UE (bs, num_target_views_minus1);
+
+        for (k = 0; k <= num_target_views_minus1; k++)
+          WRITE_UE (bs, k);
+
+        WRITE_UE (bs, num_views_minus1);
+      }
+    }
+
+    /* mvc_vui_parameters_present_flag */
+    WRITE_UINT32 (bs, 0, 1);
+  }
+
+  /* additional_extension2_flag */
+  WRITE_UINT32 (bs, 0, 1);
+
+  /* rbsp_trailing_bits */
+  bs_write_trailing_bits (bs);
+  return TRUE;
+
+  /* ERRORS */
+bs_error:
+  {
+    GST_WARNING ("failed to write subset SPS NAL unit");
+    return FALSE;
+  }
+  return FALSE;
+}
+
+/* Write a PPS NAL unit */
+static gboolean
+bs_write_pps (GstBitWriter * bs,
+    const VAEncPictureParameterBufferH264 * pic_param, GstVaapiProfile profile)
+{
+  guint32 num_slice_groups_minus1 = 0;
+  guint32 pic_init_qs_minus26 = 0;
+  guint32 redundant_pic_cnt_present_flag = 0;
+
+  /* pic_parameter_set_id */
+  WRITE_UE (bs, pic_param->pic_parameter_set_id);
+  /* seq_parameter_set_id */
+  WRITE_UE (bs, pic_param->seq_parameter_set_id);
+  /* entropy_coding_mode_flag */
+  WRITE_UINT32 (bs, pic_param->pic_fields.bits.entropy_coding_mode_flag, 1);
+  /* pic_order_present_flag */
+  WRITE_UINT32 (bs, pic_param->pic_fields.bits.pic_order_present_flag, 1);
+  /* slice_groups-1 */
+  WRITE_UE (bs, num_slice_groups_minus1);
+
+  if (num_slice_groups_minus1 > 0) {
+     /*FIXME*/ g_assert (0 && "unsupported arbitrary slice ordering (ASO)");
+  }
+  WRITE_UE (bs, pic_param->num_ref_idx_l0_active_minus1);
+  WRITE_UE (bs, pic_param->num_ref_idx_l1_active_minus1);
+  WRITE_UINT32 (bs, pic_param->pic_fields.bits.weighted_pred_flag, 1);
+  WRITE_UINT32 (bs, pic_param->pic_fields.bits.weighted_bipred_idc, 2);
+  /* pic_init_qp_minus26 */
+  WRITE_SE (bs, pic_param->pic_init_qp - 26);
+  /* pic_init_qs_minus26 */
+  WRITE_SE (bs, pic_init_qs_minus26);
+  /* chroma_qp_index_offset */
+  WRITE_SE (bs, pic_param->chroma_qp_index_offset);
+
+  WRITE_UINT32 (bs,
+      pic_param->pic_fields.bits.deblocking_filter_control_present_flag, 1);
+  WRITE_UINT32 (bs, pic_param->pic_fields.bits.constrained_intra_pred_flag, 1);
+  WRITE_UINT32 (bs, redundant_pic_cnt_present_flag, 1);
+
+  /* more_rbsp_data */
+  if (profile == GST_VAAPI_PROFILE_H264_HIGH) {
+    WRITE_UINT32 (bs, pic_param->pic_fields.bits.transform_8x8_mode_flag, 1);
+    WRITE_UINT32 (bs,
+        pic_param->pic_fields.bits.pic_scaling_matrix_present_flag, 1);
+    if (pic_param->pic_fields.bits.pic_scaling_matrix_present_flag) {
+      g_assert (0 && "unsupported scaling lists");
+      /* FIXME */
+      /*
+         for (i = 0; i <
+         (6+(-( (chroma_format_idc ! = 3) ? 2 : 6) * -pic_param->pic_fields.bits.transform_8x8_mode_flag));
+         i++) {
+         gst_bit_writer_put_bits_uint8(bs, pic_param->pic_fields.bits.pic_scaling_list_present_flag, 1);
+         }
+       */
+    }
+    WRITE_SE (bs, pic_param->second_chroma_qp_index_offset);
+  }
+
+  /* rbsp_trailing_bits */
+  bs_write_trailing_bits (bs);
+  return TRUE;
+
+  /* ERRORS */
+bs_error:
+  {
+    GST_WARNING ("failed to write PPS NAL unit");
+    return FALSE;
   }
-  return -1;
 }
 
-/* Get log2_max_frame_num value for H.264 specification */
-static guint
-h264_get_log2_max_frame_num (guint num)
+/* ------------------------------------------------------------------------- */
+/* --- H.264 Encoder                                                     --- */
+/* ------------------------------------------------------------------------- */
+
+#define GST_VAAPI_ENCODER_H264_CAST(encoder) \
+    ((GstVaapiEncoderH264 *)(encoder))
+
+struct _GstVaapiEncoderH264
 {
-  guint ret = 0;
+  GstVaapiEncoder parent_instance;
 
-  while (num) {
-    ++ret;
-    num >>= 1;
+  GstVaapiProfile profile;
+  GstVaapiLevelH264 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 mb_width;
+  guint32 mb_height;
+  gboolean use_cabac;
+  gboolean use_dct8x8;
+  GstClockTime cts_offset;
+
+  /* frame, poc */
+  guint32 max_frame_num;
+  guint32 log2_max_frame_num;
+  guint32 max_pic_order_cnt;
+  guint32 log2_max_pic_order_cnt;
+  guint32 idr_num;
+  guint8 pic_order_cnt_type;
+  guint8 delta_pic_order_always_zero_flag;
+
+  GstBuffer *sps_data;
+  GstBuffer *subset_sps_data;
+  GstBuffer *pps_data;
+
+  guint bitrate_bits;           // bitrate (bits)
+  guint cpb_length;             // length of CPB buffer (ms)
+  guint cpb_length_bits;        // length of CPB buffer (bits)
+
+  /* MVC */
+  gboolean is_mvc;
+  guint32 view_idx;
+  guint32 num_views;
+  GstVaapiH264ViewRefPool ref_pools[MAX_NUM_VIEWS];
+  GstVaapiH264ViewReorderPool reorder_pools[MAX_NUM_VIEWS];
+};
+
+/* Write a Slice NAL unit */
+static gboolean
+bs_write_slice (GstBitWriter * bs,
+    const VAEncSliceParameterBufferH264 * slice_param,
+    GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture)
+{
+  const VAEncPictureParameterBufferH264 *const pic_param = picture->param;
+  guint32 field_pic_flag = 0;
+  guint32 ref_pic_list_modification_flag_l0 = 0;
+  guint32 ref_pic_list_modification_flag_l1 = 0;
+  guint32 no_output_of_prior_pics_flag = 0;
+  guint32 long_term_reference_flag = 0;
+  guint32 adaptive_ref_pic_marking_mode_flag = 0;
+
+  /* first_mb_in_slice */
+  WRITE_UE (bs, slice_param->macroblock_address);
+  /* slice_type */
+  WRITE_UE (bs, slice_param->slice_type);
+  /* pic_parameter_set_id */
+  WRITE_UE (bs, slice_param->pic_parameter_set_id);
+  /* frame_num */
+  WRITE_UINT32 (bs, picture->frame_num, encoder->log2_max_frame_num);
+
+  /* XXX: only frames (i.e. non-interlaced) are supported for now */
+  /* frame_mbs_only_flag == 0 */
+
+  /* idr_pic_id */
+  if (GST_VAAPI_ENC_PICTURE_IS_IDR (picture))
+    WRITE_UE (bs, slice_param->idr_pic_id);
+
+  /* XXX: only POC type 0 is supported */
+  if (!encoder->pic_order_cnt_type) {
+    WRITE_UINT32 (bs, slice_param->pic_order_cnt_lsb,
+        encoder->log2_max_pic_order_cnt);
+    /* bottom_field_pic_order_in_frame_present_flag is FALSE */
+    if (pic_param->pic_fields.bits.pic_order_present_flag && !field_pic_flag)
+      WRITE_SE (bs, slice_param->delta_pic_order_cnt_bottom);
+  } else if (encoder->pic_order_cnt_type == 1 &&
+      !encoder->delta_pic_order_always_zero_flag) {
+    WRITE_SE (bs, slice_param->delta_pic_order_cnt[0]);
+    if (pic_param->pic_fields.bits.pic_order_present_flag && !field_pic_flag)
+      WRITE_SE (bs, slice_param->delta_pic_order_cnt[1]);
+  }
+  /* redundant_pic_cnt_present_flag is FALSE, no redundant coded pictures */
+
+  /* only works for B-frames */
+  if (slice_param->slice_type == 1)
+    WRITE_UINT32 (bs, slice_param->direct_spatial_mv_pred_flag, 1);
+
+  /* not supporting SP slices */
+  if (slice_param->slice_type == 0 || slice_param->slice_type == 1) {
+    WRITE_UINT32 (bs, slice_param->num_ref_idx_active_override_flag, 1);
+    if (slice_param->num_ref_idx_active_override_flag) {
+      WRITE_UE (bs, slice_param->num_ref_idx_l0_active_minus1);
+      if (slice_param->slice_type == 1)
+        WRITE_UE (bs, slice_param->num_ref_idx_l1_active_minus1);
+    }
+  }
+  /* XXX: not supporting custom reference picture list modifications */
+  if ((slice_param->slice_type != 2) && (slice_param->slice_type != 4))
+    WRITE_UINT32 (bs, ref_pic_list_modification_flag_l0, 1);
+  if (slice_param->slice_type == 1)
+    WRITE_UINT32 (bs, ref_pic_list_modification_flag_l1, 1);
+
+  /* we have: weighted_pred_flag == FALSE and */
+  /*        : weighted_bipred_idc == FALSE */
+  if ((pic_param->pic_fields.bits.weighted_pred_flag &&
+          (slice_param->slice_type == 0)) ||
+      ((pic_param->pic_fields.bits.weighted_bipred_idc == 1) &&
+          (slice_param->slice_type == 1))) {
+    /* XXXX: add pred_weight_table() */
+  }
+
+  /* dec_ref_pic_marking() */
+  if (slice_param->slice_type == 0 || slice_param->slice_type == 2) {
+    if (GST_VAAPI_ENC_PICTURE_IS_IDR (picture)) {
+      /* no_output_of_prior_pics_flag = 0 */
+      WRITE_UINT32 (bs, no_output_of_prior_pics_flag, 1);
+      /* long_term_reference_flag = 0 */
+      WRITE_UINT32 (bs, long_term_reference_flag, 1);
+    } else {
+      /* only sliding_window reference picture marking mode is supported */
+      /* adpative_ref_pic_marking_mode_flag = 0 */
+      WRITE_UINT32 (bs, adaptive_ref_pic_marking_mode_flag, 1);
+    }
+  }
+
+  /* cabac_init_idc */
+  if (pic_param->pic_fields.bits.entropy_coding_mode_flag &&
+      slice_param->slice_type != 2)
+    WRITE_UE (bs, slice_param->cabac_init_idc);
+  /*slice_qp_delta */
+  WRITE_SE (bs, slice_param->slice_qp_delta);
+
+  /* XXX: only supporting I, P and B type slices */
+  /* no sp_for_switch_flag and no slice_qs_delta */
+
+  if (pic_param->pic_fields.bits.deblocking_filter_control_present_flag) {
+    /* disable_deblocking_filter_idc */
+    WRITE_UE (bs, slice_param->disable_deblocking_filter_idc);
+    if (slice_param->disable_deblocking_filter_idc != 1) {
+      WRITE_SE (bs, slice_param->slice_alpha_c0_offset_div2);
+      WRITE_SE (bs, slice_param->slice_beta_offset_div2);
+    }
+  }
+
+  /* XXX: unsupported arbitrary slice ordering (ASO) */
+  /* num_slic_groups_minus1 should be zero */
+  return TRUE;
+
+  /* ERRORS */
+bs_error:
+  {
+    GST_WARNING ("failed to write Slice NAL unit");
+    return FALSE;
   }
-  if (ret <= 4)
-    ret = 4;
-  else if (ret > 10)
-    ret = 10;
-  /* must greater than 4 */
-  return ret;
 }
 
 static inline void
@@ -132,20 +883,27 @@ _check_sps_pps_status (GstVaapiEncoderH264 * encoder,
 {
   guint8 nal_type;
   gsize ret;
+  gboolean has_subset_sps;
 
   g_assert (size);
 
-  if (encoder->sps_data && encoder->pps_data)
+  has_subset_sps = !encoder->is_mvc || (encoder->subset_sps_data != NULL);
+  if (encoder->sps_data && encoder->pps_data && has_subset_sps)
     return;
 
   nal_type = nal[0] & 0x1F;
   switch (nal_type) {
-    case GST_VAAPI_ENCODER_H264_NAL_SPS:
+    case GST_H264_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_VAAPI_ENCODER_H264_NAL_PPS:
+    case GST_H264_NAL_SUBSET_SPS:
+      encoder->subset_sps_data = gst_buffer_new_allocate (NULL, size, NULL);
+      ret = gst_buffer_fill (encoder->subset_sps_data, 0, nal, size);
+      g_assert (ret == size);
+      break;
+    case GST_H264_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);
@@ -277,15 +1035,22 @@ ensure_profile (GstVaapiEncoderH264 * encoder)
   if (encoder->use_dct8x8)
     profile = GST_VAAPI_PROFILE_H264_HIGH;
 
+  /* MVC profiles coding tools */
+  if (encoder->num_views == 2)
+    profile = GST_VAAPI_PROFILE_H264_STEREO_HIGH;
+  else if (encoder->num_views > 2)
+    profile = GST_VAAPI_PROFILE_H264_MULTIVIEW_HIGH;
+
   encoder->profile = profile;
   encoder->profile_idc = gst_vaapi_utils_h264_get_profile_idc (profile);
   return TRUE;
 }
 
+/* Derives the level from the currently set limits */
 static gboolean
 ensure_level (GstVaapiEncoderH264 * encoder)
 {
-  const guint bitrate = GST_VAAPI_ENCODER_CAST (encoder)->bitrate;
+  const guint cpb_factor = h264_get_cpb_nal_factor (encoder->profile);
   const GstVaapiH264LevelLimits *limits_table;
   guint i, num_limits, PicSizeMbs, MaxDpbMbs, MaxMBPS;
 
@@ -299,7 +1064,10 @@ ensure_level (GstVaapiEncoderH264 * encoder)
     const GstVaapiH264LevelLimits *const limits = &limits_table[i];
     if (PicSizeMbs <= limits->MaxFS &&
         MaxDpbMbs <= limits->MaxDpbMbs &&
-        MaxMBPS <= limits->MaxMBPS && (!bitrate || bitrate <= limits->MaxBR))
+        MaxMBPS <= limits->MaxMBPS && (!encoder->bitrate_bits
+            || encoder->bitrate_bits <= (limits->MaxBR * cpb_factor)) &&
+        (!encoder->cpb_length_bits ||
+            encoder->cpb_length_bits <= (limits->MaxCPB * cpb_factor)))
       break;
   }
   if (i == num_limits)
@@ -331,7 +1099,7 @@ ensure_tuning_high_compression (GstVaapiEncoderH264 * encoder)
     profile_idc = encoder->max_profile_idc;
 
   /* Tuning options to enable Main profile */
-  if (profile_idc >= 77) {
+  if (profile_idc >= 77 && profile_idc != 88) {
     encoder->use_cabac = TRUE;
     if (!encoder->num_bframes)
       encoder->num_bframes = 1;
@@ -361,45 +1129,62 @@ ensure_tuning (GstVaapiEncoderH264 * encoder)
   return success;
 }
 
-static inline void
-_reset_gop_start (GstVaapiEncoderH264 * encoder)
+/* Handle new GOP starts */
+static void
+reset_gop_start (GstVaapiEncoderH264 * encoder)
 {
+  GstVaapiH264ViewReorderPool *const reorder_pool =
+      &encoder->reorder_pools[encoder->view_idx];
+
+  reorder_pool->frame_index = 1;
+  reorder_pool->cur_frame_num = 0;
+  reorder_pool->cur_present_index = 0;
   ++encoder->idr_num;
-  encoder->frame_index = 1;
-  encoder->cur_frame_num = 0;
-  encoder->cur_present_index = 0;
 }
 
+/* Marks the supplied picture as a B-frame */
 static void
-_set_b_frame (GstVaapiEncPicture * pic, GstVaapiEncoderH264 * encoder)
+set_b_frame (GstVaapiEncPicture * pic, GstVaapiEncoderH264 * encoder)
 {
+  GstVaapiH264ViewReorderPool *const reorder_pool =
+      &encoder->reorder_pools[encoder->view_idx];
+
   g_assert (pic && encoder);
   g_return_if_fail (pic->type == GST_VAAPI_PICTURE_TYPE_NONE);
   pic->type = GST_VAAPI_PICTURE_TYPE_B;
-  pic->frame_num = (encoder->cur_frame_num % encoder->max_frame_num);
+  pic->frame_num = (reorder_pool->cur_frame_num % encoder->max_frame_num);
 }
 
-static inline void
-_set_p_frame (GstVaapiEncPicture * pic, GstVaapiEncoderH264 * encoder)
+/* Marks the supplied picture as a P-frame */
+static void
+set_p_frame (GstVaapiEncPicture * pic, GstVaapiEncoderH264 * encoder)
 {
+  GstVaapiH264ViewReorderPool *const reorder_pool =
+      &encoder->reorder_pools[encoder->view_idx];
+
   g_return_if_fail (pic->type == GST_VAAPI_PICTURE_TYPE_NONE);
   pic->type = GST_VAAPI_PICTURE_TYPE_P;
-  pic->frame_num = (encoder->cur_frame_num % encoder->max_frame_num);
+  pic->frame_num = (reorder_pool->cur_frame_num % encoder->max_frame_num);
 }
 
-static inline void
-_set_i_frame (GstVaapiEncPicture * pic, GstVaapiEncoderH264 * encoder)
+/* Marks the supplied picture as an I-frame */
+static void
+set_i_frame (GstVaapiEncPicture * pic, GstVaapiEncoderH264 * encoder)
 {
+  GstVaapiH264ViewReorderPool *const reorder_pool =
+      &encoder->reorder_pools[encoder->view_idx];
+
   g_return_if_fail (pic->type == GST_VAAPI_PICTURE_TYPE_NONE);
   pic->type = GST_VAAPI_PICTURE_TYPE_I;
-  pic->frame_num = (encoder->cur_frame_num % encoder->max_frame_num);
+  pic->frame_num = (reorder_pool->cur_frame_num % encoder->max_frame_num);
 
   g_assert (pic->frame);
   GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (pic->frame);
 }
 
-static inline void
-_set_idr_frame (GstVaapiEncPicture * pic, GstVaapiEncoderH264 * encoder)
+/* Marks the supplied picture as an IDR frame */
+static void
+set_idr_frame (GstVaapiEncPicture * pic, GstVaapiEncoderH264 * encoder)
 {
   g_return_if_fail (pic->type == GST_VAAPI_PICTURE_TYPE_NONE);
   pic->type = GST_VAAPI_PICTURE_TYPE_I;
@@ -411,379 +1196,117 @@ _set_idr_frame (GstVaapiEncPicture * pic, GstVaapiEncoderH264 * encoder)
   GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (pic->frame);
 }
 
-static inline void
-_set_key_frame (GstVaapiEncPicture * picture,
+/* Marks the supplied picture a a key-frame */
+static void
+set_key_frame (GstVaapiEncPicture * picture,
     GstVaapiEncoderH264 * encoder, gboolean is_idr)
 {
   if (is_idr) {
-    _reset_gop_start (encoder);
-    _set_idr_frame (picture, encoder);
+    reset_gop_start (encoder);
+    set_idr_frame (picture, encoder);
   } else
-    _set_i_frame (picture, encoder);
+    set_i_frame (picture, encoder);
 }
 
-gboolean
-gst_bit_writer_put_ue (GstBitWriter * bitwriter, guint32 value)
+/* Fills in VA HRD parameters */
+static void
+fill_hrd_params (GstVaapiEncoderH264 * encoder, VAEncMiscParameterHRD * hrd)
 {
-  guint32 size_in_bits = 0;
-  guint32 tmp_value = ++value;
-
-  while (tmp_value) {
-    ++size_in_bits;
-    tmp_value >>= 1;
+  if (encoder->bitrate_bits > 0) {
+    hrd->buffer_size = encoder->cpb_length_bits;
+    hrd->initial_buffer_fullness = hrd->buffer_size / 2;
+  } else {
+    hrd->buffer_size = 0;
+    hrd->initial_buffer_fullness = 0;
   }
-  if (size_in_bits > 1
-      && !gst_bit_writer_put_bits_uint32 (bitwriter, 0, size_in_bits - 1))
-    return FALSE;
-  if (!gst_bit_writer_put_bits_uint32 (bitwriter, value, size_in_bits))
-    return FALSE;
-  return TRUE;
-}
-
-gboolean
-gst_bit_writer_put_se (GstBitWriter * bitwriter, gint32 value)
-{
-  guint32 new_val;
-
-  if (value <= 0)
-    new_val = -(value << 1);
-  else
-    new_val = (value << 1) - 1;
-
-  if (!gst_bit_writer_put_ue (bitwriter, new_val))
-    return FALSE;
-  return TRUE;
-}
-
-
-static gboolean
-gst_bit_writer_write_nal_header (GstBitWriter * bitwriter,
-    guint32 nal_ref_idc, guint32 nal_unit_type)
-{
-  gst_bit_writer_put_bits_uint32 (bitwriter, 0, 1);
-  gst_bit_writer_put_bits_uint32 (bitwriter, nal_ref_idc, 2);
-  gst_bit_writer_put_bits_uint32 (bitwriter, nal_unit_type, 5);
-  return TRUE;
-}
-
-static gboolean
-gst_bit_writer_write_trailing_bits (GstBitWriter * bitwriter)
-{
-  gst_bit_writer_put_bits_uint32 (bitwriter, 1, 1);
-  gst_bit_writer_align_bytes_unchecked (bitwriter, 0);
-  return TRUE;
 }
 
+/* Adds the supplied sequence header (SPS) to the list of packed
+   headers to pass down as-is to the encoder */
 static gboolean
-gst_bit_writer_write_sps (GstBitWriter * bitwriter,
-    const VAEncSequenceParameterBufferH264 * seq_param, GstVaapiProfile profile)
+add_packed_sequence_header (GstVaapiEncoderH264 * encoder,
+    GstVaapiEncPicture * picture, GstVaapiEncSequence * sequence)
 {
-  guint8 profile_idc;
-  guint32 constraint_set0_flag, constraint_set1_flag;
-  guint32 constraint_set2_flag, constraint_set3_flag;
-  guint32 gaps_in_frame_num_value_allowed_flag = 0;     // ??
-  gboolean nal_hrd_parameters_present_flag;
-
-  guint32 b_qpprime_y_zero_transform_bypass = 0;
-  guint32 residual_color_transform_flag = 0;
-  guint32 pic_height_in_map_units =
-      (seq_param->seq_fields.bits.frame_mbs_only_flag ?
-      seq_param->picture_height_in_mbs : seq_param->picture_height_in_mbs / 2);
-  guint32 mb_adaptive_frame_field =
-      !seq_param->seq_fields.bits.frame_mbs_only_flag;
-  guint32 i = 0;
-
-  profile_idc = gst_vaapi_utils_h264_get_profile_idc (profile);
-  constraint_set0_flag =        /* A.2.1 (baseline profile constraints) */
-      profile == GST_VAAPI_PROFILE_H264_BASELINE ||
-      profile == GST_VAAPI_PROFILE_H264_CONSTRAINED_BASELINE;
-  constraint_set1_flag =        /* A.2.2 (main profile constraints) */
-      profile == GST_VAAPI_PROFILE_H264_MAIN ||
-      profile == GST_VAAPI_PROFILE_H264_CONSTRAINED_BASELINE;
-  constraint_set2_flag = 0;
-  constraint_set3_flag = 0;
-
-  /* profile_idc */
-  gst_bit_writer_put_bits_uint32 (bitwriter, profile_idc, 8);
-  /* constraint_set0_flag */
-  gst_bit_writer_put_bits_uint32 (bitwriter, constraint_set0_flag, 1);
-  /* constraint_set1_flag */
-  gst_bit_writer_put_bits_uint32 (bitwriter, constraint_set1_flag, 1);
-  /* constraint_set2_flag */
-  gst_bit_writer_put_bits_uint32 (bitwriter, constraint_set2_flag, 1);
-  /* constraint_set3_flag */
-  gst_bit_writer_put_bits_uint32 (bitwriter, constraint_set3_flag, 1);
-  /* reserved_zero_4bits */
-  gst_bit_writer_put_bits_uint32 (bitwriter, 0, 4);
-  /* level_idc */
-  gst_bit_writer_put_bits_uint32 (bitwriter, seq_param->level_idc, 8);
-  /* seq_parameter_set_id */
-  gst_bit_writer_put_ue (bitwriter, seq_param->seq_parameter_set_id);
-
-  if (profile == GST_VAAPI_PROFILE_H264_HIGH) {
-    /* for high profile */
-    /* chroma_format_idc  = 1, 4:2:0 */
-    gst_bit_writer_put_ue (bitwriter,
-        seq_param->seq_fields.bits.chroma_format_idc);
-    if (3 == seq_param->seq_fields.bits.chroma_format_idc) {
-      gst_bit_writer_put_bits_uint32 (bitwriter, residual_color_transform_flag,
-          1);
-    }
-    /* bit_depth_luma_minus8 */
-    gst_bit_writer_put_ue (bitwriter, seq_param->bit_depth_luma_minus8);
-    /* bit_depth_chroma_minus8 */
-    gst_bit_writer_put_ue (bitwriter, seq_param->bit_depth_chroma_minus8);
-    /* b_qpprime_y_zero_transform_bypass */
-    gst_bit_writer_put_bits_uint32 (bitwriter,
-        b_qpprime_y_zero_transform_bypass, 1);
-    g_assert (seq_param->seq_fields.bits.seq_scaling_matrix_present_flag == 0);
-    /* seq_scaling_matrix_present_flag  */
-    gst_bit_writer_put_bits_uint32 (bitwriter,
-        seq_param->seq_fields.bits.seq_scaling_matrix_present_flag, 1);
-
-#if 0
-    if (seq_param->seq_fields.bits.seq_scaling_matrix_present_flag) {
-      for (i = 0;
-          i < (seq_param->seq_fields.bits.chroma_format_idc != 3 ? 8 : 12);
-          i++) {
-        gst_bit_writer_put_bits_uint8 (bitwriter,
-            seq_param->seq_fields.bits.seq_scaling_list_present_flag, 1);
-        if (seq_param->seq_fields.bits.seq_scaling_list_present_flag) {
-          g_assert (0);
-          /* FIXME, need write scaling list if seq_scaling_matrix_present_flag ==1 */
-        }
-      }
-    }
-#endif
-  }
-
-  /* log2_max_frame_num_minus4 */
-  gst_bit_writer_put_ue (bitwriter,
-      seq_param->seq_fields.bits.log2_max_frame_num_minus4);
-  /* pic_order_cnt_type */
-  gst_bit_writer_put_ue (bitwriter,
-      seq_param->seq_fields.bits.pic_order_cnt_type);
-
-  if (seq_param->seq_fields.bits.pic_order_cnt_type == 0) {
-    /* log2_max_pic_order_cnt_lsb_minus4 */
-    gst_bit_writer_put_ue (bitwriter,
-        seq_param->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4);
-  } else if (seq_param->seq_fields.bits.pic_order_cnt_type == 1) {
-    g_assert (0);
-    gst_bit_writer_put_bits_uint32 (bitwriter,
-        seq_param->seq_fields.bits.delta_pic_order_always_zero_flag, 1);
-    gst_bit_writer_put_se (bitwriter, seq_param->offset_for_non_ref_pic);
-    gst_bit_writer_put_se (bitwriter,
-        seq_param->offset_for_top_to_bottom_field);
-    gst_bit_writer_put_ue (bitwriter,
-        seq_param->num_ref_frames_in_pic_order_cnt_cycle);
-    for (i = 0; i < seq_param->num_ref_frames_in_pic_order_cnt_cycle; i++) {
-      gst_bit_writer_put_se (bitwriter, seq_param->offset_for_ref_frame[i]);
-    }
-  }
-
-  /* num_ref_frames */
-  gst_bit_writer_put_ue (bitwriter, seq_param->max_num_ref_frames);
-  /* gaps_in_frame_num_value_allowed_flag */
-  gst_bit_writer_put_bits_uint32 (bitwriter,
-      gaps_in_frame_num_value_allowed_flag, 1);
+  GstVaapiEncPackedHeader *packed_seq;
+  GstBitWriter bs;
+  VAEncPackedHeaderParameterBuffer packed_seq_param = { 0 };
+  const VAEncSequenceParameterBufferH264 *const seq_param = sequence->param;
+  GstVaapiProfile profile = encoder->profile;
 
-  /* pic_width_in_mbs_minus1 */
-  gst_bit_writer_put_ue (bitwriter, seq_param->picture_width_in_mbs - 1);
-  /* pic_height_in_map_units_minus1 */
-  gst_bit_writer_put_ue (bitwriter, pic_height_in_map_units - 1);
-  /* frame_mbs_only_flag */
-  gst_bit_writer_put_bits_uint32 (bitwriter,
-      seq_param->seq_fields.bits.frame_mbs_only_flag, 1);
+  VAEncMiscParameterHRD hrd_params;
+  guint32 data_bit_size;
+  guint8 *data;
 
-  if (!seq_param->seq_fields.bits.frame_mbs_only_flag) {        //ONLY mbs
-    g_assert (0);
-    gst_bit_writer_put_bits_uint32 (bitwriter, mb_adaptive_frame_field, 1);
-  }
+  fill_hrd_params (encoder, &hrd_params);
 
-  /* direct_8x8_inference_flag */
-  gst_bit_writer_put_bits_uint32 (bitwriter, 0, 1);
-  /* frame_cropping_flag */
-  gst_bit_writer_put_bits_uint32 (bitwriter, seq_param->frame_cropping_flag, 1);
+  gst_bit_writer_init (&bs, 128 * 8);
+  WRITE_UINT32 (&bs, 0x00000001, 32);   /* start code */
+  bs_write_nal_header (&bs, GST_H264_NAL_REF_IDC_HIGH, GST_H264_NAL_SPS);
 
-  if (seq_param->frame_cropping_flag) {
-    /* frame_crop_left_offset */
-    gst_bit_writer_put_ue (bitwriter, seq_param->frame_crop_left_offset);
-    /* frame_crop_right_offset */
-    gst_bit_writer_put_ue (bitwriter, seq_param->frame_crop_right_offset);
-    /* frame_crop_top_offset */
-    gst_bit_writer_put_ue (bitwriter, seq_param->frame_crop_top_offset);
-    /* frame_crop_bottom_offset */
-    gst_bit_writer_put_ue (bitwriter, seq_param->frame_crop_bottom_offset);
-  }
+  /* Set High profile for encoding the MVC base view. Otherwise, some
+     traditional decoder cannot recognize MVC profile streams with
+     only the base view in there */
+  if (profile == GST_VAAPI_PROFILE_H264_MULTIVIEW_HIGH ||
+      profile == GST_VAAPI_PROFILE_H264_STEREO_HIGH)
+    profile = GST_VAAPI_PROFILE_H264_HIGH;
 
-  /* vui_parameters_present_flag */
-  gst_bit_writer_put_bits_uint32 (bitwriter,
-      seq_param->vui_parameters_present_flag, 1);
-  if (seq_param->vui_parameters_present_flag) {
-    /* aspect_ratio_info_present_flag */
-    gst_bit_writer_put_bits_uint32 (bitwriter,
-        seq_param->vui_fields.bits.aspect_ratio_info_present_flag, 1);
-    if (seq_param->vui_fields.bits.aspect_ratio_info_present_flag) {
-      gst_bit_writer_put_bits_uint32 (bitwriter, seq_param->aspect_ratio_idc,
-          8);
-      if (seq_param->aspect_ratio_idc == 0xFF) {
-        gst_bit_writer_put_bits_uint32 (bitwriter, seq_param->sar_width, 16);
-        gst_bit_writer_put_bits_uint32 (bitwriter, seq_param->sar_height, 16);
-      }
-    }
+  bs_write_sps (&bs, seq_param, profile, &hrd_params);
 
-    /* overscan_info_present_flag */
-    gst_bit_writer_put_bits_uint32 (bitwriter, 0, 1);
-    /* video_signal_type_present_flag */
-    gst_bit_writer_put_bits_uint32 (bitwriter, 0, 1);
-    /* chroma_loc_info_present_flag */
-    gst_bit_writer_put_bits_uint32 (bitwriter, 0, 1);
+  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);
 
-    /* timing_info_present_flag */
-    gst_bit_writer_put_bits_uint32 (bitwriter,
-        seq_param->vui_fields.bits.timing_info_present_flag, 1);
-    if (seq_param->vui_fields.bits.timing_info_present_flag) {
-      gst_bit_writer_put_bits_uint32 (bitwriter, seq_param->num_units_in_tick,
-          32);
-      gst_bit_writer_put_bits_uint32 (bitwriter, seq_param->time_scale, 32);
-      gst_bit_writer_put_bits_uint32 (bitwriter, 1, 1); /* fixed_frame_rate_flag */
-    }
+  packed_seq_param.type = VAEncPackedHeaderSequence;
+  packed_seq_param.bit_length = data_bit_size;
+  packed_seq_param.has_emulation_bytes = 0;
 
-    nal_hrd_parameters_present_flag =
-        (seq_param->bits_per_second > 0 ? TRUE : FALSE);
-    /* nal_hrd_parameters_present_flag */
-    gst_bit_writer_put_bits_uint32 (bitwriter, nal_hrd_parameters_present_flag,
-        1);
-    if (nal_hrd_parameters_present_flag) {
-      /* hrd_parameters */
-      /* cpb_cnt_minus1 */
-      gst_bit_writer_put_ue (bitwriter, 0);
-      gst_bit_writer_put_bits_uint32 (bitwriter, 4, 4); /* bit_rate_scale */
-      gst_bit_writer_put_bits_uint32 (bitwriter, 6, 4); /* cpb_size_scale */
+  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);
 
-      for (i = 0; i < 1; ++i) {
-        /* bit_rate_value_minus1[0] */
-        gst_bit_writer_put_ue (bitwriter,
-            seq_param->bits_per_second / 1000 - 1);
-        /* cpb_size_value_minus1[0] */
-        gst_bit_writer_put_ue (bitwriter,
-            seq_param->bits_per_second / 1000 * 8 - 1);
-        /* cbr_flag[0] */
-        gst_bit_writer_put_bits_uint32 (bitwriter, 1, 1);
-      }
-      /* initial_cpb_removal_delay_length_minus1 */
-      gst_bit_writer_put_bits_uint32 (bitwriter, 23, 5);
-      /* cpb_removal_delay_length_minus1 */
-      gst_bit_writer_put_bits_uint32 (bitwriter, 23, 5);
-      /* dpb_output_delay_length_minus1 */
-      gst_bit_writer_put_bits_uint32 (bitwriter, 23, 5);
-      /* time_offset_length  */
-      gst_bit_writer_put_bits_uint32 (bitwriter, 23, 5);
-    }
-    /* vcl_hrd_parameters_present_flag */
-    gst_bit_writer_put_bits_uint32 (bitwriter, 0, 1);
-    if (nal_hrd_parameters_present_flag
-        || 0 /*vcl_hrd_parameters_present_flag */ ) {
-      /* low_delay_hrd_flag */
-      gst_bit_writer_put_bits_uint32 (bitwriter, 0, 1);
-    }
-    /* pic_struct_present_flag */
-    gst_bit_writer_put_bits_uint32 (bitwriter, 0, 1);
-    /* bitwriter_restriction_flag */
-    gst_bit_writer_put_bits_uint32 (bitwriter, 0, 1);
-  }
+  gst_vaapi_enc_picture_add_packed_header (picture, packed_seq);
+  gst_vaapi_codec_object_replace (&packed_seq, NULL);
 
-  /* rbsp_trailing_bits */
-  gst_bit_writer_write_trailing_bits (bitwriter);
+  /* store sps data */
+  _check_sps_pps_status (encoder, data + 4, data_bit_size / 8 - 4);
+  gst_bit_writer_clear (&bs, TRUE);
   return TRUE;
-}
-
-static gboolean
-gst_bit_writer_write_pps (GstBitWriter * bitwriter,
-    const VAEncPictureParameterBufferH264 * pic_param)
-{
-  guint32 num_slice_groups_minus1 = 0;
-  guint32 pic_init_qs_minus26 = 0;
-  guint32 redundant_pic_cnt_present_flag = 0;
-
-  /* pic_parameter_set_id */
-  gst_bit_writer_put_ue (bitwriter, pic_param->pic_parameter_set_id);
-  /* seq_parameter_set_id */
-  gst_bit_writer_put_ue (bitwriter, pic_param->seq_parameter_set_id);
-  /* entropy_coding_mode_flag */
-  gst_bit_writer_put_bits_uint32 (bitwriter,
-      pic_param->pic_fields.bits.entropy_coding_mode_flag, 1);
-  /* pic_order_present_flag */
-  gst_bit_writer_put_bits_uint32 (bitwriter,
-      pic_param->pic_fields.bits.pic_order_present_flag, 1);
-  /* slice_groups-1 */
-  gst_bit_writer_put_ue (bitwriter, num_slice_groups_minus1);
-
-  if (num_slice_groups_minus1 > 0) {
-     /*FIXME*/ g_assert (0);
-  }
-  gst_bit_writer_put_ue (bitwriter, pic_param->num_ref_idx_l0_active_minus1);
-  gst_bit_writer_put_ue (bitwriter, pic_param->num_ref_idx_l1_active_minus1);
-  gst_bit_writer_put_bits_uint32 (bitwriter,
-      pic_param->pic_fields.bits.weighted_pred_flag, 1);
-  gst_bit_writer_put_bits_uint32 (bitwriter,
-      pic_param->pic_fields.bits.weighted_bipred_idc, 2);
-  /* pic_init_qp_minus26 */
-  gst_bit_writer_put_se (bitwriter, pic_param->pic_init_qp - 26);
-  /* pic_init_qs_minus26 */
-  gst_bit_writer_put_se (bitwriter, pic_init_qs_minus26);
-  /* chroma_qp_index_offset */
-  gst_bit_writer_put_se (bitwriter, pic_param->chroma_qp_index_offset);
-
-  gst_bit_writer_put_bits_uint32 (bitwriter,
-      pic_param->pic_fields.bits.deblocking_filter_control_present_flag, 1);
-  gst_bit_writer_put_bits_uint32 (bitwriter,
-      pic_param->pic_fields.bits.constrained_intra_pred_flag, 1);
-  gst_bit_writer_put_bits_uint32 (bitwriter, redundant_pic_cnt_present_flag, 1);
-
-  /* more_rbsp_data */
-  gst_bit_writer_put_bits_uint32 (bitwriter,
-      pic_param->pic_fields.bits.transform_8x8_mode_flag, 1);
-  gst_bit_writer_put_bits_uint32 (bitwriter,
-      pic_param->pic_fields.bits.pic_scaling_matrix_present_flag, 1);
-  if (pic_param->pic_fields.bits.pic_scaling_matrix_present_flag) {
-    g_assert (0);
-    /* FIXME */
-    /*
-       for (i = 0; i <
-       (6+(-( (chroma_format_idc ! = 3) ? 2 : 6) * -pic_param->pic_fields.bits.transform_8x8_mode_flag));
-       i++) {
-       gst_bit_writer_put_bits_uint8(bitwriter, pic_param->pic_fields.bits.pic_scaling_list_present_flag, 1);
-       }
-     */
-  }
-
-  gst_bit_writer_put_se (bitwriter, pic_param->second_chroma_qp_index_offset);
-  gst_bit_writer_write_trailing_bits (bitwriter);
 
-  return TRUE;
+  /* ERRORS */
+bs_error:
+  {
+    GST_WARNING ("failed to write SPS NAL unit");
+    gst_bit_writer_clear (&bs, TRUE);
+    return FALSE;
+  }
 }
 
 static gboolean
-add_sequence_packed_header (GstVaapiEncoderH264 * encoder,
+add_packed_sequence_header_mvc (GstVaapiEncoderH264 * encoder,
     GstVaapiEncPicture * picture, GstVaapiEncSequence * sequence)
 {
   GstVaapiEncPackedHeader *packed_seq;
-  GstBitWriter writer;
+  GstBitWriter bs;
   VAEncPackedHeaderParameterBuffer packed_header_param_buffer = { 0 };
   const VAEncSequenceParameterBufferH264 *const seq_param = sequence->param;
+  VAEncMiscParameterHRD hrd_params;
   guint32 data_bit_size;
   guint8 *data;
 
-  gst_bit_writer_init (&writer, 128 * 8);
-  gst_bit_writer_put_bits_uint32 (&writer, 0x00000001, 32);     /* start code */
-  gst_bit_writer_write_nal_header (&writer,
-      GST_VAAPI_ENCODER_H264_NAL_REF_IDC_HIGH, GST_VAAPI_ENCODER_H264_NAL_SPS);
-  gst_bit_writer_write_sps (&writer, seq_param, encoder->profile);
-  g_assert (GST_BIT_WRITER_BIT_SIZE (&writer) % 8 == 0);
-  data_bit_size = GST_BIT_WRITER_BIT_SIZE (&writer);
-  data = GST_BIT_WRITER_DATA (&writer);
+  fill_hrd_params (encoder, &hrd_params);
+
+  /* non-base layer, pack one subset sps */
+  gst_bit_writer_init (&bs, 128 * 8);
+  WRITE_UINT32 (&bs, 0x00000001, 32);   /* start code */
+  bs_write_nal_header (&bs, GST_H264_NAL_REF_IDC_HIGH, GST_H264_NAL_SUBSET_SPS);
+
+  bs_write_subset_sps (&bs, seq_param, encoder->profile, encoder->num_views,
+      &hrd_params);
+
+  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_header_param_buffer.type = VAEncPackedHeaderSequence;
   packed_header_param_buffer.bit_length = data_bit_size;
@@ -795,41 +1318,49 @@ add_sequence_packed_header (GstVaapiEncoderH264 * encoder,
   g_assert (packed_seq);
 
   gst_vaapi_enc_picture_add_packed_header (picture, packed_seq);
-  gst_vaapi_codec_object_replace (&packed_seq, NULL);
+  gst_vaapi_mini_object_replace ((GstVaapiMiniObject **) & packed_seq, NULL);
 
-  /* store sps data */
+  /* store subset sps data */
   _check_sps_pps_status (encoder, data + 4, data_bit_size / 8 - 4);
-  gst_bit_writer_clear (&writer, TRUE);
-
+  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_picture_packed_header (GstVaapiEncoderH264 * encoder,
+add_packed_picture_header (GstVaapiEncoderH264 * encoder,
     GstVaapiEncPicture * picture)
 {
   GstVaapiEncPackedHeader *packed_pic;
-  GstBitWriter writer;
-  VAEncPackedHeaderParameterBuffer packed_header_param_buffer = { 0 };
+  GstBitWriter bs;
+  VAEncPackedHeaderParameterBuffer packed_pic_param = { 0 };
   const VAEncPictureParameterBufferH264 *const pic_param = picture->param;
   guint32 data_bit_size;
   guint8 *data;
 
-  gst_bit_writer_init (&writer, 128 * 8);
-  gst_bit_writer_put_bits_uint32 (&writer, 0x00000001, 32);     /* start code */
-  gst_bit_writer_write_nal_header (&writer,
-      GST_VAAPI_ENCODER_H264_NAL_REF_IDC_HIGH, GST_VAAPI_ENCODER_H264_NAL_PPS);
-  gst_bit_writer_write_pps (&writer, pic_param);
-  g_assert (GST_BIT_WRITER_BIT_SIZE (&writer) % 8 == 0);
-  data_bit_size = GST_BIT_WRITER_BIT_SIZE (&writer);
-  data = GST_BIT_WRITER_DATA (&writer);
+  gst_bit_writer_init (&bs, 128 * 8);
+  WRITE_UINT32 (&bs, 0x00000001, 32);   /* start code */
+  bs_write_nal_header (&bs, GST_H264_NAL_REF_IDC_HIGH, GST_H264_NAL_PPS);
+  bs_write_pps (&bs, pic_param, encoder->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_header_param_buffer.type = VAEncPackedHeaderPicture;
-  packed_header_param_buffer.bit_length = data_bit_size;
-  packed_header_param_buffer.has_emulation_bytes = 0;
+  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_header_param_buffer, sizeof (packed_header_param_buffer),
+      &packed_pic_param, sizeof (packed_pic_param),
       data, (data_bit_size + 7) / 8);
   g_assert (packed_pic);
 
@@ -838,12 +1369,146 @@ add_picture_packed_header (GstVaapiEncoderH264 * encoder,
 
   /* store pps data */
   _check_sps_pps_status (encoder, data + 4, data_bit_size / 8 - 4);
-  gst_bit_writer_clear (&writer, TRUE);
+  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_hdr_attributes (GstVaapiEncPicture * picture,
+    guint8 * nal_ref_idc, guint8 * nal_unit_type)
+{
+  switch (picture->type) {
+    case GST_VAAPI_PICTURE_TYPE_I:
+      *nal_ref_idc = GST_H264_NAL_REF_IDC_HIGH;
+      if (GST_VAAPI_ENC_PICTURE_IS_IDR (picture))
+        *nal_unit_type = GST_H264_NAL_SLICE_IDR;
+      else
+        *nal_unit_type = GST_H264_NAL_SLICE;
+      break;
+    case GST_VAAPI_PICTURE_TYPE_P:
+      *nal_ref_idc = GST_H264_NAL_REF_IDC_MEDIUM;
+      *nal_unit_type = GST_H264_NAL_SLICE;
+      break;
+    case GST_VAAPI_PICTURE_TYPE_B:
+      *nal_ref_idc = GST_H264_NAL_REF_IDC_NONE;
+      *nal_unit_type = GST_H264_NAL_SLICE;
+      break;
+    default:
+      return FALSE;
+  }
+  return TRUE;
+}
+
+/* Adds the supplied prefix nal header to the list of packed
+   headers to pass down as-is to the encoder */
+static gboolean
+add_packed_prefix_nal_header (GstVaapiEncoderH264 * encoder,
+    GstVaapiEncPicture * picture, GstVaapiEncSlice * slice)
+{
+  GstVaapiEncPackedHeader *packed_prefix_nal;
+  GstBitWriter bs;
+  VAEncPackedHeaderParameterBuffer packed_prefix_nal_param = { 0 };
+  guint32 data_bit_size;
+  guint8 *data;
+  guint8 nal_ref_idc, nal_unit_type;
+
+  gst_bit_writer_init (&bs, 128 * 8);
+  WRITE_UINT32 (&bs, 0x00000001, 32);   /* start code */
+
+  if (!get_nal_hdr_attributes (picture, &nal_ref_idc, &nal_unit_type))
+    goto bs_error;
+  nal_unit_type = GST_H264_NAL_PREFIX_UNIT;
+
+  bs_write_nal_header (&bs, nal_ref_idc, nal_unit_type);
+  bs_write_nal_header_mvc_extension (&bs, picture, encoder->view_idx);
+  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_prefix_nal_param.type = VAEncPackedHeaderRawData;
+  packed_prefix_nal_param.bit_length = data_bit_size;
+  packed_prefix_nal_param.has_emulation_bytes = 0;
+
+  packed_prefix_nal =
+      gst_vaapi_enc_packed_header_new (GST_VAAPI_ENCODER (encoder),
+      &packed_prefix_nal_param, sizeof (packed_prefix_nal_param), data,
+      (data_bit_size + 7) / 8);
+  g_assert (packed_prefix_nal);
+
+  gst_vaapi_enc_slice_add_packed_header (slice, packed_prefix_nal);
+  gst_vaapi_codec_object_replace (&packed_prefix_nal, NULL);
+
+  gst_bit_writer_clear (&bs, TRUE);
+
+  return TRUE;
+
+  /* ERRORS */
+bs_error:
+  {
+    GST_WARNING ("failed to write Prefix NAL unit header");
+    gst_bit_writer_clear (&bs, TRUE);
+    return FALSE;
+  }
+}
+
+/* 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 (GstVaapiEncoderH264 * encoder,
+    GstVaapiEncPicture * picture, GstVaapiEncSlice * slice)
+{
+  GstVaapiEncPackedHeader *packed_slice;
+  GstBitWriter bs;
+  VAEncPackedHeaderParameterBuffer packed_slice_param = { 0 };
+  const VAEncSliceParameterBufferH264 *const slice_param = slice->param;
+  guint32 data_bit_size;
+  guint8 *data;
+  guint8 nal_ref_idc, nal_unit_type;
+
+  gst_bit_writer_init (&bs, 128 * 8);
+  WRITE_UINT32 (&bs, 0x00000001, 32);   /* start code */
 
+  if (!get_nal_hdr_attributes (picture, &nal_ref_idc, &nal_unit_type))
+    goto bs_error;
+  bs_write_nal_header (&bs, nal_ref_idc, nal_unit_type);
+
+  bs_write_slice (&bs, slice_param, encoder, picture);
+  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 */
+/* Reference picture management */
 static void
 reference_pic_free (GstVaapiEncoderH264 * encoder, GstVaapiEncoderH264Ref * ref)
 {
@@ -871,20 +1536,24 @@ reference_list_update (GstVaapiEncoderH264 * encoder,
     GstVaapiEncPicture * picture, GstVaapiSurfaceProxy * surface)
 {
   GstVaapiEncoderH264Ref *ref;
+  GstVaapiH264ViewRefPool *const ref_pool =
+      &encoder->ref_pools[encoder->view_idx];
 
   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 (&encoder->ref_list))
-      reference_pic_free (encoder, g_queue_pop_head (&encoder->ref_list));
-  } else if (g_queue_get_length (&encoder->ref_list) >= encoder->max_ref_frames) {
-    reference_pic_free (encoder, g_queue_pop_head (&encoder->ref_list));
+    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 (&encoder->ref_list, ref);
-  g_assert (g_queue_get_length (&encoder->ref_list) <= encoder->max_ref_frames);
+  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;
 }
 
@@ -896,8 +1565,9 @@ reference_list_init (GstVaapiEncoderH264 * encoder,
     GstVaapiEncoderH264Ref ** reflist_1, guint * reflist_1_count)
 {
   GstVaapiEncoderH264Ref *tmp;
+  GstVaapiH264ViewRefPool *const ref_pool =
+      &encoder->ref_pools[encoder->view_idx];
   GList *iter, *list_0_start = NULL, *list_1_start = NULL;
-  guint max_pic_order_cnt = (1 << encoder->log2_max_pic_order_cnt);
   guint count;
 
   *reflist_0_count = 0;
@@ -905,11 +1575,11 @@ reference_list_init (GstVaapiEncoderH264 * encoder,
   if (picture->type == GST_VAAPI_PICTURE_TYPE_I)
     return TRUE;
 
-  iter = g_queue_peek_tail_link (&encoder->ref_list);
+  iter = g_queue_peek_tail_link (&ref_pool->ref_list);
   for (; iter; iter = g_list_previous (iter)) {
     tmp = (GstVaapiEncoderH264Ref *) iter->data;
     g_assert (tmp && tmp->poc != picture->poc);
-    if (_poc_greater_than (picture->poc, tmp->poc, max_pic_order_cnt)) {
+    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;
@@ -940,25 +1610,22 @@ reference_list_init (GstVaapiEncoderH264 * encoder,
   return TRUE;
 }
 
-/* fill the  H264 VA encoding parameters */
+/* Fills in VA sequence parameter buffer */
 static gboolean
-fill_va_sequence_param (GstVaapiEncoderH264 * encoder,
-    GstVaapiEncSequence * sequence)
+fill_sequence (GstVaapiEncoderH264 * encoder, GstVaapiEncSequence * sequence)
 {
-  GstVaapiEncoder *const base_encoder = GST_VAAPI_ENCODER_CAST (encoder);
   VAEncSequenceParameterBufferH264 *const seq_param = sequence->param;
+  GstVaapiH264ViewRefPool *const ref_pool =
+      &encoder->ref_pools[encoder->view_idx];
 
   memset (seq_param, 0, sizeof (VAEncSequenceParameterBufferH264));
-  seq_param->seq_parameter_set_id = 0;
+  seq_param->seq_parameter_set_id = encoder->view_idx;
   seq_param->level_idc = encoder->level_idc;
   seq_param->intra_period = GST_VAAPI_ENCODER_KEYFRAME_PERIOD (encoder);
-  seq_param->ip_period = 0;     // ?
-  if (base_encoder->bitrate > 0)
-    seq_param->bits_per_second = base_encoder->bitrate * 1000;
-  else
-    seq_param->bits_per_second = 0;
+  seq_param->ip_period = 1 + encoder->num_bframes;
+  seq_param->bits_per_second = encoder->bitrate_bits;
 
-  seq_param->max_num_ref_frames = encoder->max_ref_frames;
+  seq_param->max_num_ref_frames = ref_pool->max_ref_frames;
   seq_param->picture_width_in_mbs = encoder->mb_width;
   seq_param->picture_height_in_mbs = encoder->mb_height;
 
@@ -974,7 +1641,8 @@ fill_va_sequence_param (GstVaapiEncoderH264 * encoder,
   seq_param->seq_fields.bits.log2_max_frame_num_minus4 =
       encoder->log2_max_frame_num - 4;
   /* picture order count */
-  seq_param->seq_fields.bits.pic_order_cnt_type = 0;
+  encoder->pic_order_cnt_type = seq_param->seq_fields.bits.pic_order_cnt_type =
+      0;
   g_assert (encoder->log2_max_pic_order_cnt >= 4);
   seq_param->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 =
       encoder->log2_max_pic_order_cnt - 4;
@@ -984,7 +1652,8 @@ fill_va_sequence_param (GstVaapiEncoderH264 * encoder,
 
   /* not used if pic_order_cnt_type == 0 */
   if (seq_param->seq_fields.bits.pic_order_cnt_type == 1) {
-    seq_param->seq_fields.bits.delta_pic_order_always_zero_flag = TRUE;
+    encoder->delta_pic_order_always_zero_flag =
+        seq_param->seq_fields.bits.delta_pic_order_always_zero_flag = TRUE;
     seq_param->num_ref_frames_in_pic_order_cnt_cycle = 0;
     seq_param->offset_for_non_ref_pic = 0;
     seq_param->offset_for_top_to_bottom_field = 0;
@@ -1025,16 +1694,17 @@ fill_va_sequence_param (GstVaapiEncoderH264 * encoder,
       seq_param->time_scale = GST_VAAPI_ENCODER_FPS_N (encoder) * 2;
     }
   }
-
   return TRUE;
 }
 
+/* Fills in VA picture parameter buffer */
 static gboolean
-fill_va_picture_param (GstVaapiEncoderH264 * encoder,
-    GstVaapiEncPicture * picture,
+fill_picture (GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture,
     GstVaapiCodedBuffer * codedbuf, GstVaapiSurfaceProxy * surface)
 {
   VAEncPictureParameterBufferH264 *const pic_param = picture->param;
+  GstVaapiH264ViewRefPool *const ref_pool =
+      &encoder->ref_pools[encoder->view_idx];
   GstVaapiEncoderH264Ref *ref_pic;
   GList *reflist;
   guint i;
@@ -1046,7 +1716,7 @@ fill_va_picture_param (GstVaapiEncoderH264 * encoder,
   pic_param->CurrPic.TopFieldOrderCnt = picture->poc;
   i = 0;
   if (picture->type != GST_VAAPI_PICTURE_TYPE_I) {
-    for (reflist = g_queue_peek_head_link (&encoder->ref_list);
+    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 &&
@@ -1056,22 +1726,22 @@ fill_va_picture_param (GstVaapiEncoderH264 * encoder,
           GST_VAAPI_SURFACE_PROXY_SURFACE_ID (ref_pic->pic);
       ++i;
     }
-    g_assert (i <= 16 && i <= encoder->max_ref_frames);
+    g_assert (i <= 16 && i <= ref_pool->max_ref_frames);
   }
   for (; i < 16; ++i) {
     pic_param->ReferenceFrames[i].picture_id = VA_INVALID_ID;
   }
   pic_param->coded_buf = GST_VAAPI_OBJECT_ID (codedbuf);
 
-  pic_param->pic_parameter_set_id = 0;
-  pic_param->seq_parameter_set_id = 0;
+  pic_param->pic_parameter_set_id = encoder->view_idx;
+  pic_param->seq_parameter_set_id = encoder->view_idx;
   pic_param->last_picture = 0;  /* means last encoding picture */
   pic_param->frame_num = picture->frame_num;
   pic_param->pic_init_qp = encoder->init_qp;
   pic_param->num_ref_idx_l0_active_minus1 =
-      (encoder->max_reflist0_count ? (encoder->max_reflist0_count - 1) : 0);
+      (ref_pool->max_reflist0_count ? (ref_pool->max_reflist0_count - 1) : 0);
   pic_param->num_ref_idx_l1_active_minus1 =
-      (encoder->max_reflist1_count ? (encoder->max_reflist1_count - 1) : 0);
+      (ref_pool->max_reflist1_count ? (ref_pool->max_reflist1_count - 1) : 0);
   pic_param->chroma_qp_index_offset = 0;
   pic_param->second_chroma_qp_index_offset = 0;
 
@@ -1096,11 +1766,10 @@ fill_va_picture_param (GstVaapiEncoderH264 * encoder,
   return TRUE;
 }
 
+/* Adds slice headers to picture */
 static gboolean
-fill_va_slices_param (GstVaapiEncoderH264 * encoder,
-    GstVaapiEncPicture * picture,
-    GstVaapiEncoderH264Ref ** reflist_0,
-    guint reflist_0_count,
+add_slice_headers (GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture,
+    GstVaapiEncoderH264Ref ** reflist_0, guint reflist_0_count,
     GstVaapiEncoderH264Ref ** reflist_1, guint reflist_1_count)
 {
   VAEncSliceParameterBufferH264 *slice_param;
@@ -1134,7 +1803,7 @@ fill_va_slices_param (GstVaapiEncoderH264 * encoder,
     slice_param->macroblock_info = VA_INVALID_ID;
     slice_param->slice_type = h264_get_slice_type (picture->type);
     g_assert (slice_param->slice_type != -1);
-    slice_param->pic_parameter_set_id = 0;
+    slice_param->pic_parameter_set_id = encoder->view_idx;
     slice_param->idr_pic_id = encoder->idr_num;
     slice_param->pic_order_cnt_lsb = picture->poc;
 
@@ -1217,63 +1886,149 @@ fill_va_slices_param (GstVaapiEncoderH264 * encoder,
     /* set calculation for next slice */
     last_mb_index += cur_slice_mbs;
 
+    if (encoder->is_mvc &&
+        (GST_VAAPI_ENCODER_PACKED_HEADERS (encoder) & VA_ENC_PACKED_HEADER_RAW_DATA)
+        && !add_packed_prefix_nal_header (encoder, picture, slice))
+      goto error_create_packed_prefix_nal_hdr;
+    if ((GST_VAAPI_ENCODER_PACKED_HEADERS (encoder) &
+            VA_ENC_PACKED_HEADER_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_mb_index == mb_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;
+  }
+error_create_packed_prefix_nal_hdr:
+  {
+    GST_ERROR ("failed to create packed prefix nal header buffer");
+    gst_vaapi_codec_object_replace (&slice, NULL);
+    return FALSE;
+  }
 }
 
+/* Generates and submits SPS header accordingly into the bitstream */
 static gboolean
 ensure_sequence (GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture)
 {
-  GstVaapiEncSequence *sequence;
+  GstVaapiEncSequence *sequence = NULL;
 
-  g_assert (picture);
-  sequence = GST_VAAPI_ENC_SEQUENCE_NEW (H264, encoder);
-  g_assert (sequence);
-  if (!sequence)
-    goto error;
+  // Submit an SPS header before every new I-frame
+  if (picture->type != GST_VAAPI_PICTURE_TYPE_I)
+    return TRUE;
 
-  if (!fill_va_sequence_param (encoder, sequence))
-    goto error;
+  sequence = GST_VAAPI_ENC_SEQUENCE_NEW (H264, encoder);
+  if (!sequence || !fill_sequence (encoder, sequence))
+    goto error_create_seq_param;
+
+  /* add subset sps for non-base view and sps for base view */
+  if (encoder->is_mvc && encoder->view_idx) {
+    if ((GST_VAAPI_ENCODER_PACKED_HEADERS (encoder) & VAEncPackedHeaderH264_SPS)
+        && !add_packed_sequence_header_mvc (encoder, picture, sequence))
+      goto error_create_packed_seq_hdr;
+  } else {
+    if ((GST_VAAPI_ENCODER_PACKED_HEADERS (encoder) & VAEncPackedHeaderH264_SPS)
+        && !add_packed_sequence_header (encoder, picture, sequence))
+      goto error_create_packed_seq_hdr;
+  }
 
-  if (picture->type == GST_VAAPI_PICTURE_TYPE_I &&
-      !add_sequence_packed_header (encoder, picture, sequence))
-    goto error;
-  gst_vaapi_enc_picture_set_sequence (picture, sequence);
-  gst_vaapi_codec_object_replace (&sequence, NULL);
+  if (sequence) {
+    gst_vaapi_enc_picture_set_sequence (picture, sequence);
+    gst_vaapi_codec_object_replace (&sequence, NULL);
+  }
   return TRUE;
 
-error:
-  gst_vaapi_codec_object_replace (&sequence, NULL);
-  return FALSE;
+  /* 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 additional control parameters */
+static gboolean
+ensure_misc_params (GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture)
+{
+  GstVaapiEncMiscParam *misc = NULL;
+  VAEncMiscParameterRateControl *rate_control;
+
+  /* HRD params */
+  misc = GST_VAAPI_ENC_MISC_PARAM_NEW (HRD, encoder);
+  g_assert (misc);
+  if (!misc)
+    return FALSE;
+  fill_hrd_params (encoder, misc->data);
+  gst_vaapi_enc_picture_add_misc_param (picture, misc);
+  gst_vaapi_codec_object_replace (&misc, NULL);
+
+  /* RateControl params */
+  if (GST_VAAPI_ENCODER_RATE_CONTROL (encoder) == GST_VAAPI_RATECONTROL_CBR ||
+      GST_VAAPI_ENCODER_RATE_CONTROL (encoder) == GST_VAAPI_RATECONTROL_VBR) {
+    misc = GST_VAAPI_ENC_MISC_PARAM_NEW (RateControl, encoder);
+    g_assert (misc);
+    if (!misc)
+      return FALSE;
+    rate_control = misc->data;
+    memset (rate_control, 0, sizeof (VAEncMiscParameterRateControl));
+    rate_control->bits_per_second = encoder->bitrate_bits;
+    rate_control->target_percentage = 70;
+    rate_control->window_size = encoder->cpb_length;
+    rate_control->initial_qp = encoder->init_qp;
+    rate_control->min_qp = encoder->min_qp;
+    rate_control->basic_unit_size = 0;
+    gst_vaapi_enc_picture_add_misc_param (picture, misc);
+    gst_vaapi_codec_object_replace (&misc, NULL);
+  }
+  return TRUE;
 }
 
+/* Generates and submits PPS header accordingly into the bitstream */
 static gboolean
 ensure_picture (GstVaapiEncoderH264 * 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 (!fill_va_picture_param (encoder, picture, codedbuf, surface))
+  if (!res)
     return FALSE;
 
   if (picture->type == GST_VAAPI_PICTURE_TYPE_I &&
-      !add_picture_packed_header (encoder, picture)) {
+      (GST_VAAPI_ENCODER_PACKED_HEADERS (encoder) & VAEncPackedHeaderH264_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 (GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture)
 {
   GstVaapiEncoderH264Ref *reflist_0[16];
   GstVaapiEncoderH264Ref *reflist_1[16];
+  GstVaapiH264ViewRefPool *const ref_pool =
+      &encoder->ref_pools[encoder->view_idx];
   guint reflist_0_count = 0, reflist_1_count = 0;
 
   g_assert (picture);
@@ -1285,88 +2040,47 @@ ensure_slices (GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture)
     return FALSE;
   }
 
-  g_assert (reflist_0_count + reflist_1_count <= encoder->max_ref_frames);
-  if (reflist_0_count > encoder->max_reflist0_count)
-    reflist_0_count = encoder->max_reflist0_count;
-  if (reflist_1_count > encoder->max_reflist1_count)
-    reflist_1_count = encoder->max_reflist1_count;
+  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 (!fill_va_slices_param (encoder, picture,
+  if (!add_slice_headers (encoder, picture,
           reflist_0, reflist_0_count, reflist_1, reflist_1_count))
     return FALSE;
 
   return TRUE;
 }
 
-static gboolean
-ensure_misc (GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture)
+/* Normalizes bitrate (and CPB size) for HRD conformance */
+static void
+ensure_bitrate_hrd (GstVaapiEncoderH264 * encoder)
 {
   GstVaapiEncoder *const base_encoder = GST_VAAPI_ENCODER_CAST (encoder);
-  GstVaapiEncMiscParam *misc = NULL;
-  VAEncMiscParameterHRD *hrd;
-  VAEncMiscParameterRateControl *rate_control;
-
-  /* add hrd */
-  misc = GST_VAAPI_ENC_MISC_PARAM_NEW (HRD, encoder);
-  g_assert (misc);
-  if (!misc)
-    return FALSE;
-  gst_vaapi_enc_picture_add_misc_param (picture, misc);
-  hrd = misc->data;
-  if (base_encoder->bitrate > 0) {
-    hrd->initial_buffer_fullness = base_encoder->bitrate * 1000 * 4;
-    hrd->buffer_size = base_encoder->bitrate * 1000 * 8;
-  } else {
-    hrd->initial_buffer_fullness = 0;
-    hrd->buffer_size = 0;
-  }
-  gst_vaapi_codec_object_replace (&misc, NULL);
+  guint bitrate, cpb_size;
 
-  /* add ratecontrol */
-  if (GST_VAAPI_ENCODER_RATE_CONTROL (encoder) == GST_VAAPI_RATECONTROL_CBR ||
-      GST_VAAPI_ENCODER_RATE_CONTROL (encoder) == GST_VAAPI_RATECONTROL_VBR) {
-    misc = GST_VAAPI_ENC_MISC_PARAM_NEW (RateControl, encoder);
-    g_assert (misc);
-    if (!misc)
-      return FALSE;
-    gst_vaapi_enc_picture_add_misc_param (picture, misc);
-    rate_control = misc->data;
-    memset (rate_control, 0, sizeof (VAEncMiscParameterRateControl));
-    if (base_encoder->bitrate)
-      rate_control->bits_per_second = base_encoder->bitrate * 1000;
-    else
-      rate_control->bits_per_second = 0;
-    rate_control->target_percentage = 70;
-    rate_control->window_size = 500;
-    rate_control->initial_qp = encoder->init_qp;
-    rate_control->min_qp = encoder->min_qp;
-    rate_control->basic_unit_size = 0;
-    gst_vaapi_codec_object_replace (&misc, NULL);
+  if (!base_encoder->bitrate) {
+    encoder->bitrate_bits = 0;
+    return;
   }
 
-  return TRUE;
-}
-
-static GstVaapiEncoderStatus
-ensure_profile_and_level (GstVaapiEncoderH264 * encoder)
-{
-  ensure_tuning (encoder);
-
-  if (!ensure_profile (encoder) || !ensure_profile_limits (encoder))
-    return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
-
-  if (!ensure_level (encoder))
-    return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
-
-  /* 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;
-  return GST_VAAPI_ENCODER_STATUS_SUCCESS;
+  /* Round down bitrate. This is a hard limit mandated by the user */
+  g_assert (SX_BITRATE >= 6);
+  bitrate = (base_encoder->bitrate * 1000) & ~((1U << SX_BITRATE) - 1);
+  GST_DEBUG ("HRD bitrate: %u bits/sec", bitrate);
+  encoder->bitrate_bits = bitrate;
+
+  /* Round up CPB size. This is an HRD compliance detail */
+  g_assert (SX_CPB_SIZE >= 4);
+  cpb_size = gst_util_uint64_scale (bitrate, encoder->cpb_length, 1000) &
+      ~((1U << SX_CPB_SIZE) - 1);
+  GST_DEBUG ("HRD CPB size: %u bits", cpb_size);
+  encoder->cpb_length_bits = cpb_size;
 }
 
-static gboolean
+/* Estimates a good enough bitrate if none was supplied */
+static void
 ensure_bitrate (GstVaapiEncoderH264 * encoder)
 {
   GstVaapiEncoder *const base_encoder = GST_VAAPI_ENCODER_CAST (encoder);
@@ -1398,19 +2112,41 @@ ensure_bitrate (GstVaapiEncoderH264 * encoder)
       base_encoder->bitrate = 0;
       break;
   }
-  return TRUE;
+  ensure_bitrate_hrd (encoder);
+}
+
+/* Constructs profile and level information based on user-defined limits */
+static GstVaapiEncoderStatus
+ensure_profile_and_level (GstVaapiEncoderH264 * encoder)
+{
+  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 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;
+  return GST_VAAPI_ENCODER_STATUS_SUCCESS;
 }
 
 static void
 reset_properties (GstVaapiEncoderH264 * encoder)
 {
   GstVaapiEncoder *const base_encoder = GST_VAAPI_ENCODER_CAST (encoder);
-  guint mb_size;
+  guint mb_size, i;
 
   if (encoder->idr_period < base_encoder->keyframe_period)
     encoder->idr_period = base_encoder->keyframe_period;
-  if (encoder->idr_period > GST_VAAPI_ENCODER_H264_MAX_IDR_PERIOD)
-    encoder->idr_period = GST_VAAPI_ENCODER_H264_MAX_IDR_PERIOD;
+  if (encoder->idr_period > MAX_IDR_PERIOD)
+    encoder->idr_period = MAX_IDR_PERIOD;
 
   if (encoder->min_qp > encoder->init_qp ||
       (GST_VAAPI_ENCODER_RATE_CONTROL (encoder) == GST_VAAPI_RATECONTROL_CQP &&
@@ -1425,9 +2161,6 @@ reset_properties (GstVaapiEncoderH264 * encoder)
   if (encoder->num_bframes > (base_encoder->keyframe_period + 1) / 2)
     encoder->num_bframes = (base_encoder->keyframe_period + 1) / 2;
 
-  if (encoder->num_bframes > 50)
-    encoder->num_bframes = 50;
-
   if (encoder->num_bframes)
     encoder->cts_offset = GST_SECOND * GST_VAAPI_ENCODER_FPS_D (encoder) /
         GST_VAAPI_ENCODER_FPS_N (encoder);
@@ -1441,13 +2174,20 @@ reset_properties (GstVaapiEncoderH264 * encoder)
   encoder->max_frame_num = (1 << encoder->log2_max_frame_num);
   encoder->log2_max_pic_order_cnt = encoder->log2_max_frame_num + 1;
   encoder->max_pic_order_cnt = (1 << encoder->log2_max_pic_order_cnt);
-
-  encoder->frame_index = 0;
   encoder->idr_num = 0;
-  encoder->max_reflist0_count = 1;
-  encoder->max_reflist1_count = encoder->num_bframes > 0;
-  encoder->max_ref_frames =
-      encoder->max_reflist0_count + encoder->max_reflist1_count;
+
+  encoder->is_mvc = encoder->num_views > 1;
+  for (i = 0; i < encoder->num_views; i++) {
+    GstVaapiH264ViewRefPool *const ref_pool = &encoder->ref_pools[i];
+    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;
+
+    GstVaapiH264ViewReorderPool *const reorder_pool =
+        &encoder->reorder_pools[i];
+    reorder_pool->frame_index = 0;
+  }
 }
 
 static GstVaapiEncoderStatus
@@ -1465,9 +2205,9 @@ gst_vaapi_encoder_h264_encode (GstVaapiEncoder * base_encoder,
 
   if (!ensure_sequence (encoder, picture))
     goto error;
-  if (!ensure_picture (encoder, picture, codedbuf, reconstruct))
+  if (!ensure_misc_params (encoder, picture))
     goto error;
-  if (!ensure_misc (encoder, picture))
+  if (!ensure_picture (encoder, picture, codedbuf, reconstruct))
     goto error;
   if (!ensure_slices (encoder, picture))
     goto error;
@@ -1490,16 +2230,23 @@ gst_vaapi_encoder_h264_flush (GstVaapiEncoder * base_encoder)
 {
   GstVaapiEncoderH264 *const encoder =
       GST_VAAPI_ENCODER_H264_CAST (base_encoder);
+  GstVaapiH264ViewReorderPool *reorder_pool;
   GstVaapiEncPicture *pic;
+  guint i;
 
-  encoder->frame_index = 0;
-  encoder->cur_frame_num = 0;
-  encoder->cur_present_index = 0;
-  while (!g_queue_is_empty (&encoder->reorder_frame_list)) {
-    pic = g_queue_pop_head (&encoder->reorder_frame_list);
-    gst_vaapi_enc_picture_unref (pic);
+  for (i = 0; i < encoder->num_views; i++) {
+    reorder_pool = &encoder->reorder_pools[i];
+    reorder_pool->frame_index = 0;
+    reorder_pool->cur_frame_num = 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);
   }
-  g_queue_clear (&encoder->reorder_frame_list);
 
   return GST_VAAPI_ENCODER_STATUS_SUCCESS;
 }
@@ -1515,7 +2262,7 @@ gst_vaapi_encoder_h264_get_codec_data (GstVaapiEncoder * base_encoder,
   const guint32 nal_length_size = 4;
   guint8 profile_idc, profile_comp, level_idc;
   GstMapInfo sps_info, pps_info;
-  GstBitWriter writer;
+  GstBitWriter bs;
   GstBuffer *buffer;
 
   if (!encoder->sps_data || !encoder->pps_data)
@@ -1535,39 +2282,47 @@ gst_vaapi_encoder_h264_get_codec_data (GstVaapiEncoder * base_encoder,
   level_idc = sps_info.data[3];
 
   /* Header */
-  gst_bit_writer_init (&writer, (sps_info.size + pps_info.size + 64) * 8);
-  gst_bit_writer_put_bits_uint32 (&writer, configuration_version, 8);
-  gst_bit_writer_put_bits_uint32 (&writer, profile_idc, 8);
-  gst_bit_writer_put_bits_uint32 (&writer, profile_comp, 8);
-  gst_bit_writer_put_bits_uint32 (&writer, level_idc, 8);
-  gst_bit_writer_put_bits_uint32 (&writer, 0x3f, 6);    /* 111111 */
-  gst_bit_writer_put_bits_uint32 (&writer, nal_length_size - 1, 2);
-  gst_bit_writer_put_bits_uint32 (&writer, 0x07, 3);    /* 111 */
+  gst_bit_writer_init (&bs, (sps_info.size + pps_info.size + 64) * 8);
+  WRITE_UINT32 (&bs, configuration_version, 8);
+  WRITE_UINT32 (&bs, profile_idc, 8);
+  WRITE_UINT32 (&bs, profile_comp, 8);
+  WRITE_UINT32 (&bs, level_idc, 8);
+  WRITE_UINT32 (&bs, 0x3f, 6);  /* 111111 */
+  WRITE_UINT32 (&bs, nal_length_size - 1, 2);
+  WRITE_UINT32 (&bs, 0x07, 3);  /* 111 */
 
   /* Write SPS */
-  gst_bit_writer_put_bits_uint32 (&writer, 1, 5);       /* SPS count = 1 */
-  g_assert (GST_BIT_WRITER_BIT_SIZE (&writer) % 8 == 0);
-  gst_bit_writer_put_bits_uint32 (&writer, sps_info.size, 16);
-  gst_bit_writer_put_bytes (&writer, sps_info.data, sps_info.size);
+  WRITE_UINT32 (&bs, 1, 5);     /* SPS count = 1 */
+  g_assert (GST_BIT_WRITER_BIT_SIZE (&bs) % 8 == 0);
+  WRITE_UINT32 (&bs, sps_info.size, 16);
+  gst_bit_writer_put_bytes (&bs, sps_info.data, sps_info.size);
 
   /* Write PPS */
-  gst_bit_writer_put_bits_uint32 (&writer, 1, 8);       /* PPS count = 1 */
-  gst_bit_writer_put_bits_uint32 (&writer, pps_info.size, 16);
-  gst_bit_writer_put_bytes (&writer, pps_info.data, pps_info.size);
+  WRITE_UINT32 (&bs, 1, 8);     /* PPS count = 1 */
+  WRITE_UINT32 (&bs, pps_info.size, 16);
+  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);
 
-  buffer = gst_buffer_new_wrapped (GST_BIT_WRITER_DATA (&writer),
-      GST_BIT_WRITER_BIT_SIZE (&writer) / 8);
+  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 (&writer, FALSE);
+  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->sps_data, &sps_info);
+    gst_buffer_unmap (encoder->pps_data, &pps_info);
+    gst_bit_writer_clear (&bs, TRUE);
+    return FALSE;
+  }
 error_map_sps_buffer:
   {
     GST_ERROR ("failed to map SPS packed header");
@@ -1582,7 +2337,7 @@ error_map_pps_buffer:
 error_alloc_buffer:
   {
     GST_ERROR ("failed to allocate codec-data buffer");
-    gst_bit_writer_clear (&writer, TRUE);
+    gst_bit_writer_clear (&bs, TRUE);
     return GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
   }
 }
@@ -1593,24 +2348,34 @@ gst_vaapi_encoder_h264_reordering (GstVaapiEncoder * base_encoder,
 {
   GstVaapiEncoderH264 *const encoder =
       GST_VAAPI_ENCODER_H264_CAST (base_encoder);
+  GstVaapiH264ViewReorderPool *reorder_pool = NULL;
   GstVaapiEncPicture *picture;
   gboolean is_idr = FALSE;
 
   *output = NULL;
 
+  /* encoding views alternatively for MVC */
+  if (encoder->is_mvc) {
+    if (frame)
+      encoder->view_idx = frame->system_frame_number % MAX_NUM_VIEWS;
+    else
+      encoder->view_idx = (encoder->view_idx + 1) % MAX_NUM_VIEWS;
+  }
+  reorder_pool = &encoder->reorder_pools[encoder->view_idx];
+
   if (!frame) {
-    if (encoder->reorder_state != GST_VAAPI_ENC_H264_REORD_DUMP_FRAMES)
+    if (reorder_pool->reorder_state != GST_VAAPI_ENC_H264_REORD_DUMP_FRAMES)
       return GST_VAAPI_ENCODER_STATUS_NO_SURFACE;
 
     /* reorder_state = GST_VAAPI_ENC_H264_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 (&encoder->reorder_frame_list),
+    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 (&encoder->reorder_frame_list);
+    picture = g_queue_pop_head (&reorder_pool->reorder_frame_list);
     g_assert (picture);
-    if (g_queue_is_empty (&encoder->reorder_frame_list)) {
-      encoder->reorder_state = GST_VAAPI_ENC_H264_REORD_WAIT_FRAMES;
+    if (g_queue_is_empty (&reorder_pool->reorder_frame_list)) {
+      reorder_pool->reorder_state = GST_VAAPI_ENC_H264_REORD_WAIT_FRAMES;
     }
     goto end;
   }
@@ -1622,60 +2387,60 @@ gst_vaapi_encoder_h264_reordering (GstVaapiEncoder * base_encoder,
         GST_TIME_FORMAT, GST_TIME_ARGS (frame->pts));
     return GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
   }
-  ++encoder->cur_present_index;
-  picture->poc = ((encoder->cur_present_index * 2) %
+  ++reorder_pool->cur_present_index;
+  picture->poc = ((reorder_pool->cur_present_index * 2) %
       encoder->max_pic_order_cnt);
 
-  is_idr = (encoder->frame_index == 0 ||
-      encoder->frame_index >= encoder->idr_period);
+  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) ||
-      (encoder->frame_index % GST_VAAPI_ENCODER_KEYFRAME_PERIOD (encoder)) ==
-      0) {
-    ++encoder->cur_frame_num;
-    ++encoder->frame_index;
+      (reorder_pool->frame_index %
+          GST_VAAPI_ENCODER_KEYFRAME_PERIOD (encoder)) == 0) {
+    ++reorder_pool->cur_frame_num;
+    ++reorder_pool->frame_index;
 
     /* b frame enabled,  check queue of reorder_frame_list */
     if (encoder->num_bframes
-        && !g_queue_is_empty (&encoder->reorder_frame_list)) {
+        && !g_queue_is_empty (&reorder_pool->reorder_frame_list)) {
       GstVaapiEncPicture *p_pic;
 
-      p_pic = g_queue_pop_tail (&encoder->reorder_frame_list);
-      _set_p_frame (p_pic, encoder);
-      g_queue_foreach (&encoder->reorder_frame_list,
-          (GFunc) _set_b_frame, encoder);
-      ++encoder->cur_frame_num;
-      _set_key_frame (picture, encoder, is_idr);
-      g_queue_push_tail (&encoder->reorder_frame_list, picture);
+      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);
+      ++reorder_pool->cur_frame_num;
+      set_key_frame (picture, encoder, is_idr);
+      g_queue_push_tail (&reorder_pool->reorder_frame_list, picture);
       picture = p_pic;
-      encoder->reorder_state = GST_VAAPI_ENC_H264_REORD_DUMP_FRAMES;
+      reorder_pool->reorder_state = GST_VAAPI_ENC_H264_REORD_DUMP_FRAMES;
     } else {                    /* no b frames in queue */
-      _set_key_frame (picture, encoder, is_idr);
-      g_assert (g_queue_is_empty (&encoder->reorder_frame_list));
+      set_key_frame (picture, encoder, is_idr);
+      g_assert (g_queue_is_empty (&reorder_pool->reorder_frame_list));
       if (encoder->num_bframes)
-        encoder->reorder_state = GST_VAAPI_ENC_H264_REORD_WAIT_FRAMES;
+        reorder_pool->reorder_state = GST_VAAPI_ENC_H264_REORD_WAIT_FRAMES;
     }
     goto end;
   }
 
   /* new p/b frames coming */
-  ++encoder->frame_index;
-  if (encoder->reorder_state == GST_VAAPI_ENC_H264_REORD_WAIT_FRAMES &&
-      g_queue_get_length (&encoder->reorder_frame_list) <
+  ++reorder_pool->frame_index;
+  if (reorder_pool->reorder_state == GST_VAAPI_ENC_H264_REORD_WAIT_FRAMES &&
+      g_queue_get_length (&reorder_pool->reorder_frame_list) <
       encoder->num_bframes) {
-    g_queue_push_tail (&encoder->reorder_frame_list, picture);
+    g_queue_push_tail (&reorder_pool->reorder_frame_list, picture);
     return GST_VAAPI_ENCODER_STATUS_NO_SURFACE;
   }
 
-  ++encoder->cur_frame_num;
-  _set_p_frame (picture, encoder);
+  ++reorder_pool->cur_frame_num;
+  set_p_frame (picture, encoder);
 
-  if (encoder->reorder_state == GST_VAAPI_ENC_H264_REORD_WAIT_FRAMES) {
-    g_queue_foreach (&encoder->reorder_frame_list, (GFunc) _set_b_frame,
+  if (reorder_pool->reorder_state == GST_VAAPI_ENC_H264_REORD_WAIT_FRAMES) {
+    g_queue_foreach (&reorder_pool->reorder_frame_list, (GFunc) set_b_frame,
         encoder);
-    encoder->reorder_state = GST_VAAPI_ENC_H264_REORD_DUMP_FRAMES;
-    g_assert (!g_queue_is_empty (&encoder->reorder_frame_list));
+    reorder_pool->reorder_state = GST_VAAPI_ENC_H264_REORD_DUMP_FRAMES;
+    g_assert (!g_queue_is_empty (&reorder_pool->reorder_frame_list));
   }
 
 end:
@@ -1710,7 +2475,8 @@ set_context_info (GstVaapiEncoder * base_encoder)
     return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
 
   base_encoder->num_ref_frames =
-      (encoder->num_bframes ? 2 : 1) + DEFAULT_SURFACES_COUNT;
+      ((encoder->num_bframes ? 2 : 1) + DEFAULT_SURFACES_COUNT)
+      * encoder->num_views;
 
   /* Only YUV 4:2:0 formats are supported for now. This means that we
      have a limit of 3200 bits per macroblock. */
@@ -1748,14 +2514,8 @@ gst_vaapi_encoder_h264_reconfigure (GstVaapiEncoder * base_encoder)
   if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
     return status;
 
-  if (!ensure_bitrate (encoder))
-    goto error;
-
   reset_properties (encoder);
   return set_context_info (base_encoder);
-
-error:
-  return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
 }
 
 static gboolean
@@ -1763,16 +2523,32 @@ gst_vaapi_encoder_h264_init (GstVaapiEncoder * base_encoder)
 {
   GstVaapiEncoderH264 *const encoder =
       GST_VAAPI_ENCODER_H264_CAST (base_encoder);
+  guint32 i;
+
+  /* Multi-view coding information */
+  encoder->is_mvc = FALSE;
+  encoder->num_views = 1;
+  encoder->view_idx = 0;
+
+  /* re-ordering  list initialize */
+  for (i = 0; i < MAX_NUM_VIEWS; i++) {
+    GstVaapiH264ViewReorderPool *const reorder_pool =
+        &encoder->reorder_pools[i];
+    g_queue_init (&reorder_pool->reorder_frame_list);
+    reorder_pool->reorder_state = GST_VAAPI_ENC_H264_REORD_NONE;
+    reorder_pool->frame_index = 0;
+    reorder_pool->cur_frame_num = 0;
+    reorder_pool->cur_present_index = 0;
+  }
 
-  /* re-ordering */
-  g_queue_init (&encoder->reorder_frame_list);
-  encoder->reorder_state = GST_VAAPI_ENC_H264_REORD_NONE;
-
-  /* reference frames */
-  g_queue_init (&encoder->ref_list);
-  encoder->max_ref_frames = 0;
-  encoder->max_reflist0_count = 1;
-  encoder->max_reflist1_count = 1;
+  /* reference list info initialize */
+  for (i = 0; i < MAX_NUM_VIEWS; i++) {
+    GstVaapiH264ViewRefPool *const ref_pool = &encoder->ref_pools[i];
+    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;
 }
@@ -1785,22 +2561,33 @@ gst_vaapi_encoder_h264_finalize (GstVaapiEncoder * base_encoder)
       GST_VAAPI_ENCODER_H264_CAST (base_encoder);
   GstVaapiEncPicture *pic;
   GstVaapiEncoderH264Ref *ref;
+  guint32 i;
 
   gst_buffer_replace (&encoder->sps_data, NULL);
+  gst_buffer_replace (&encoder->subset_sps_data, NULL);
   gst_buffer_replace (&encoder->pps_data, NULL);
 
-  while (!g_queue_is_empty (&encoder->ref_list)) {
-    ref = g_queue_pop_head (&encoder->ref_list);
-    reference_pic_free (encoder, ref);
+  /* reference list info de-init */
+  for (i = 0; i < MAX_NUM_VIEWS; i++) {
+    GstVaapiH264ViewRefPool *const ref_pool = &encoder->ref_pools[i];
+    while (!g_queue_is_empty (&ref_pool->ref_list)) {
+      ref = (GstVaapiEncoderH264Ref *) g_queue_pop_head (&ref_pool->ref_list);
+      reference_pic_free (encoder, ref);
+    }
+    g_queue_clear (&ref_pool->ref_list);
   }
-  g_queue_clear (&encoder->ref_list);
 
-  while (!g_queue_is_empty (&encoder->reorder_frame_list)) {
-    pic = g_queue_pop_head (&encoder->reorder_frame_list);
-    gst_vaapi_enc_picture_unref (pic);
+  /* re-ordering  list initialize */
+  for (i = 0; i < MAX_NUM_VIEWS; i++) {
+    GstVaapiH264ViewReorderPool *const reorder_pool =
+        &encoder->reorder_pools[i];
+    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);
   }
-  g_queue_clear (&encoder->reorder_frame_list);
-
 }
 
 static GstVaapiEncoderStatus
@@ -1829,6 +2616,12 @@ gst_vaapi_encoder_h264_set_property (GstVaapiEncoder * base_encoder,
     case GST_VAAPI_ENCODER_H264_PROP_DCT8X8:
       encoder->use_dct8x8 = g_value_get_boolean (value);
       break;
+    case GST_VAAPI_ENCODER_H264_PROP_CPB_LENGTH:
+      encoder->cpb_length = g_value_get_uint (value);
+      break;
+    case GST_VAAPI_ENCODER_H264_PROP_NUM_VIEWS:
+      encoder->num_views = g_value_get_uint (value);
+      break;
     default:
       return GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_PARAMETER;
   }
@@ -1957,6 +2750,30 @@ gst_vaapi_encoder_h264_get_default_properties (void)
           "Enable adaptive use of 8x8 transforms in I-frames",
           FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  /**
+   * GstVaapiEncoderH264:cpb-length:
+   *
+   * The size of the CPB buffer in milliseconds.
+   */
+  GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
+      GST_VAAPI_ENCODER_H264_PROP_CPB_LENGTH,
+      g_param_spec_uint ("cpb-length",
+          "CPB Length", "Length of the CPB buffer in milliseconds",
+          1, 10000, DEFAULT_CPB_LENGTH,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GstVaapiEncoderH264:num-views:
+   *
+   * The number of views for MVC encoding .
+   */
+  GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
+      GST_VAAPI_ENCODER_H264_PROP_NUM_VIEWS,
+      g_param_spec_uint ("num-views",
+          "Number of Views",
+          "Number of Views for MVC encoding",
+          1, MAX_NUM_VIEWS, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   return props;
 }