encoder: h264: allow target decoder constraints.

Allow user to precise the largest profile to use for encoding due
to target decoder constraints. For instance, if CABAC entropy coding
mode is requested by "constrained-baseline" profile only is desired,
then an error is returned during codec configuration.

Also make sure that the suitable profile we derived actually matches
what the HW can cope with.

https://bugzilla.gnome.org/show_bug.cgi?id=719694
This commit is contained in:
Gwenole Beauchesne 2014-01-12 22:24:04 +01:00
parent 00e0af9a7c
commit bdf91aa765
6 changed files with 217 additions and 8 deletions

View file

@ -154,6 +154,37 @@ _check_sps_pps_status (GstVaapiEncoderH264 * encoder,
} }
} }
/* 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 */ /* Derives the profile supported by the underlying hardware */
static gboolean static gboolean
ensure_hw_profile (GstVaapiEncoderH264 * encoder) ensure_hw_profile (GstVaapiEncoderH264 * encoder)
@ -197,6 +228,36 @@ error_unsupported_profile:
} }
} }
/* 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 */ /* Derives the minimum profile from the active coding tools */
static gboolean static gboolean
ensure_profile (GstVaapiEncoderH264 * encoder) ensure_profile (GstVaapiEncoderH264 * encoder)
@ -1230,14 +1291,21 @@ ensure_misc (GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture)
return TRUE; return TRUE;
} }
static gboolean static GstVaapiEncoderStatus
ensure_profile_and_level (GstVaapiEncoderH264 * encoder) ensure_profile_and_level (GstVaapiEncoderH264 * encoder)
{ {
if (!ensure_profile (encoder)) if (!ensure_profile (encoder) || !ensure_profile_limits (encoder))
return FALSE; return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
if (!ensure_level (encoder)) if (!ensure_level (encoder))
return FALSE; return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
return TRUE;
/* Check HW constraints */
if (!ensure_hw_profile_limits (encoder))
return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
if (encoder->profile_idc > encoder->hw_max_profile_idc)
return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
return GST_VAAPI_ENCODER_STATUS_SUCCESS;
} }
static gboolean static gboolean
@ -1601,12 +1669,15 @@ gst_vaapi_encoder_h264_reconfigure (GstVaapiEncoder * base_encoder)
{ {
GstVaapiEncoderH264 *const encoder = GstVaapiEncoderH264 *const encoder =
GST_VAAPI_ENCODER_H264_CAST (base_encoder); GST_VAAPI_ENCODER_H264_CAST (base_encoder);
GstVaapiEncoderStatus status;
encoder->mb_width = (GST_VAAPI_ENCODER_WIDTH (encoder) + 15) / 16; encoder->mb_width = (GST_VAAPI_ENCODER_WIDTH (encoder) + 15) / 16;
encoder->mb_height = (GST_VAAPI_ENCODER_HEIGHT (encoder) + 15) / 16; encoder->mb_height = (GST_VAAPI_ENCODER_HEIGHT (encoder) + 15) / 16;
if (!ensure_profile_and_level (encoder)) status = ensure_profile_and_level (encoder);
goto error; if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
return status;
if (!ensure_bitrate (encoder)) if (!ensure_bitrate (encoder))
goto error; goto error;
@ -1819,6 +1890,40 @@ gst_vaapi_encoder_h264_get_default_properties (void)
return props; return props;
} }
/**
* 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)
{
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: * gst_vaapi_encoder_h264_get_profile_and_level:
* @encoder: a #GstVaapiEncoderH264 * @encoder: a #GstVaapiEncoderH264

View file

@ -60,6 +60,10 @@ gst_vaapi_encoder_h264_new (GstVaapiDisplay * display);
GPtrArray * GPtrArray *
gst_vaapi_encoder_h264_get_default_properties (void); gst_vaapi_encoder_h264_get_default_properties (void);
gboolean
gst_vaapi_encoder_h264_set_max_profile (GstVaapiEncoderH264 * encoder,
GstVaapiProfile profile);
gboolean gboolean
gst_vaapi_encoder_h264_get_profile_and_level (GstVaapiEncoderH264 * encoder, gst_vaapi_encoder_h264_get_profile_and_level (GstVaapiEncoderH264 * encoder,
GstVaapiProfile * out_profile_ptr, GstVaapiLevelH264 * out_level_ptr); GstVaapiProfile * out_profile_ptr, GstVaapiLevelH264 * out_level_ptr);

View file

@ -39,6 +39,8 @@ struct _GstVaapiEncoderH264
GstVaapiProfile profile; GstVaapiProfile profile;
GstVaapiLevelH264 level; GstVaapiLevelH264 level;
guint8 profile_idc; guint8 profile_idc;
guint8 max_profile_idc;
guint8 hw_max_profile_idc;
guint8 level_idc; guint8 level_idc;
guint32 idr_period; guint32 idr_period;
guint32 init_qp; guint32 init_qp;

View file

@ -127,6 +127,16 @@ map_lookup_name (const struct map *m, const gchar * name)
return NULL; return NULL;
} }
/** Returns a relative score for the supplied GstVaapiProfile */
guint
gst_vaapi_utils_h264_get_profile_score (GstVaapiProfile profile)
{
const struct map *const m =
map_lookup_value (gst_vaapi_h264_profile_map, profile);
return m ? 1 + (m - gst_vaapi_h264_profile_map) : 0;
}
/** Returns GstVaapiProfile from H.264 profile_idc value */ /** Returns GstVaapiProfile from H.264 profile_idc value */
GstVaapiProfile GstVaapiProfile
gst_vaapi_utils_h264_get_profile (guint8 profile_idc) gst_vaapi_utils_h264_get_profile (guint8 profile_idc)

View file

@ -71,6 +71,10 @@ typedef enum
GST_VAAPI_LEVEL_H264_L5_2, GST_VAAPI_LEVEL_H264_L5_2,
} GstVaapiLevelH264; } GstVaapiLevelH264;
/* Returns a relative score for the supplied GstVaapiProfile */
guint
gst_vaapi_utils_h264_get_profile_score (GstVaapiProfile profile);
/* Returns GstVaapiProfile from a string representation */ /* Returns GstVaapiProfile from a string representation */
GstVaapiProfile GstVaapiProfile
gst_vaapi_utils_h264_get_profile_from_string (const gchar * str); gst_vaapi_utils_h264_get_profile_from_string (const gchar * str);

View file

@ -22,6 +22,7 @@
#include "gst/vaapi/sysdeps.h" #include "gst/vaapi/sysdeps.h"
#include <gst/vaapi/gstvaapidisplay.h> #include <gst/vaapi/gstvaapidisplay.h>
#include <gst/vaapi/gstvaapiencoder_h264.h> #include <gst/vaapi/gstvaapiencoder_h264.h>
#include <gst/vaapi/gstvaapiutils_h264.h>
#include "gstvaapiencode_h264.h" #include "gstvaapiencode_h264.h"
#include "gstvaapipluginutil.h" #include "gstvaapipluginutil.h"
#if GST_CHECK_VERSION(1,0,0) #if GST_CHECK_VERSION(1,0,0)
@ -59,7 +60,8 @@ static const char gst_vaapiencode_h264_sink_caps_str[] =
/* *INDENT-OFF* */ /* *INDENT-OFF* */
static const char gst_vaapiencode_h264_src_caps_str[] = static const char gst_vaapiencode_h264_src_caps_str[] =
GST_CODEC_CAPS; GST_CODEC_CAPS ", "
"profile = (string) { constrained-baseline, baseline, main, high }";
/* *INDENT-ON* */ /* *INDENT-ON* */
/* *INDENT-OFF* */ /* *INDENT-OFF* */
@ -123,6 +125,87 @@ gst_vaapiencode_h264_get_property (GObject * object,
} }
} }
typedef struct
{
GstVaapiProfile best_profile;
guint best_score;
} FindBestProfileData;
static void
find_best_profile_value (FindBestProfileData * data, const GValue * value)
{
const gchar *str;
GstVaapiProfile profile;
guint score;
if (!value || !G_VALUE_HOLDS_STRING (value))
return;
str = g_value_get_string (value);
if (!str)
return;
profile = gst_vaapi_utils_h264_get_profile_from_string (str);
if (!profile)
return;
score = gst_vaapi_utils_h264_get_profile_score (profile);
if (score < data->best_score)
return;
data->best_profile = profile;
data->best_score = score;
}
static GstVaapiProfile
find_best_profile (GstCaps * caps)
{
FindBestProfileData data;
guint i, j, num_structures, num_values;
data.best_profile = GST_VAAPI_PROFILE_UNKNOWN;
data.best_score = 0;
num_structures = gst_caps_get_size (caps);
for (i = 0; i < num_structures; i++) {
GstStructure *const structure = gst_caps_get_structure (caps, i);
const GValue *const value = gst_structure_get_value (structure, "profile");
if (!value)
continue;
if (G_VALUE_HOLDS_STRING (value))
find_best_profile_value (&data, value);
else if (GST_VALUE_HOLDS_LIST (value)) {
num_values = gst_value_list_get_size (value);
for (j = 0; j < num_values; j++)
find_best_profile_value (&data, gst_value_list_get_value (value, j));
}
}
return data.best_profile;
}
static gboolean
gst_vaapiencode_h264_set_config (GstVaapiEncode * base_encode)
{
GstVaapiEncoderH264 *const encoder =
GST_VAAPI_ENCODER_H264 (base_encode->encoder);
GstCaps *allowed_caps;
GstVaapiProfile profile;
/* Check for the largest profile that is supported */
allowed_caps =
gst_pad_get_allowed_caps (GST_VAAPI_PLUGIN_BASE_SRC_PAD (base_encode));
if (!allowed_caps)
return TRUE;
profile = find_best_profile (allowed_caps);
gst_caps_unref (allowed_caps);
if (profile) {
GST_INFO ("using %s profile as target decoder constraints",
gst_vaapi_utils_h264_get_profile_string (profile));
if (!gst_vaapi_encoder_h264_set_max_profile (encoder, profile))
return FALSE;
}
return TRUE;
}
static GstCaps * static GstCaps *
gst_vaapiencode_h264_get_caps (GstVaapiEncode * base_encode) gst_vaapiencode_h264_get_caps (GstVaapiEncode * base_encode)
{ {
@ -305,6 +388,7 @@ gst_vaapiencode_h264_class_init (GstVaapiEncodeH264Class * klass)
object_class->get_property = gst_vaapiencode_h264_get_property; object_class->get_property = gst_vaapiencode_h264_get_property;
encode_class->get_properties = gst_vaapi_encoder_h264_get_default_properties; encode_class->get_properties = gst_vaapi_encoder_h264_get_default_properties;
encode_class->set_config = gst_vaapiencode_h264_set_config;
encode_class->get_caps = gst_vaapiencode_h264_get_caps; encode_class->get_caps = gst_vaapiencode_h264_get_caps;
encode_class->alloc_encoder = gst_vaapiencode_h264_alloc_encoder; encode_class->alloc_encoder = gst_vaapiencode_h264_alloc_encoder;
encode_class->alloc_buffer = gst_vaapiencode_h264_alloc_buffer; encode_class->alloc_buffer = gst_vaapiencode_h264_alloc_buffer;