/*
* 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))
/* Supported set of tuning options, within this implementation */
-#define SUPPORTED_TUNE_OPTIONS \
- (GST_VAAPI_ENCODER_TUNE_MASK (NONE))
-
-#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
-
-typedef enum
-{
- GST_VAAPI_ENCODER_H264_ENTROPY_MODE_CAVLC = 0,
- GST_VAAPI_ENCODER_H264_ENTROPY_MODE_CABAC = 1
-} GstVaapiEncoderH264EntropyMode;
-
-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 SUPPORTED_TUNE_OPTIONS \
+ (GST_VAAPI_ENCODER_TUNE_MASK (NONE) | \
+ GST_VAAPI_ENCODER_TUNE_MASK (HIGH_COMPRESSION))
+
+/* 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)
+
+#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
{
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)
{
{
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:
- return -1;
+ break;
}
return -1;
}
-static inline gboolean
-_read_sps_attributes (const guint8 * sps_data,
- guint32 sps_size,
- guint32 * profile_idc, guint32 * profile_comp, guint32 * level_idc)
-{
- g_assert (profile_idc && profile_comp && level_idc);
- g_assert (sps_size >= 4);
- if (sps_size < 4) {
- return FALSE;
- }
- /* skip sps_data[0], nal_type */
- *profile_idc = sps_data[1];
- *profile_comp = sps_data[2];
- *level_idc = sps_data[3];
- return TRUE;
-}
-
/* Get log2_max_frame_num value for H.264 specification */
static guint
h264_get_log2_max_frame_num (guint num)
ret = 4;
else if (ret > 10)
ret = 10;
- /* must greater than 4 */
+ /* must be greater than 4 */
return ret;
}
-static inline guint
-_profile_to_value (GstVaapiProfile profile)
+/* 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_BASELINE:
- return 66;
- case GST_VAAPI_PROFILE_H264_MAIN:
- return 77;
case GST_VAAPI_PROFILE_H264_HIGH:
- return 100;
- default:
+ f = 1500;
break;
- }
- return 0;
-}
-
-static inline void
-_check_sps_pps_status (GstVaapiEncoderH264 * encoder,
- const guint8 * nal, guint32 size)
-{
- guint8 nal_type;
- gsize ret;
-
- g_assert (size);
-
- if (encoder->sps_data && encoder->pps_data)
- return;
-
- nal_type = nal[0] & 0x1F;
- switch (nal_type) {
- case GST_VAAPI_ENCODER_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);
+ case GST_VAAPI_PROFILE_H264_HIGH10:
+ f = 3600;
break;
- case GST_VAAPI_ENCODER_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);
+ case GST_VAAPI_PROFILE_H264_HIGH_422:
+ case GST_VAAPI_PROFILE_H264_HIGH_444:
+ f = 4800;
break;
default:
+ f = 1200;
break;
}
+ return f;
}
-static void
-_set_level (GstVaapiEncoderH264 * encoder)
-{
- guint pic_mb_size;
- guint MaxDpbMbs, MaxMBPS;
- guint dbp_level, mbps_level, profile_level;
-
- if (encoder->level_idc) {
- if (encoder->level_idc < GST_VAAPI_ENCODER_H264_LEVEL_10)
- encoder->level_idc = GST_VAAPI_ENCODER_H264_LEVEL_10;
- else if (encoder->level_idc > GST_VAAPI_ENCODER_H264_LEVEL_51)
- encoder->level_idc = GST_VAAPI_ENCODER_H264_LEVEL_51;
- return;
- }
-
- /* calculate level */
- pic_mb_size = ((GST_VAAPI_ENCODER_WIDTH (encoder) + 15) / 16) *
- ((GST_VAAPI_ENCODER_HEIGHT (encoder) + 15) / 16);
- MaxDpbMbs = pic_mb_size * ((encoder->num_bframes) ? 2 : 1);
- MaxMBPS = pic_mb_size * GST_VAAPI_ENCODER_FPS_N (encoder) /
- GST_VAAPI_ENCODER_FPS_D (encoder);
-
- /* calculate from MaxDbpMbs */
- if (MaxDpbMbs > 110400)
- dbp_level = GST_VAAPI_ENCODER_H264_LEVEL_51;
- else if (MaxDpbMbs > 34816)
- dbp_level = GST_VAAPI_ENCODER_H264_LEVEL_50;
- else if (MaxDpbMbs > 32768)
- dbp_level = GST_VAAPI_ENCODER_H264_LEVEL_42;
- else if (MaxDpbMbs > 20480) /* 41 or 40 */
- dbp_level = GST_VAAPI_ENCODER_H264_LEVEL_41;
- else if (MaxDpbMbs > 18000)
- dbp_level = GST_VAAPI_ENCODER_H264_LEVEL_32;
- else if (MaxDpbMbs > 8100)
- dbp_level = GST_VAAPI_ENCODER_H264_LEVEL_31;
- else if (MaxDpbMbs > 4752) /* 30 or 22 */
- dbp_level = GST_VAAPI_ENCODER_H264_LEVEL_30;
- else if (MaxDpbMbs > 2376)
- dbp_level = GST_VAAPI_ENCODER_H264_LEVEL_21;
- else if (MaxDpbMbs > 900) /* 20, 13, 12 */
- dbp_level = GST_VAAPI_ENCODER_H264_LEVEL_20;
- else if (MaxDpbMbs > 396)
- dbp_level = GST_VAAPI_ENCODER_H264_LEVEL_11;
- else
- dbp_level = GST_VAAPI_ENCODER_H264_LEVEL_10;
-
- /* calculate from Max Mb processing rate */
- if (MaxMBPS > 589824)
- mbps_level = GST_VAAPI_ENCODER_H264_LEVEL_51;
- else if (MaxMBPS > 522240)
- mbps_level = GST_VAAPI_ENCODER_H264_LEVEL_50;
- else if (MaxMBPS > 245760)
- mbps_level = GST_VAAPI_ENCODER_H264_LEVEL_42;
- else if (MaxMBPS > 216000) /* 40 or 41 */
- mbps_level = GST_VAAPI_ENCODER_H264_LEVEL_41;
- else if (MaxMBPS > 108000)
- mbps_level = GST_VAAPI_ENCODER_H264_LEVEL_32;
- else if (MaxMBPS > 40500)
- mbps_level = GST_VAAPI_ENCODER_H264_LEVEL_31;
- else if (MaxMBPS > 20250)
- mbps_level = GST_VAAPI_ENCODER_H264_LEVEL_30;
- else if (MaxMBPS > 19800)
- mbps_level = GST_VAAPI_ENCODER_H264_LEVEL_22;
- else if (MaxMBPS > 11800)
- mbps_level = GST_VAAPI_ENCODER_H264_LEVEL_21;
- else if (MaxMBPS > 6000) /*13 or 20 */
- mbps_level = GST_VAAPI_ENCODER_H264_LEVEL_20;
- else if (MaxMBPS > 3000)
- mbps_level = GST_VAAPI_ENCODER_H264_LEVEL_12;
- else if (MaxMBPS > 1485)
- mbps_level = GST_VAAPI_ENCODER_H264_LEVEL_11;
- else
- mbps_level = GST_VAAPI_ENCODER_H264_LEVEL_10;
-
- if (encoder->profile == GST_VAAPI_PROFILE_H264_HIGH)
- profile_level = GST_VAAPI_ENCODER_H264_LEVEL_41;
- else if (encoder->profile == GST_VAAPI_PROFILE_H264_MAIN)
- profile_level = GST_VAAPI_ENCODER_H264_LEVEL_30;
- else
- profile_level = GST_VAAPI_ENCODER_H264_LEVEL_20;
-
- encoder->level_idc = (dbp_level > mbps_level ? dbp_level : mbps_level);
- if (encoder->level_idc < profile_level)
- encoder->level_idc = profile_level;
-}
-
-static inline void
-_reset_gop_start (GstVaapiEncoderH264 * encoder)
-{
- ++encoder->idr_num;
- encoder->frame_index = 1;
- encoder->cur_frame_num = 0;
- encoder->cur_present_index = 0;
-}
-
-static void
-_set_b_frame (GstVaapiEncPicture * pic, GstVaapiEncoderH264 * encoder)
-{
- 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);
-}
-
-static inline void
-_set_p_frame (GstVaapiEncPicture * pic, GstVaapiEncoderH264 * encoder)
-{
- 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);
-}
-
-static inline void
-_set_i_frame (GstVaapiEncPicture * pic, GstVaapiEncoderH264 * encoder)
-{
- 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);
- g_assert (GST_VAAPI_ENC_PICTURE_GET_FRAME (pic));
- GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (GST_VAAPI_ENC_PICTURE_GET_FRAME (pic));
-}
-
-static inline 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;
- pic->frame_num = 0;
- pic->poc = 0;
- GST_VAAPI_ENC_PICTURE_FLAG_SET (pic, GST_VAAPI_ENC_PICTURE_FLAG_IDR);
-
- g_assert (GST_VAAPI_ENC_PICTURE_GET_FRAME (pic));
- GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (GST_VAAPI_ENC_PICTURE_GET_FRAME (pic));
-}
-
-static inline void
-_set_key_frame (GstVaapiEncPicture * picture,
- GstVaapiEncoderH264 * encoder, gboolean is_idr)
-{
- if (is_idr) {
- _reset_gop_start (encoder);
- _set_idr_frame (picture, encoder);
- } else
- _set_i_frame (picture, encoder);
-}
-
-gboolean
-gst_bit_writer_put_ue (GstBitWriter * bitwriter, guint32 value)
+/* ------------------------------------------------------------------------- */
+/* --- 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;
tmp_value >>= 1;
}
if (size_in_bits > 1
- && !gst_bit_writer_put_bits_uint32 (bitwriter, 0, 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 (bitwriter, value, size_in_bits))
+ if (!gst_bit_writer_put_bits_uint32 (bs, value, size_in_bits))
return FALSE;
return TRUE;
}
-gboolean
-gst_bit_writer_put_se (GstBitWriter * bitwriter, gint32 value)
+/* 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;
else
new_val = (value << 1) - 1;
- if (!gst_bit_writer_put_ue (bitwriter, new_val))
+ 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
-gst_bit_writer_write_nal_header (GstBitWriter * bitwriter,
- guint32 nal_ref_idc, guint32 nal_unit_type)
+bs_write_nal_header_mvc_extension (GstBitWriter * bs,
+ GstVaapiEncPicture * picture, guint32 view_id)
{
- 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);
+ 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
-gst_bit_writer_write_trailing_bits (GstBitWriter * bitwriter)
+bs_write_trailing_bits (GstBitWriter * bs)
{
- gst_bit_writer_put_bits_uint32 (bitwriter, 1, 1);
- gst_bit_writer_align_bytes_unchecked (bitwriter, 0);
+ 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
-gst_bit_writer_write_sps (GstBitWriter * bitwriter,
- const VAEncSequenceParameterBufferH264 * seq_param, GstVaapiProfile profile)
+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; // ??
!seq_param->seq_fields.bits.frame_mbs_only_flag;
guint32 i = 0;
- constraint_set0_flag = profile == GST_VAAPI_PROFILE_H264_BASELINE;
- constraint_set1_flag = profile <= GST_VAAPI_PROFILE_H264_MAIN;
+ 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_to_value (profile), 8);
+ WRITE_UINT32 (bs, profile_idc, 8);
/* constraint_set0_flag */
- gst_bit_writer_put_bits_uint32 (bitwriter, constraint_set0_flag, 1);
+ WRITE_UINT32 (bs, constraint_set0_flag, 1);
/* constraint_set1_flag */
- gst_bit_writer_put_bits_uint32 (bitwriter, constraint_set1_flag, 1);
+ WRITE_UINT32 (bs, constraint_set1_flag, 1);
/* constraint_set2_flag */
- gst_bit_writer_put_bits_uint32 (bitwriter, constraint_set2_flag, 1);
+ WRITE_UINT32 (bs, constraint_set2_flag, 1);
/* constraint_set3_flag */
- gst_bit_writer_put_bits_uint32 (bitwriter, constraint_set3_flag, 1);
+ WRITE_UINT32 (bs, constraint_set3_flag, 1);
/* reserved_zero_4bits */
- gst_bit_writer_put_bits_uint32 (bitwriter, 0, 4);
+ WRITE_UINT32 (bs, 0, 4);
/* level_idc */
- gst_bit_writer_put_bits_uint32 (bitwriter, seq_param->level_idc, 8);
+ WRITE_UINT32 (bs, seq_param->level_idc, 8);
/* seq_parameter_set_id */
- gst_bit_writer_put_ue (bitwriter, seq_param->seq_parameter_set_id);
+ WRITE_UE (bs, seq_param->seq_parameter_set_id);
- if (profile == GST_VAAPI_PROFILE_H264_HIGH) {
+ 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 */
- gst_bit_writer_put_ue (bitwriter,
- seq_param->seq_fields.bits.chroma_format_idc);
+ WRITE_UE (bs, 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);
+ WRITE_UINT32 (bs, residual_color_transform_flag, 1);
}
/* bit_depth_luma_minus8 */
- gst_bit_writer_put_ue (bitwriter, seq_param->bit_depth_luma_minus8);
+ WRITE_UE (bs, seq_param->bit_depth_luma_minus8);
/* bit_depth_chroma_minus8 */
- gst_bit_writer_put_ue (bitwriter, seq_param->bit_depth_chroma_minus8);
+ WRITE_UE (bs, 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);
+ WRITE_UINT32 (bs, b_qpprime_y_zero_transform_bypass, 1);
+
/* seq_scaling_matrix_present_flag */
- gst_bit_writer_put_bits_uint32 (bitwriter,
+ 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
for (i = 0;
i < (seq_param->seq_fields.bits.chroma_format_idc != 3 ? 8 : 12);
i++) {
- gst_bit_writer_put_bits_uint8 (bitwriter,
+ 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);
}
/* log2_max_frame_num_minus4 */
- gst_bit_writer_put_ue (bitwriter,
- seq_param->seq_fields.bits.log2_max_frame_num_minus4);
+ WRITE_UE (bs, 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);
+ 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 */
- gst_bit_writer_put_ue (bitwriter,
- seq_param->seq_fields.bits.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);
- gst_bit_writer_put_bits_uint32 (bitwriter,
+ g_assert (0 && "only POC type 0 is supported");
+ WRITE_UINT32 (bs,
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);
+ 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++) {
- gst_bit_writer_put_se (bitwriter, seq_param->offset_for_ref_frame[i]);
+ WRITE_SE (bs, seq_param->offset_for_ref_frame[i]);
}
}
/* num_ref_frames */
- gst_bit_writer_put_ue (bitwriter, seq_param->max_num_ref_frames);
+ WRITE_UE (bs, 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);
+ WRITE_UINT32 (bs, gaps_in_frame_num_value_allowed_flag, 1);
/* pic_width_in_mbs_minus1 */
- gst_bit_writer_put_ue (bitwriter, seq_param->picture_width_in_mbs - 1);
+ WRITE_UE (bs, 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);
+ WRITE_UE (bs, 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);
+ 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);
- gst_bit_writer_put_bits_uint32 (bitwriter, mb_adaptive_frame_field, 1);
+ g_assert (0 && "only progressive frames encoding is supported");
+ WRITE_UINT32 (bs, mb_adaptive_frame_field, 1);
}
/* direct_8x8_inference_flag */
- gst_bit_writer_put_bits_uint32 (bitwriter, 0, 1);
+ WRITE_UINT32 (bs, 0, 1);
/* frame_cropping_flag */
- gst_bit_writer_put_bits_uint32 (bitwriter, seq_param->frame_cropping_flag, 1);
+ WRITE_UINT32 (bs, seq_param->frame_cropping_flag, 1);
if (seq_param->frame_cropping_flag) {
/* frame_crop_left_offset */
- gst_bit_writer_put_ue (bitwriter, seq_param->frame_crop_left_offset);
+ WRITE_UE (bs, seq_param->frame_crop_left_offset);
/* frame_crop_right_offset */
- gst_bit_writer_put_ue (bitwriter, seq_param->frame_crop_right_offset);
+ WRITE_UE (bs, seq_param->frame_crop_right_offset);
/* frame_crop_top_offset */
- gst_bit_writer_put_ue (bitwriter, seq_param->frame_crop_top_offset);
+ WRITE_UE (bs, seq_param->frame_crop_top_offset);
/* frame_crop_bottom_offset */
- gst_bit_writer_put_ue (bitwriter, seq_param->frame_crop_bottom_offset);
+ WRITE_UE (bs, seq_param->frame_crop_bottom_offset);
}
/* vui_parameters_present_flag */
- gst_bit_writer_put_bits_uint32 (bitwriter,
- seq_param->vui_parameters_present_flag, 1);
+ WRITE_UINT32 (bs, 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,
+ 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) {
- gst_bit_writer_put_bits_uint32 (bitwriter, seq_param->aspect_ratio_idc,
- 8);
+ WRITE_UINT32 (bs, 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);
+ WRITE_UINT32 (bs, seq_param->sar_width, 16);
+ WRITE_UINT32 (bs, seq_param->sar_height, 16);
}
}
/* overscan_info_present_flag */
- gst_bit_writer_put_bits_uint32 (bitwriter, 0, 1);
+ WRITE_UINT32 (bs, 0, 1);
/* video_signal_type_present_flag */
- gst_bit_writer_put_bits_uint32 (bitwriter, 0, 1);
+ WRITE_UINT32 (bs, 0, 1);
/* chroma_loc_info_present_flag */
- gst_bit_writer_put_bits_uint32 (bitwriter, 0, 1);
+ WRITE_UINT32 (bs, 0, 1);
/* timing_info_present_flag */
- gst_bit_writer_put_bits_uint32 (bitwriter,
- seq_param->vui_fields.bits.timing_info_present_flag, 1);
+ WRITE_UINT32 (bs, 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 */
+ 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 =
- (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);
+ 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 */
- 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 */
+ 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] */
- gst_bit_writer_put_ue (bitwriter,
- seq_param->bits_per_second / 1000 - 1);
+ WRITE_UE (bs, (seq_param->bits_per_second >> SX_BITRATE) - 1);
/* cpb_size_value_minus1[0] */
- gst_bit_writer_put_ue (bitwriter,
- seq_param->bits_per_second / 1000 * 8 - 1);
+ WRITE_UE (bs, (hrd_params->buffer_size >> SX_CPB_SIZE) - 1);
/* cbr_flag[0] */
- gst_bit_writer_put_bits_uint32 (bitwriter, 1, 1);
+ WRITE_UINT32 (bs, 1, 1);
}
/* initial_cpb_removal_delay_length_minus1 */
- gst_bit_writer_put_bits_uint32 (bitwriter, 23, 5);
+ WRITE_UINT32 (bs, 23, 5);
/* cpb_removal_delay_length_minus1 */
- gst_bit_writer_put_bits_uint32 (bitwriter, 23, 5);
+ WRITE_UINT32 (bs, 23, 5);
/* dpb_output_delay_length_minus1 */
- gst_bit_writer_put_bits_uint32 (bitwriter, 23, 5);
+ WRITE_UINT32 (bs, 23, 5);
/* time_offset_length */
- gst_bit_writer_put_bits_uint32 (bitwriter, 23, 5);
+ WRITE_UINT32 (bs, 23, 5);
}
+
/* vcl_hrd_parameters_present_flag */
- gst_bit_writer_put_bits_uint32 (bitwriter, 0, 1);
+ WRITE_UINT32 (bs, 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);
+ WRITE_UINT32 (bs, 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);
+ 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);
+ WRITE_UE (bs, num_level_values_signalled_minus1);
+
+ 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 */
- gst_bit_writer_write_trailing_bits (bitwriter);
+ 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
-gst_bit_writer_write_pps (GstBitWriter * bitwriter,
- const VAEncPictureParameterBufferH264 * pic_param)
+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 */
- gst_bit_writer_put_ue (bitwriter, pic_param->pic_parameter_set_id);
+ WRITE_UE (bs, pic_param->pic_parameter_set_id);
/* seq_parameter_set_id */
- gst_bit_writer_put_ue (bitwriter, pic_param->seq_parameter_set_id);
+ WRITE_UE (bs, 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);
+ WRITE_UINT32 (bs, 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);
+ WRITE_UINT32 (bs, pic_param->pic_fields.bits.pic_order_present_flag, 1);
/* slice_groups-1 */
- gst_bit_writer_put_ue (bitwriter, num_slice_groups_minus1);
+ WRITE_UE (bs, 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);
+ /*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 */
- gst_bit_writer_put_se (bitwriter, pic_param->pic_init_qp - 26);
+ WRITE_SE (bs, pic_param->pic_init_qp - 26);
/* pic_init_qs_minus26 */
- gst_bit_writer_put_se (bitwriter, pic_init_qs_minus26);
+ WRITE_SE (bs, pic_init_qs_minus26);
/* chroma_qp_index_offset */
- gst_bit_writer_put_se (bitwriter, pic_param->chroma_qp_index_offset);
+ WRITE_SE (bs, pic_param->chroma_qp_index_offset);
- gst_bit_writer_put_bits_uint32 (bitwriter,
+ WRITE_UINT32 (bs,
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);
+ 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 */
- 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);
+ 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;
+ }
}
+/* ------------------------------------------------------------------------- */
+/* --- H.264 Encoder --- */
+/* ------------------------------------------------------------------------- */
+
+#define GST_VAAPI_ENCODER_H264_CAST(encoder) \
+ ((GstVaapiEncoderH264 *)(encoder))
+
+struct _GstVaapiEncoderH264
+{
+ GstVaapiEncoder parent_instance;
+
+ 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
-add_sequence_packed_header (GstVaapiEncoderH264 * encoder,
+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;
+ }
+}
+
+static inline void
+_check_sps_pps_status (GstVaapiEncoderH264 * encoder,
+ const guint8 * nal, guint32 size)
+{
+ guint8 nal_type;
+ gsize ret;
+ gboolean has_subset_sps;
+
+ g_assert (size);
+
+ 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_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_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);
+ break;
+ default:
+ break;
+ }
+}
+
+/* Determines the largest supported profile by the underlying hardware */
+static gboolean
+ensure_hw_profile_limits (GstVaapiEncoderH264 * encoder)
+{
+ GstVaapiDisplay *const display = GST_VAAPI_ENCODER_DISPLAY (encoder);
+ GArray *profiles;
+ guint i, profile_idc, max_profile_idc;
+
+ if (encoder->hw_max_profile_idc)
+ return TRUE;
+
+ profiles = gst_vaapi_display_get_encode_profiles (display);
+ if (!profiles)
+ return FALSE;
+
+ max_profile_idc = 0;
+ for (i = 0; i < profiles->len; i++) {
+ const GstVaapiProfile profile =
+ g_array_index (profiles, GstVaapiProfile, i);
+ profile_idc = gst_vaapi_utils_h264_get_profile_idc (profile);
+ if (!profile_idc)
+ continue;
+ if (max_profile_idc < profile_idc)
+ max_profile_idc = profile_idc;
+ }
+ g_array_unref (profiles);
+
+ encoder->hw_max_profile_idc = max_profile_idc;
+ return TRUE;
+}
+
+/* Derives the profile supported by the underlying hardware */
+static gboolean
+ensure_hw_profile (GstVaapiEncoderH264 * encoder)
+{
+ GstVaapiDisplay *const display = GST_VAAPI_ENCODER_DISPLAY (encoder);
+ GstVaapiEntrypoint entrypoint = GST_VAAPI_ENTRYPOINT_SLICE_ENCODE;
+ GstVaapiProfile profile, profiles[4];
+ guint i, num_profiles = 0;
+
+ profiles[num_profiles++] = encoder->profile;
+ switch (encoder->profile) {
+ case GST_VAAPI_PROFILE_H264_CONSTRAINED_BASELINE:
+ profiles[num_profiles++] = GST_VAAPI_PROFILE_H264_BASELINE;
+ profiles[num_profiles++] = GST_VAAPI_PROFILE_H264_MAIN;
+ // fall-through
+ case GST_VAAPI_PROFILE_H264_MAIN:
+ profiles[num_profiles++] = GST_VAAPI_PROFILE_H264_HIGH;
+ break;
+ default:
+ break;
+ }
+
+ profile = GST_VAAPI_PROFILE_UNKNOWN;
+ for (i = 0; i < num_profiles; i++) {
+ if (gst_vaapi_display_has_encoder (display, profiles[i], entrypoint)) {
+ profile = profiles[i];
+ break;
+ }
+ }
+ if (profile == GST_VAAPI_PROFILE_UNKNOWN)
+ goto error_unsupported_profile;
+
+ GST_VAAPI_ENCODER_CAST (encoder)->profile = profile;
+ return TRUE;
+
+ /* ERRORS */
+error_unsupported_profile:
+ {
+ GST_ERROR ("unsupported HW profile (0x%08x)", encoder->profile);
+ return FALSE;
+ }
+}
+
+/* Check target decoder constraints */
+static gboolean
+ensure_profile_limits (GstVaapiEncoderH264 * encoder)
+{
+ GstVaapiProfile profile;
+
+ if (!encoder->max_profile_idc
+ || encoder->profile_idc <= encoder->max_profile_idc)
+ return TRUE;
+
+ GST_WARNING ("lowering coding tools to meet target decoder constraints");
+
+ /* Try Main profile coding tools */
+ if (encoder->max_profile_idc < 100) {
+ encoder->use_dct8x8 = FALSE;
+ profile = GST_VAAPI_PROFILE_H264_MAIN;
+ }
+
+ /* Try Constrained Baseline profile coding tools */
+ if (encoder->max_profile_idc < 77) {
+ encoder->num_bframes = 0;
+ encoder->use_cabac = FALSE;
+ profile = GST_VAAPI_PROFILE_H264_CONSTRAINED_BASELINE;
+ }
+
+ encoder->profile = profile;
+ encoder->profile_idc = encoder->max_profile_idc;
+ return TRUE;
+}
+
+/* Derives the minimum profile from the active coding tools */
+static gboolean
+ensure_profile (GstVaapiEncoderH264 * encoder)
+{
+ GstVaapiProfile profile;
+
+ /* Always start from "constrained-baseline" profile for maximum
+ compatibility */
+ profile = GST_VAAPI_PROFILE_H264_CONSTRAINED_BASELINE;
+
+ /* Main profile coding tools */
+ if (encoder->num_bframes > 0 || encoder->use_cabac)
+ profile = GST_VAAPI_PROFILE_H264_MAIN;
+
+ /* High profile coding tools */
+ 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 cpb_factor = h264_get_cpb_nal_factor (encoder->profile);
+ const GstVaapiH264LevelLimits *limits_table;
+ guint i, num_limits, PicSizeMbs, MaxDpbMbs, MaxMBPS;
+
+ PicSizeMbs = encoder->mb_width * encoder->mb_height;
+ MaxDpbMbs = PicSizeMbs * ((encoder->num_bframes) ? 2 : 1);
+ MaxMBPS = gst_util_uint64_scale_int_ceil (PicSizeMbs,
+ GST_VAAPI_ENCODER_FPS_N (encoder), GST_VAAPI_ENCODER_FPS_D (encoder));
+
+ limits_table = gst_vaapi_utils_h264_get_level_limits_table (&num_limits);
+ for (i = 0; i < num_limits; i++) {
+ const GstVaapiH264LevelLimits *const limits = &limits_table[i];
+ if (PicSizeMbs <= limits->MaxFS &&
+ MaxDpbMbs <= limits->MaxDpbMbs &&
+ 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)
+ goto error_unsupported_level;
+
+ encoder->level = limits_table[i].level;
+ encoder->level_idc = limits_table[i].level_idc;
+ return TRUE;
+
+ /* ERRORS */
+error_unsupported_level:
+ {
+ GST_ERROR ("failed to find a suitable level matching codec config");
+ return FALSE;
+ }
+}
+
+/* Enable "high-compression" tuning options */
+static gboolean
+ensure_tuning_high_compression (GstVaapiEncoderH264 * encoder)
+{
+ guint8 profile_idc;
+
+ if (!ensure_hw_profile_limits (encoder))
+ return FALSE;
+
+ profile_idc = encoder->hw_max_profile_idc;
+ if (encoder->max_profile_idc && encoder->max_profile_idc < profile_idc)
+ profile_idc = encoder->max_profile_idc;
+
+ /* Tuning options to enable Main profile */
+ if (profile_idc >= 77 && profile_idc != 88) {
+ encoder->use_cabac = TRUE;
+ if (!encoder->num_bframes)
+ encoder->num_bframes = 1;
+ }
+
+ /* Tuning options to enable High profile */
+ if (profile_idc >= 100) {
+ encoder->use_dct8x8 = TRUE;
+ }
+ return TRUE;
+}
+
+/* Ensure tuning options */
+static gboolean
+ensure_tuning (GstVaapiEncoderH264 * encoder)
+{
+ gboolean success;
+
+ switch (GST_VAAPI_ENCODER_TUNE (encoder)) {
+ case GST_VAAPI_ENCODER_TUNE_HIGH_COMPRESSION:
+ success = ensure_tuning_high_compression (encoder);
+ break;
+ default:
+ success = TRUE;
+ break;
+ }
+ return success;
+}
+
+/* Handle new GOP starts */
+static void
+reset_gop_start (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;
+}
+
+/* Marks the supplied picture as a B-frame */
+static void
+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 = (reorder_pool->cur_frame_num % encoder->max_frame_num);
+}
+
+/* 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 = (reorder_pool->cur_frame_num % encoder->max_frame_num);
+}
+
+/* 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 = (reorder_pool->cur_frame_num % encoder->max_frame_num);
+
+ g_assert (pic->frame);
+ GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (pic->frame);
+}
+
+/* Marks the supplied picture as an IDR frame */
+static void
+set_idr_frame (GstVaapiEncPicture * pic, GstVaapiEncoderH264 * encoder)
+{
+ g_return_if_fail (pic->type == GST_VAAPI_PICTURE_TYPE_NONE);
+ pic->type = GST_VAAPI_PICTURE_TYPE_I;
+ pic->frame_num = 0;
+ pic->poc = 0;
+ GST_VAAPI_ENC_PICTURE_FLAG_SET (pic, GST_VAAPI_ENC_PICTURE_FLAG_IDR);
+
+ g_assert (pic->frame);
+ GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (pic->frame);
+}
+
+/* Marks the supplied picture a a key-frame */
+static void
+set_key_frame (GstVaapiEncPicture * picture,
+ GstVaapiEncoderH264 * encoder, gboolean is_idr)
+{
+ if (is_idr) {
+ reset_gop_start (encoder);
+ set_idr_frame (picture, encoder);
+ } else
+ set_i_frame (picture, encoder);
+}
+
+/* Fills in VA HRD parameters */
+static void
+fill_hrd_params (GstVaapiEncoderH264 * encoder, VAEncMiscParameterHRD * hrd)
+{
+ 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;
+ }
+}
+
+/* Adds the supplied sequence header (SPS) to the list of packed
+ headers to pass down as-is to the encoder */
+static gboolean
+add_packed_sequence_header (GstVaapiEncoderH264 * encoder,
+ GstVaapiEncPicture * picture, GstVaapiEncSequence * sequence)
+{
+ GstVaapiEncPackedHeader *packed_seq;
+ GstBitWriter bs;
+ VAEncPackedHeaderParameterBuffer packed_seq_param = { 0 };
+ const VAEncSequenceParameterBufferH264 *const seq_param = sequence->param;
+ GstVaapiProfile profile = encoder->profile;
+
+ VAEncMiscParameterHRD hrd_params;
+ guint32 data_bit_size;
+ guint8 *data;
+
+ fill_hrd_params (encoder, &hrd_params);
+
+ 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);
+
+ /* 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;
+
+ bs_write_sps (&bs, seq_param, profile, &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_seq_param.type = VAEncPackedHeaderSequence;
+ packed_seq_param.bit_length = data_bit_size;
+ packed_seq_param.has_emulation_bytes = 0;
+
+ packed_seq = gst_vaapi_enc_packed_header_new (GST_VAAPI_ENCODER (encoder),
+ &packed_seq_param, sizeof (packed_seq_param),
+ data, (data_bit_size + 7) / 8);
+ g_assert (packed_seq);
+
+ gst_vaapi_enc_picture_add_packed_header (picture, packed_seq);
+ gst_vaapi_codec_object_replace (&packed_seq, NULL);
+
+ /* store sps data */
+ _check_sps_pps_status (encoder, data + 4, data_bit_size / 8 - 4);
+ gst_bit_writer_clear (&bs, TRUE);
+ return TRUE;
+
+ /* ERRORS */
+bs_error:
+ {
+ GST_WARNING ("failed to write SPS NAL unit");
+ gst_bit_writer_clear (&bs, TRUE);
+ return FALSE;
+ }
+}
+
+static gboolean
+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;
data, (data_bit_size + 7) / 8);
g_assert (packed_seq);
- gst_vaapi_enc_picture_add_packed_header (picture, packed_seq);
- gst_vaapi_codec_object_replace (&packed_seq, NULL);
+ gst_vaapi_enc_picture_add_packed_header (picture, packed_seq);
+ gst_vaapi_mini_object_replace ((GstVaapiMiniObject **) & packed_seq, NULL);
+
+ /* store subset sps data */
+ _check_sps_pps_status (encoder, data + 4, data_bit_size / 8 - 4);
+ gst_bit_writer_clear (&bs, TRUE);
+ return TRUE;
+
+ /* ERRORS */
+bs_error:
+ {
+ GST_WARNING ("failed to write SPS NAL unit");
+ gst_bit_writer_clear (&bs, TRUE);
+ return FALSE;
+ }
+}
+
+/* Adds the supplied picture header (PPS) to the list of packed
+ headers to pass down as-is to the encoder */
+static gboolean
+add_packed_picture_header (GstVaapiEncoderH264 * encoder,
+ GstVaapiEncPicture * picture)
+{
+ GstVaapiEncPackedHeader *packed_pic;
+ GstBitWriter bs;
+ VAEncPackedHeaderParameterBuffer packed_pic_param = { 0 };
+ const VAEncPictureParameterBufferH264 *const pic_param = picture->param;
+ guint32 data_bit_size;
+ guint8 *data;
+
+ gst_bit_writer_init (&bs, 128 * 8);
+ WRITE_UINT32 (&bs, 0x00000001, 32); /* start code */
+ bs_write_nal_header (&bs, GST_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_pic_param.type = VAEncPackedHeaderPicture;
+ packed_pic_param.bit_length = data_bit_size;
+ packed_pic_param.has_emulation_bytes = 0;
+
+ packed_pic = gst_vaapi_enc_packed_header_new (GST_VAAPI_ENCODER (encoder),
+ &packed_pic_param, sizeof (packed_pic_param),
+ data, (data_bit_size + 7) / 8);
+ g_assert (packed_pic);
+
+ gst_vaapi_enc_picture_add_packed_header (picture, packed_pic);
+ gst_vaapi_codec_object_replace (&packed_pic, NULL);
+
+ /* store pps data */
+ _check_sps_pps_status (encoder, data + 4, data_bit_size / 8 - 4);
+ gst_bit_writer_clear (&bs, TRUE);
+ return TRUE;
+
+ /* ERRORS */
+bs_error:
+ {
+ GST_WARNING ("failed to write PPS NAL unit");
+ gst_bit_writer_clear (&bs, TRUE);
+ return FALSE;
+ }
+}
+
+static gboolean
+get_nal_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);
- /* store sps data */
- _check_sps_pps_status (encoder, data + 4, data_bit_size / 8 - 4);
- gst_bit_writer_clear (&writer, TRUE);
+ 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_picture_packed_header (GstVaapiEncoderH264 * encoder,
- GstVaapiEncPicture * picture)
+add_packed_slice_header (GstVaapiEncoderH264 * encoder,
+ GstVaapiEncPicture * picture, GstVaapiEncSlice * slice)
{
- GstVaapiEncPackedHeader *packed_pic;
- GstBitWriter writer;
- VAEncPackedHeaderParameterBuffer packed_header_param_buffer = { 0 };
- const VAEncPictureParameterBufferH264 *const pic_param = picture->param;
+ 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 (&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 */
- packed_header_param_buffer.type = VAEncPackedHeaderPicture;
- packed_header_param_buffer.bit_length = data_bit_size;
- packed_header_param_buffer.has_emulation_bytes = 0;
+ 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);
- packed_pic = gst_vaapi_enc_packed_header_new (GST_VAAPI_ENCODER (encoder),
- &packed_header_param_buffer, sizeof (packed_header_param_buffer),
- data, (data_bit_size + 7) / 8);
- g_assert (packed_pic);
+ bs_write_slice (&bs, slice_param, encoder, picture);
+ data_bit_size = GST_BIT_WRITER_BIT_SIZE (&bs);
+ data = GST_BIT_WRITER_DATA (&bs);
- gst_vaapi_enc_picture_add_packed_header (picture, packed_pic);
- gst_vaapi_codec_object_replace (&packed_pic, NULL);
+ packed_slice_param.type = VAEncPackedHeaderSlice;
+ packed_slice_param.bit_length = data_bit_size;
+ packed_slice_param.has_emulation_bytes = 0;
- /* store pps data */
- _check_sps_pps_status (encoder, data + 4, data_bit_size / 8 - 4);
- gst_bit_writer_clear (&writer, TRUE);
+ 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)
{
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;
}
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;
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;
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;
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;
/* 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;
sizeof (seq_param->offset_for_ref_frame));
}
- if (encoder->mb_height * 16 - GST_VAAPI_ENCODER_HEIGHT (encoder)) {
+ /* frame_cropping_flag */
+ if ((GST_VAAPI_ENCODER_WIDTH (encoder) & 15) ||
+ (GST_VAAPI_ENCODER_HEIGHT (encoder) & 15)) {
+ static const guint SubWidthC[] = { 1, 2, 2, 1 };
+ static const guint SubHeightC[] = { 1, 2, 1, 1 };
+ const guint CropUnitX =
+ SubWidthC[seq_param->seq_fields.bits.chroma_format_idc];
+ const guint CropUnitY =
+ SubHeightC[seq_param->seq_fields.bits.chroma_format_idc] *
+ (2 - seq_param->seq_fields.bits.frame_mbs_only_flag);
+
seq_param->frame_cropping_flag = 1;
seq_param->frame_crop_left_offset = 0;
- seq_param->frame_crop_right_offset = 0;
+ seq_param->frame_crop_right_offset =
+ (16 * encoder->mb_width -
+ GST_VAAPI_ENCODER_WIDTH (encoder)) / CropUnitX;
seq_param->frame_crop_top_offset = 0;
seq_param->frame_crop_bottom_offset =
- ((encoder->mb_height * 16 - GST_VAAPI_ENCODER_HEIGHT (encoder)) /
- (2 * (!seq_param->seq_fields.bits.frame_mbs_only_flag + 1)));
+ (16 * encoder->mb_height -
+ GST_VAAPI_ENCODER_HEIGHT (encoder)) / CropUnitY;
}
- /* vui not set */
- seq_param->vui_parameters_present_flag =
- (base_encoder->bitrate > 0 ? TRUE : FALSE);
+ /* VUI parameters are always set, at least for timing_info (framerate) */
+ seq_param->vui_parameters_present_flag = TRUE;
if (seq_param->vui_parameters_present_flag) {
seq_param->vui_fields.bits.aspect_ratio_info_present_flag = FALSE;
seq_param->vui_fields.bits.bitstream_restriction_flag = FALSE;
- seq_param->vui_fields.bits.timing_info_present_flag =
- (base_encoder->bitrate > 0 ? TRUE : FALSE);
+ seq_param->vui_fields.bits.timing_info_present_flag = TRUE;
if (seq_param->vui_fields.bits.timing_info_present_flag) {
seq_param->num_units_in_tick = GST_VAAPI_ENCODER_FPS_D (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;
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 &&
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;
GST_VAAPI_ENC_PICTURE_IS_IDR (picture);
pic_param->pic_fields.bits.reference_pic_flag =
(picture->type != GST_VAAPI_PICTURE_TYPE_B);
- pic_param->pic_fields.bits.entropy_coding_mode_flag =
- GST_VAAPI_ENCODER_H264_ENTROPY_MODE_CABAC;
+ pic_param->pic_fields.bits.entropy_coding_mode_flag = encoder->use_cabac;
pic_param->pic_fields.bits.weighted_pred_flag = FALSE;
pic_param->pic_fields.bits.weighted_bipred_idc = 0;
pic_param->pic_fields.bits.constrained_intra_pred_flag = 0;
- pic_param->pic_fields.bits.transform_8x8_mode_flag = (encoder->profile >= GST_VAAPI_PROFILE_H264_HIGH); /* enable 8x8 */
+ pic_param->pic_fields.bits.transform_8x8_mode_flag = encoder->use_dct8x8;
/* enable debloking */
pic_param->pic_fields.bits.deblocking_filter_control_present_flag = TRUE;
pic_param->pic_fields.bits.redundant_pic_cnt_present_flag = FALSE;
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;
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;
/* 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);
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_buffer (picture, misc);
- hrd = misc->impl;
- 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_buffer (picture, misc);
- rate_control = misc->impl;
- 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 gboolean
-ensure_profile_and_level (GstVaapiEncoderH264 * encoder)
-{
- if (!encoder->profile)
- encoder->profile = GST_VAAPI_ENCODER_H264_DEFAULT_PROFILE;
-
- _set_level (encoder);
- return TRUE;
+ /* 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);
- /* Default compression: 64 bits per macroblock */
+ /* Default compression: 48 bits per macroblock in "high-compression" mode */
switch (GST_VAAPI_ENCODER_RATE_CONTROL (encoder)) {
case GST_VAAPI_RATECONTROL_CBR:
case GST_VAAPI_RATECONTROL_VBR:
case GST_VAAPI_RATECONTROL_VBR_CONSTRAINED:
- if (!base_encoder->bitrate)
- base_encoder->bitrate = GST_VAAPI_ENCODER_WIDTH (encoder) *
- GST_VAAPI_ENCODER_HEIGHT (encoder) *
+ if (!base_encoder->bitrate) {
+ /* According to the literature and testing, CABAC entropy coding
+ mode could provide for +10% to +18% improvement in general,
+ thus estimating +15% here ; and using adaptive 8x8 transforms
+ in I-frames could bring up to +10% improvement. */
+ guint bits_per_mb = 48;
+ if (!encoder->use_cabac)
+ bits_per_mb += (bits_per_mb * 15) / 100;
+ if (!encoder->use_dct8x8)
+ bits_per_mb += (bits_per_mb * 10) / 100;
+
+ base_encoder->bitrate =
+ encoder->mb_width * encoder->mb_height * bits_per_mb *
GST_VAAPI_ENCODER_FPS_N (encoder) /
- GST_VAAPI_ENCODER_FPS_D (encoder) / 4 / 1000;
+ GST_VAAPI_ENCODER_FPS_D (encoder) / 1000;
+ GST_INFO ("target bitrate computed to %u kbps", base_encoder->bitrate);
+ }
break;
default:
base_encoder->bitrate = 0;
break;
}
- 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 &&
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);
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
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;
{
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;
}
+/* Generate "codec-data" buffer */
static GstVaapiEncoderStatus
-gst_vaapi_encoder_h264_get_avcC_codec_data (GstVaapiEncoderH264 * encoder,
- GstBuffer ** buffer)
+gst_vaapi_encoder_h264_get_codec_data (GstVaapiEncoder * base_encoder,
+ GstBuffer ** out_buffer_ptr)
{
- GstBuffer *avc_codec;
+ GstVaapiEncoderH264 *const encoder =
+ GST_VAAPI_ENCODER_H264_CAST (base_encoder);
const guint32 configuration_version = 0x01;
- const guint32 length_size_minus_one = 0x03;
- guint32 profile, profile_comp, level_idc;
+ const guint32 nal_length_size = 4;
+ guint8 profile_idc, profile_comp, level_idc;
GstMapInfo sps_info, pps_info;
- GstVaapiEncoderStatus ret = GST_VAAPI_ENCODER_STATUS_SUCCESS;
- GstBitWriter writer;
+ GstBitWriter bs;
+ GstBuffer *buffer;
- g_assert (buffer);
if (!encoder->sps_data || !encoder->pps_data)
return GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_HEADER;
+ if (gst_buffer_get_size (encoder->sps_data) < 4)
+ return GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_HEADER;
if (!gst_buffer_map (encoder->sps_data, &sps_info, GST_MAP_READ))
- return GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
-
- if (FALSE == _read_sps_attributes (sps_info.data, sps_info.size,
- &profile, &profile_comp, &level_idc)) {
- ret = GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_HEADER;
- goto end;
- }
-
- if (!gst_buffer_map (encoder->pps_data, &pps_info, GST_MAP_READ)) {
- ret = GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
- goto end;
- }
-
- gst_bit_writer_init (&writer, (sps_info.size + pps_info.size + 64) * 8);
- /* codec_data */
- gst_bit_writer_put_bits_uint32 (&writer, configuration_version, 8);
- gst_bit_writer_put_bits_uint32 (&writer, profile, 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, length_size_minus_one, 2);
- gst_bit_writer_put_bits_uint32 (&writer, 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 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);
-
- avc_codec = gst_buffer_new_wrapped (GST_BIT_WRITER_DATA (&writer),
- GST_BIT_WRITER_BIT_SIZE (&writer) / 8);
- g_assert (avc_codec);
- if (!avc_codec) {
- ret = GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
- goto clear_writer;
- }
- *buffer = avc_codec;
+ goto error_map_sps_buffer;
+
+ if (!gst_buffer_map (encoder->pps_data, &pps_info, GST_MAP_READ))
+ goto error_map_pps_buffer;
+
+ /* skip sps_data[0], which is the nal_unit_type */
+ profile_idc = sps_info.data[1];
+ profile_comp = sps_info.data[2];
+ level_idc = sps_info.data[3];
+
+ /* Header */
+ 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 */
+ 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 */
+ 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_bit_writer_clear (&writer, FALSE);
- ret = GST_VAAPI_ENCODER_STATUS_SUCCESS;
- goto end;
-
-clear_writer:
- gst_bit_writer_clear (&writer, TRUE);
-
-end:
gst_buffer_unmap (encoder->sps_data, &sps_info);
- return ret;
-}
-
-static GstVaapiEncoderStatus
-gst_vaapi_encoder_h264_get_codec_data (GstVaapiEncoder * base_encoder,
- GstBuffer ** buffer)
-{
- GstVaapiEncoderH264 *const encoder =
- GST_VAAPI_ENCODER_H264_CAST (base_encoder);
-
- *buffer = NULL;
+ 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;
- if (!encoder->is_avc)
- return GST_VAAPI_ENCODER_STATUS_SUCCESS;
+ gst_bit_writer_clear (&bs, FALSE);
+ return GST_VAAPI_ENCODER_STATUS_SUCCESS;
- return gst_vaapi_encoder_h264_get_avcC_codec_data (encoder, buffer);
+ /* 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");
+ return GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
+ }
+error_map_pps_buffer:
+ {
+ GST_ERROR ("failed to map PPS packed header");
+ gst_buffer_unmap (encoder->sps_data, &sps_info);
+ return GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
+ }
+error_alloc_buffer:
+ {
+ GST_ERROR ("failed to allocate codec-data buffer");
+ gst_bit_writer_clear (&bs, TRUE);
+ return GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
+ }
}
static GstVaapiEncoderStatus
{
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;
}
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:
g_assert (picture);
- frame = GST_VAAPI_ENC_PICTURE_GET_FRAME (picture);
+ frame = picture->frame;
if (GST_CLOCK_TIME_IS_VALID (frame->pts))
frame->pts += encoder->cts_offset;
*output = picture;
return GST_VAAPI_ENCODER_STATUS_SUCCESS;
}
-static void
+static GstVaapiEncoderStatus
set_context_info (GstVaapiEncoder * base_encoder)
{
GstVaapiEncoderH264 *const encoder =
MAX_SLICE_HDR_SIZE = 397 + 2572 + 6670 + 2402,
};
- base_encoder->profile = encoder->profile;
+ if (!ensure_hw_profile (encoder))
+ return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
+
base_encoder->num_ref_frames =
- (encoder->num_bframes ? 2 : 1) + DEFAULT_SURFACES_COUNT;
+ ((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. */
/* XXX: exclude slice groups, scaling lists, MVC/SVC extensions */
base_encoder->codedbuf_size += 4 + GST_ROUND_UP_8 (MAX_PPS_HDR_SIZE) / 8;
- /* Account for slice header. At most 200 slices are supported */
- base_encoder->codedbuf_size += 200 * (4 +
+ /* Account for slice header */
+ base_encoder->codedbuf_size += encoder->num_slices * (4 +
GST_ROUND_UP_8 (MAX_SLICE_HDR_SIZE) / 8);
+
+ return GST_VAAPI_ENCODER_STATUS_SUCCESS;
}
static GstVaapiEncoderStatus
{
GstVaapiEncoderH264 *const encoder =
GST_VAAPI_ENCODER_H264_CAST (base_encoder);
+ GstVaapiEncoderStatus status;
encoder->mb_width = (GST_VAAPI_ENCODER_WIDTH (encoder) + 15) / 16;
encoder->mb_height = (GST_VAAPI_ENCODER_HEIGHT (encoder) + 15) / 16;
- if (!ensure_profile_and_level (encoder))
- goto error;
- if (!ensure_bitrate (encoder))
- goto error;
+ status = ensure_profile_and_level (encoder);
+ if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
+ return status;
reset_properties (encoder);
- set_context_info (base_encoder);
- return GST_VAAPI_ENCODER_STATUS_SUCCESS;
-
-error:
- return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
+ return set_context_info (base_encoder);
}
static gboolean
{
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;
}
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
case GST_VAAPI_ENCODER_H264_PROP_NUM_SLICES:
encoder->num_slices = g_value_get_uint (value);
break;
+ case GST_VAAPI_ENCODER_H264_PROP_CABAC:
+ encoder->use_cabac = g_value_get_boolean (value);
+ break;
+ 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;
}
"Number of slices per frame",
1, 200, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ * GstVaapiEncoderH264:cabac:
+ *
+ * Enable CABAC entropy coding mode for improved compression ratio,
+ * at the expense that the minimum target profile is Main. Default
+ * is CAVLC entropy coding mode.
+ */
+ GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
+ GST_VAAPI_ENCODER_H264_PROP_CABAC,
+ g_param_spec_boolean ("cabac",
+ "Enable CABAC",
+ "Enable CABAC entropy coding mode",
+ FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstVaapiEncoderH264:dct8x8:
+ *
+ * Enable adaptive use of 8x8 transforms in I-frames. This improves
+ * the compression ratio by the minimum target profile is High.
+ * Default is to use 4x4 DCT only.
+ */
+ GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
+ GST_VAAPI_ENCODER_H264_PROP_DCT8X8,
+ g_param_spec_boolean ("dct8x8",
+ "Enable 8x8 DCT",
+ "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;
}
-void
-gst_vaapi_encoder_h264_set_avc (GstVaapiEncoderH264 * encoder, gboolean is_avc)
+/**
+ * gst_vaapi_encoder_h264_set_max_profile:
+ * @encoder: a #GstVaapiEncoderH264
+ * @profile: an H.264 #GstVaapiProfile
+ *
+ * Notifies the @encoder to use coding tools from the supplied
+ * @profile at most.
+ *
+ * This means that if the minimal profile derived to
+ * support the specified coding tools is greater than this @profile,
+ * then an error is returned when the @encoder is configured.
+ *
+ * Return value: %TRUE on success
+ */
+gboolean
+gst_vaapi_encoder_h264_set_max_profile (GstVaapiEncoderH264 * encoder,
+ GstVaapiProfile profile)
{
- encoder->is_avc = is_avc;
+ guint8 profile_idc;
+
+ g_return_val_if_fail (encoder != NULL, FALSE);
+ g_return_val_if_fail (profile != GST_VAAPI_PROFILE_UNKNOWN, FALSE);
+
+ if (gst_vaapi_profile_get_codec (profile) != GST_VAAPI_CODEC_H264)
+ return FALSE;
+
+ profile_idc = gst_vaapi_utils_h264_get_profile_idc (profile);
+ if (!profile_idc)
+ return FALSE;
+
+ encoder->max_profile_idc = profile_idc;
+ return TRUE;
}
+/**
+ * gst_vaapi_encoder_h264_get_profile_and_level:
+ * @encoder: a #GstVaapiEncoderH264
+ * @out_profile_ptr: return location for the #GstVaapiProfile
+ * @out_level_ptr: return location for the #GstVaapiLevelH264
+ *
+ * Queries the H.264 @encoder for the active profile and level. That
+ * information is only constructed and valid after the encoder is
+ * configured, i.e. after the gst_vaapi_encoder_set_codec_state()
+ * function is called.
+ *
+ * Return value: %TRUE on success
+ */
gboolean
-gst_vaapi_encoder_h264_is_avc (GstVaapiEncoderH264 * encoder)
+gst_vaapi_encoder_h264_get_profile_and_level (GstVaapiEncoderH264 * encoder,
+ GstVaapiProfile * out_profile_ptr, GstVaapiLevelH264 * out_level_ptr)
{
- return encoder->is_avc;
+ g_return_val_if_fail (encoder != NULL, FALSE);
+
+ if (!encoder->profile || !encoder->level)
+ return FALSE;
+
+ if (out_profile_ptr)
+ *out_profile_ptr = encoder->profile;
+ if (out_level_ptr)
+ *out_level_ptr = encoder->level;
+ return TRUE;
}