*
* Functions to create and handle encoding profiles.
*
- * Encoding profiles describe the media types and settings one wishes to use
- * for an encoding process. The top-level profiles are commonly
+ * Encoding profiles describe the media types and settings one wishes to use for
+ * an encoding process. The top-level profiles are commonly
* #GstEncodingContainerProfile(s) (which contains a user-readable name and
* description along with which container format to use). These, in turn,
* reference one or more #GstEncodingProfile(s) which indicate which encoding
*
* ### Setting properties on muxers or on the encoding profile itself
*
- * Moreover, you can set extra properties `presence`, `single-segment` and
- * `variable-framerate` * of an * encoding profile using the `|presence=` syntax
- * as in:
+ * Moreover, you can set the extra properties:
+ *
+ * * `|element-properties,property1=true` (See
+ * #gst_encoding_profile_set_element_properties)
+ * * `|presence=true` (See See #gst_encoding_profile_get_presence)
+ * * `|single-segment=true` (See #gst_encoding_profile_set_single_segment)
+ * * `|single-segment=true` (See
+ * #gst_encoding_video_profile_set_variableframerate)
+ *
+ * for example:
*
* ```
- * video/webm:video/x-vp8|presence=1,variable-framerate=true|single-segment=true:audio/x-vorbis
+ * video/webm:video/x-vp8|presence=1|element-properties,target-bitrate=500000:audio/x-vorbis
* ```
*
- * This field allows specifies the maximum number of times a
- * #GstEncodingProfile can be used inside an encodebin. If 0, it is not a
- * mandatory stream and can be used as many times as necessary.
- *
* ### Enforcing properties to the stream itself (video size, number of audio channels, etc..)
*
* You can also use the `restriction_caps->encoded_format_caps` syntax to
#include <string.h>
/* GstEncodingProfile API */
+#define PROFILE_LOCK(profile) (g_mutex_lock(&((GstEncodingProfile*)profile)->lock))
+#define PROFILE_UNLOCK(profile) (g_mutex_unlock(&((GstEncodingProfile*)profile)->lock))
struct _GstEncodingProfile
{
gchar *preset;
gchar *preset_name;
guint presence;
- GstCaps *restriction;
gboolean allow_dynamic_output;
gboolean enabled;
gboolean single_segment;
+
+ GMutex lock; // {
+ GstCaps *restriction;
+ GstStructure *element_properties;
+ // }
};
struct _GstEncodingProfileClass
{
FIRST_PROPERTY,
PROP_RESTRICTION_CAPS,
+ PROP_ELEMENT_PROPERTIES,
LAST_PROPERTY
};
case PROP_RESTRICTION_CAPS:
gst_value_set_caps (value, prof->restriction);
break;
+ case PROP_ELEMENT_PROPERTIES:
+ PROFILE_LOCK (prof);
+ gst_value_set_structure (value, prof->element_properties);
+ PROFILE_UNLOCK (prof);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
gst_encoding_profile_set_restriction (prof, gst_caps_copy
(gst_value_get_caps (value)));
break;
+ case PROP_ELEMENT_PROPERTIES:
+ {
+ const GstStructure *structure = gst_value_get_structure (value);
+
+ gst_encoding_profile_set_element_properties (prof,
+ structure ? gst_structure_copy (structure) : NULL);
+ break;
+ }
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
_properties[PROP_RESTRICTION_CAPS] =
g_param_spec_boxed ("restriction-caps", "Restriction caps",
"The restriction caps to use", GST_TYPE_CAPS,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-
- g_object_class_install_property (gobject_class,
- PROP_RESTRICTION_CAPS, _properties[PROP_RESTRICTION_CAPS]);
-
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * GstEncodingProfile:element-properties:
+ *
+ * A #GstStructure defining the properties to be set to the element
+ * the profile represents.
+ *
+ * For example for `av1enc`:
+ *
+ * ```
+ * element-properties,row-mt=true, end-usage=vbr
+ * ```
+ *
+ * Since: 1.20
+ */
+ _properties[PROP_ELEMENT_PROPERTIES] =
+ g_param_spec_boxed ("element-properties", "Element properties",
+ "The element properties to use. "
+ "Example: {properties,boolean-prop=true,string-prop=\"hi\"}.",
+ GST_TYPE_STRUCTURE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (gobject_class, LAST_PROPERTY, _properties);
}
/**
_properties[PROP_RESTRICTION_CAPS]);
}
+/**
+ * gst_encoding_profile_set_element_properties:
+ * @self: a #GstEncodingProfile
+ * @element_properties: (transfer full): A #GstStructure defining the properties
+ * to be set to the element the profile represents.
+ *
+ * This allows setting the muxing/encoding element properties.
+ *
+ * **Set properties generically**
+ *
+ * ``` properties
+ * [element-properties, boolean-prop=true, string-prop="hi"]
+ * ```
+ *
+ * **Mapping properties with well known element factories**
+ *
+ * ``` properties
+ * element-properties-map, map = {
+ * [openh264enc, gop-size=32, ],
+ * [x264enc, key-int-max=32, tune=zerolatency],
+ * }
+ * ```
+ *
+ * Since: 1.20
+ */
+void
+gst_encoding_profile_set_element_properties (GstEncodingProfile * self,
+ GstStructure * element_properties)
+{
+ g_return_if_fail (GST_IS_ENCODING_PROFILE (self));
+ g_return_if_fail (!element_properties
+ || GST_IS_STRUCTURE (element_properties));
+
+#ifndef G_DISABLE_CHECKS
+ if (element_properties &&
+ (gst_structure_has_name (element_properties, "element-properties-map")
+ || gst_structure_has_name (element_properties, "properties-map")
+ || gst_structure_has_name (element_properties, "map")))
+ g_return_if_fail (gst_structure_has_field_typed (element_properties, "map",
+ GST_TYPE_LIST));
+#endif
+
+ PROFILE_LOCK (self);
+ if (self->element_properties)
+ gst_structure_free (self->element_properties);
+ if (element_properties)
+ self->element_properties = element_properties;
+ else
+ self->element_properties = NULL;
+ PROFILE_UNLOCK (self);
+
+ g_object_notify_by_pspec (G_OBJECT (self),
+ _properties[PROP_ELEMENT_PROPERTIES]);
+}
+
+/**
+ * gst_encoding_profile_get_element_properties:
+ * @self: a #GstEncodingProfile
+ *
+ * Returns: (transfer full) (nullable): The properties that are going to be set on the underlying element
+ *
+ * Since: 1.20
+ */
+GstStructure *
+gst_encoding_profile_get_element_properties (GstEncodingProfile * self)
+{
+ GstStructure *res = NULL;
+
+ g_return_val_if_fail (GST_IS_ENCODING_PROFILE (self), NULL);
+
+ PROFILE_LOCK (self);
+ if (self->element_properties)
+ res = gst_structure_copy (self->element_properties);
+ PROFILE_UNLOCK (self);
+
+ return res;
+}
+
/* Container profiles */
struct _GstEncodingContainerProfile
return profile;
}
+static gboolean
+gst_structure_validate_name (const gchar * name)
+{
+ const gchar *s;
+
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ if (G_UNLIKELY (!g_ascii_isalpha (*name)))
+ return FALSE;
+
+ /* FIXME: test name string more */
+ s = &name[1];
+ while (*s && (g_ascii_isalnum (*s) || strchr ("/-_.:+", *s) != NULL))
+ s++;
+
+ if (*s == ',')
+ return TRUE;
+
+ if (G_UNLIKELY (*s != '\0'))
+ return FALSE;
+
+ return TRUE;
+}
+
static GstEncodingProfile *
create_encoding_stream_profile (gchar * serialized_profile,
GList * muxers_and_encoders, GstCaps * raw_audio_caps,
gchar *strcaps, *strpresence, **strprops_v, **restriction_format,
**preset_v, *preset_name = NULL, *factory_name = NULL,
*variable_framerate = NULL;
+ GstStructure *element_properties = NULL;
GstCaps *restrictioncaps = NULL;
GstEncodingProfile *profile = NULL;
}
for (propi = 1; strprops_v[propi]; propi++) {
- gchar **propv = g_strsplit (strprops_v[propi], "=", -1);
+ gchar **propv;
gchar *presence_str = NULL;
+ gchar *prop = strprops_v[propi];
+ GstStructure *tmpstruct = NULL;
+ if (gst_structure_validate_name (prop))
+ tmpstruct = gst_structure_new_from_string (prop);
+ if (tmpstruct) {
+ if (element_properties)
+ gst_structure_free (element_properties);
+
+ element_properties = tmpstruct;
+
+ continue;
+ }
+
+ propv = g_strsplit (prop, "=", -1);
if (propv[1] && propv[2]) {
g_warning ("Wrong format for property: %s, only 1 `=` is expected",
- strprops_v[propi]);
+ prop);
return NULL;
}
single_segment = g_value_get_boolean (&v);
g_value_reset (&v);
+ } else {
+ g_warning ("Unsupported property: %s", propv[0]);
+ return NULL;
}
if (presence_str) {
return NULL;
}
+ if (element_properties)
+ gst_encoding_profile_set_element_properties (profile, element_properties);
+
return profile;
}
return parser;
}
+static gboolean
+_set_properties (GQuark property_id, const GValue * value, GObject * element)
+{
+ GST_DEBUG_OBJECT (element, "Setting %s", g_quark_to_string (property_id));
+ g_object_set_property (element, g_quark_to_string (property_id), value);
+
+ return TRUE;
+}
+
+static void
+set_element_properties_from_encoding_profile (GstEncodingProfile * profile,
+ GParamSpec * arg G_GNUC_UNUSED, GstElement * element)
+{
+ gint i;
+ const GValue *v;
+ GstElementFactory *factory;
+ GstStructure *properties =
+ gst_encoding_profile_get_element_properties (profile);
+
+ if (!properties)
+ return;
+
+ if (!gst_structure_has_name (properties, "element-properties-map")) {
+ gst_structure_foreach (properties,
+ (GstStructureForeachFunc) _set_properties, element);
+ goto done;
+ }
+
+ factory = gst_element_get_factory (element);
+ if (!factory) {
+ GST_INFO_OBJECT (profile, "No factory for underlying element, "
+ "not setting properties");
+ return;
+ }
+
+ v = gst_structure_get_value (properties, "map");
+ for (i = 0; i < gst_value_list_get_size (v); i++) {
+ const GValue *map_value = gst_value_list_get_value (v, i);
+ const GstStructure *tmp_properties;
+
+ if (!GST_VALUE_HOLDS_STRUCTURE (map_value)) {
+ g_warning ("Invalid value type %s in the property map "
+ "(expected GstStructure)", G_VALUE_TYPE_NAME (map_value));
+ continue;
+ }
+
+ tmp_properties = gst_value_get_structure (map_value);
+ if (!gst_structure_has_name (tmp_properties, GST_OBJECT_NAME (factory))) {
+ GST_INFO_OBJECT (GST_OBJECT_PARENT (element),
+ "Ignoring values for %" GST_PTR_FORMAT, tmp_properties);
+ continue;
+ }
+
+ GST_DEBUG_OBJECT (GST_OBJECT_PARENT (element),
+ "Setting %" GST_PTR_FORMAT " on %" GST_PTR_FORMAT, tmp_properties,
+ element);
+ gst_structure_foreach (tmp_properties,
+ (GstStructureForeachFunc) _set_properties, element);
+ goto done;
+ }
+
+ GST_ERROR_OBJECT (GST_OBJECT_PARENT (element), "Unknown factory: %s",
+ GST_OBJECT_NAME (factory));
+
+done:
+ gst_structure_free (properties);
+}
+
static GstElement *
_create_element_and_set_preset (GstElementFactory * factory,
- const gchar * preset, const gchar * name, const gchar * preset_name)
+ GstEncodingProfile * profile, const gchar * name)
{
GstElement *res = NULL;
+ const gchar *preset;
+ const gchar *preset_name;
+ preset_name = gst_encoding_profile_get_preset_name (profile);
+ preset = gst_encoding_profile_get_preset (profile);
GST_DEBUG ("Creating element from factory %s (preset factory name: %s"
" preset name: %s)", GST_OBJECT_NAME (factory), preset_name, preset);
}
}
/* Else we keep it */
+ if (res) {
+ set_element_properties_from_encoding_profile (profile, NULL, res);
+
+ g_signal_connect (profile, "notify::element-properties",
+ G_CALLBACK (set_element_properties_from_encoding_profile), res);
+ }
return res;
}
GstElement *encoder = NULL;
GstElementFactory *encoderfact = NULL;
GstCaps *format;
- const gchar *preset, *preset_name;
format = gst_encoding_profile_get_format (sprof);
- preset = gst_encoding_profile_get_preset (sprof);
- preset_name = gst_encoding_profile_get_preset_name (sprof);
GST_DEBUG ("Getting list of encoders for format %" GST_PTR_FORMAT, format);
for (tmp = encoders; tmp; tmp = tmp->next) {
encoderfact = (GstElementFactory *) tmp->data;
- if ((encoder = _create_element_and_set_preset (encoderfact, preset,
- NULL, preset_name)))
+ if ((encoder = _create_element_and_set_preset (encoderfact, sprof, NULL)))
break;
}
tosync = g_list_append (tosync, sgroup->identity);
} else {
GST_INFO_OBJECT (ebin, "Single segment is not supported when avoiding"
- " to reencode!");
+ " to re-encode!");
}
}
GstElement *formatter = NULL;
GstElementFactory *formatterfact = NULL;
GstCaps *format;
- const gchar *preset, *preset_name;
-
format = gst_encoding_profile_get_format (sprof);
- preset = gst_encoding_profile_get_preset (sprof);
- preset_name = gst_encoding_profile_get_preset_name (sprof);
GST_DEBUG ("Getting list of formatters for format %" GST_PTR_FORMAT, format);
GST_OBJECT_NAME (formatterfact));
if ((formatter =
- _create_element_and_set_preset (formatterfact, preset,
- NULL, preset_name)))
+ _create_element_and_set_preset (formatterfact, sprof, NULL)))
break;
}
GstElementFactory *muxerfact = NULL;
const GList *tmp;
GstCaps *format;
- const gchar *preset, *preset_name;
+ const gchar *preset_name;
format = gst_encoding_profile_get_format (ebin->profile);
- preset = gst_encoding_profile_get_preset (ebin->profile);
preset_name = gst_encoding_profile_get_preset_name (ebin->profile);
GST_DEBUG_OBJECT (ebin, "Getting list of muxers for format %" GST_PTR_FORMAT,
/* Only use a muxer than can use all streams and than can accept the
* preset (which may be present or not) */
if (cansinkstreams && (muxer =
- _create_element_and_set_preset (muxerfact, preset, "muxer",
- preset_name)))
+ _create_element_and_set_preset (muxerfact, ebin->profile, "muxer")))
break;
}
if (sgroup->inqueue)
gst_element_set_state (sgroup->inqueue, GST_STATE_NULL);
- if (sgroup->encoder)
+ if (sgroup->encoder) {
gst_element_set_state (sgroup->encoder, GST_STATE_NULL);
+ g_signal_handlers_disconnect_by_func (sgroup->profile,
+ set_element_properties_from_encoding_profile, sgroup->encoder);
+ }
if (sgroup->fakesink)
gst_element_set_state (sgroup->fakesink, GST_STATE_NULL);
if (sgroup->outfilter) {
/* Remove muxer if present */
if (ebin->muxer) {
+ g_signal_handlers_disconnect_by_func (ebin->profile,
+ set_element_properties_from_encoding_profile, ebin->muxer);
gst_element_set_state (ebin->muxer, GST_STATE_NULL);
gst_bin_remove (GST_BIN (ebin), ebin->muxer);
ebin->muxer = NULL;