encoding-profile: Allow using factory names in serialization format

Instead of enforcing the user to know and understand caps to describe
the encoding format, let him use element factory names directly.
This also makes it possible to ensure that a specific encodore/muxer
is used instead of letting the ranking system do it.

It is now possible to describe an encoding format simply specifying:

  matroskamux:x264enc:vobisenc

Factor out functions in the parsing, cleaning up the whole thing.
Update documentation.
This commit is contained in:
Thibault Saunier 2016-12-23 14:23:48 -03:00
parent 998f28b65c
commit 51cd2bd926

View file

@ -42,23 +42,43 @@
* Encoding profiles can be created at runtime by the application or loaded
* from (and saved to) file using the #GstEncodingTarget API.
*
* # The encoding profile serialization format
* # Defining a GstEncodingProfile as a string
*
* This is the serialization format of a #GstEncodingProfile The simplest
* serialized profile looks like:
* ## Serialized encoding profile formats
*
* ## Using encoders and muxer element factory name:
*
* |[
* muxer_source_caps:videoencoder_source_caps:audioencoder_source_caps
* muxer_factory_name:video_encoder_factory_name:audio_encoder_factory_name
* ]|
*
* For example to encode a stream into a WebM container, with an OGG audio
* stream and a VP8 video stream, the serialized #GstEncodingProfilewill look
* stream and a VP8 video stream, the serialized #GstEncodingProfile looks
* like:
*
* |[
* webmmux:vp8enc:vorbisenc
* ]|
*
* ## Define the encoding profile in a generic way using caps:
*
* |[
* muxer_source_caps:video_encoder_source_caps:audio_encoder_source_caps
* ]|
*
* For example to encode a stream into a WebM container, with an OGG audio
* stream and a VP8 video stream, the serialized #GstEncodingProfile looks
* like:
*
* |[
* video/webm:video/x-vp8:audio/x-vorbis
* ]|
*
* It is possible to mix caps and element type names so you can specify a specific
* video encoder while using caps for other encoders/muxer.
*
* ## Advanced encoding format serialization features:
*
* You can also set the preset name of the encoding profile using the
* caps+preset_name syntax as in:
*
@ -75,7 +95,7 @@
*
* 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.
* mandatory stream and can be used as many times as necessary.
*
* You can also use the `restriction_caps->encoded_format_caps` syntax to
* specify the restriction caps to be set on a #GstEncodingProfile
@ -92,7 +112,16 @@
* codec), you should use:
*
* |[
* video/webm:video/x-raw,width=1920,height=1080->video/x-vp8:audio/x-vorbis
* "video/webm:video/x-raw,width=1920,height=1080->video/x-vp8:audio/x-vorbis"
* ]|
*
* > NOTE: Make sure to enclose into quotes to avoid '>' to be reinterpreted by
* > the shell.
*
* In the case you are using encoder types, the following is also possible:
*
* |[
* "matroskamux:x264enc,width=1920,height=1080:audio/x-vorbis"
* ]|
*
* ## Some serialized encoding formats examples:
@ -115,6 +144,21 @@
* video/mpegts:video/x-h264:audio/x-ac3
* ]|
*
* ## Loading a profile from encoding targets
*
* Anywhere where you have to use a string to define a #GstEncodingProfile,
* you can use load it from a #GstEncodingTarget using the following syntaxes:
*
* |[
* target_name[/profilename/category]
* ]|
*
* or
*
* |[
* /path/to/target.gep:profilename
* ]|
*
* # Example: Creating a profile
*
* |[<!-- language="c" -->
@ -1415,47 +1459,120 @@ combo_search (const gchar * pname)
split = g_strsplit (pname, "/", 3);
split_length = g_strv_length (split);
if (split_length > 3)
return NULL;
goto done;
res = gst_encoding_profile_find (split[0],
split_length == 2 ? split[1] : NULL, split_length == 3 ? split[2] : NULL);
done:
g_strfreev (split);
return res;
}
static GstEncodingProfile *
parse_encoding_profile (const gchar * value)
static GstCaps *
get_profile_format_from_possible_factory_name (const gchar * factory_desc,
gchar ** new_factory_name, GstCaps ** restrictions)
{
GstEncodingProfile *res;
gchar **strpresence_v, **strcaps_v = g_strsplit (value, ":", 0);
guint i;
GList *tmp;
GstCaps *caps = NULL, *tmpcaps = gst_caps_from_string (factory_desc);
GstStructure *tmpstruct;
GstElementFactory *fact;
if (strcaps_v[0] && *strcaps_v[0]) {
GstCaps *caps = gst_caps_from_string (strcaps_v[0]);
if (caps == NULL) {
GST_ERROR ("Could not parse caps %s", strcaps_v[0]);
return NULL;
}
res =
GST_ENCODING_PROFILE (gst_encoding_container_profile_new
("User profile", "User profile", caps, NULL));
gst_caps_unref (caps);
} else {
res = NULL;
*new_factory_name = NULL;
if (gst_caps_get_size (tmpcaps) != 1)
goto done;
tmpstruct = gst_caps_get_structure (tmpcaps, 0);
fact = gst_element_factory_find (gst_structure_get_name (tmpstruct));
if (!fact)
goto done;
if (!gst_element_factory_list_is_type (fact,
GST_ELEMENT_FACTORY_TYPE_ENCODER | GST_ELEMENT_FACTORY_TYPE_MUXER)) {
GST_ERROR_OBJECT (fact,
"is not an encoder or muxer, it can't be"
" used in an encoding profile.");
goto done;
}
for (i = 1; strcaps_v[i] && *strcaps_v[i]; i++) {
for (tmp = (GList *) gst_element_factory_get_static_pad_templates (fact);
tmp; tmp = tmp->next) {
GstStaticPadTemplate *templ = ((GstStaticPadTemplate *) tmp->data);
if (templ->direction == GST_PAD_SRC) {
if (!caps)
caps = gst_static_caps_get (&templ->static_caps);
else
gst_caps_append (caps, gst_static_caps_get (&templ->static_caps));
}
}
if (caps) {
*new_factory_name = g_strdup (gst_structure_get_name (tmpstruct));
if (gst_structure_n_fields (tmpstruct) && restrictions) {
const gchar *sname =
gst_structure_get_name (gst_caps_get_structure (caps, 0));
if (g_str_has_prefix (sname, "audio/"))
gst_structure_set_name (tmpstruct, "audio/x-raw");
else if (g_str_has_prefix (sname, "video/") ||
g_str_has_prefix (sname, "image/"))
gst_structure_set_name (tmpstruct, "video/x-raw");
*restrictions = tmpcaps;
tmpcaps = NULL;
}
}
done:
if (fact)
gst_object_unref (fact);
if (tmpcaps)
gst_caps_unref (tmpcaps);
return caps;
}
static GstEncodingProfile *
create_encoding_profile_from_caps (GstCaps * caps, gchar * preset_name,
GstCaps * restrictioncaps, gint presence, gchar * factory_name)
{
GstEncodingProfile *profile = NULL;
gchar *strcaps, *strpresence;
gchar *preset_name = NULL;
GstCaps *caps;
gchar **restriction_format, **preset_v;
guint presence = 0;
GstCaps *restrictioncaps = NULL;
const gchar *sname =
gst_structure_get_name (gst_caps_get_structure (caps, 0));
restriction_format = g_strsplit (strcaps_v[i], "->", 0);
if (g_str_has_prefix (sname, "audio/"))
profile = GST_ENCODING_PROFILE (gst_encoding_audio_profile_new (caps,
preset_name, restrictioncaps, presence));
else if (g_str_has_prefix (sname, "video/") ||
g_str_has_prefix (sname, "image/"))
profile = GST_ENCODING_PROFILE (gst_encoding_video_profile_new (caps,
preset_name, restrictioncaps, presence));
if (factory_name && profile)
gst_encoding_profile_set_preset_name (profile, factory_name);
g_free (factory_name);
return profile;
}
static GstEncodingProfile *
create_encoding_stream_profile (gchar * serialized_profile)
{
GstCaps *caps;
guint presence = 0;
gchar *strcaps, *strpresence, **strpresence_v, **restriction_format,
**preset_v, *preset_name = NULL, *factory_name = NULL;
GstCaps *restrictioncaps = NULL;
GstEncodingProfile *profile = NULL;
restriction_format = g_strsplit (serialized_profile, "->", 0);
if (restriction_format[1]) {
restrictioncaps = gst_caps_from_string (restriction_format[0]);
strcaps = g_strdup (restriction_format[1]);
@ -1506,37 +1623,80 @@ parse_encoding_profile (const gchar * value)
preset_name ? preset_name : "none", presence);
caps = gst_caps_from_string (strcaps);
g_free (strcaps);
if (caps == NULL) {
g_warning ("Could not create caps for %s", strcaps_v[i]);
return NULL;
}
if (g_str_has_prefix (strcaps_v[i], "audio/")) {
profile = GST_ENCODING_PROFILE (gst_encoding_audio_profile_new (caps,
preset_name, restrictioncaps, presence));
} else if (g_str_has_prefix (strcaps_v[i], "video/") ||
g_str_has_prefix (strcaps_v[i], "image/")) {
profile = GST_ENCODING_PROFILE (gst_encoding_video_profile_new (caps,
preset_name, restrictioncaps, presence));
}
g_free (preset_name);
if (caps) {
profile = create_encoding_profile_from_caps (caps, preset_name,
restrictioncaps, presence, NULL);
gst_caps_unref (caps);
}
if (!profile) {
caps = get_profile_format_from_possible_factory_name (strcaps,
&factory_name, restrictioncaps ? NULL : &restrictioncaps);
if (caps) {
profile = create_encoding_profile_from_caps (caps, preset_name,
restrictioncaps, presence, factory_name);
gst_caps_unref (caps);
}
}
g_free (preset_name);
g_free (strcaps);
if (restrictioncaps)
gst_caps_unref (restrictioncaps);
if (profile == NULL) {
g_warning ("No way to create a preset for caps: %s", strcaps_v[i]);
GST_ERROR ("No way to create a profile for description: %s",
serialized_profile);
return NULL;
}
return profile;
}
static GstEncodingProfile *
parse_encoding_profile (const gchar * value)
{
gchar *factory_name;
GstEncodingProfile *res;
gchar **strcaps_v = g_strsplit (value, ":", 0);
guint i;
if (strcaps_v[0] && *strcaps_v[0]) {
GstCaps *caps = get_profile_format_from_possible_factory_name (strcaps_v[0],
&factory_name, NULL);
if (!caps)
caps = gst_caps_from_string (strcaps_v[0]);
if (caps == NULL) {
GST_ERROR ("Could not parse caps %s", strcaps_v[0]);
return NULL;
}
res =
GST_ENCODING_PROFILE (gst_encoding_container_profile_new
("User profile", "User profile", caps, NULL));
if (factory_name) {
gst_encoding_profile_set_preset_name (res, factory_name);
g_free (factory_name);
}
gst_caps_unref (caps);
} else {
res = NULL;
}
for (i = 1; strcaps_v[i] && *strcaps_v[i]; i++) {
GstEncodingProfile *profile = create_encoding_stream_profile (strcaps_v[i]);
if (!profile)
return NULL;
if (res) {
if (!gst_encoding_container_profile_add_profile
(GST_ENCODING_CONTAINER_PROFILE (res), profile)) {
g_warning ("Can not create a preset for caps: %s", strcaps_v[i]);
GST_ERROR ("Can not create a preset for caps: %s", strcaps_v[i]);
return NULL;
}