| /* GStreamer encoding profiles library |
| * Copyright (C) 2009-2010 Edward Hervey <edward.hervey@collabora.co.uk> |
| * (C) 2009-2010 Nokia Corporation |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| /** |
| * SECTION:encoding-profile |
| * @title: GstEncodingProfile |
| * @short_description: Encoding profile library |
| * |
| * 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 |
| * #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 |
| * format should be used on each individual streams. |
| * |
| * #GstEncodingProfile(s) can be provided to the 'encodebin' element, which |
| * will take care of selecting and setting up the required elements to produce |
| * an output stream conforming to the specifications of the profile. |
| * |
| * Unlike other systems, the encoding profiles do not specify which #GstElement |
| * to use for the various encoding and muxing steps, but instead relies on |
| * specifying the format one wishes to use. |
| * |
| * Encoding profiles can be created at runtime by the application or loaded |
| * from (and saved to) file using the #GstEncodingTarget API. |
| * |
| * # Defining a GstEncodingProfile as a string |
| * |
| * ## Serialized encoding profile formats |
| * |
| * ## Using encoders and muxer element factory name: |
| * |
| * |[ |
| * 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 #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: |
| * |
| * |[ |
| * video/webm:video/x-vp8+youtube-preset:audio/x-vorbis |
| * ]| |
| * |
| * Moreover, you can set the `presence` property of an |
| * encoding profile using the `|presence` syntax as in: |
| * |
| * |[ |
| * video/webm:video/x-vp8|1: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. |
| * |
| * You can also use the `restriction_caps->encoded_format_caps` syntax to |
| * specify the restriction caps to be set on a #GstEncodingProfile |
| * |
| * It corresponds to the restriction #GstCaps to apply before the encoder that |
| * will be used in the profile. The fields present in restriction caps are |
| * properties of the raw stream (that is, before encoding), such as height and |
| * width for video and depth and sampling rate for audio. This property does |
| * not make sense for muxers. See #gst_encoding_profile_get_restriction for |
| * more details. |
| * |
| * To force a video stream to be encoded with a Full HD resolution (using WebM |
| * as the container format, VP8 as the video codec and Vorbis as the audio |
| * codec), you should use: |
| * |
| * |[ |
| * "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: |
| * |
| * MP3 audio and H264 in MP4: |
| * |
| * |[ |
| * video/quicktime,variant=iso:video/x-h264:audio/mpeg,mpegversion=1,layer=3 |
| * ]| |
| * |
| * Vorbis and theora in OGG: |
| * |
| * |[ |
| * application/ogg:video/x-theora:audio/x-vorbis |
| * ]| |
| * |
| * AC3 and H264 in MPEG-TS: |
| * |
| * |[ |
| * 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" --> |
| * #include <gst/pbutils/encoding-profile.h> |
| * ... |
| * GstEncodingProfile * |
| * create_ogg_theora_profile(void) |
| *{ |
| * GstEncodingContainerProfile *prof; |
| * GstCaps *caps; |
| * |
| * caps = gst_caps_from_string("application/ogg"); |
| * prof = gst_encoding_container_profile_new("Ogg audio/video", |
| * "Standard OGG/THEORA/VORBIS", |
| * caps, NULL); |
| * gst_caps_unref (caps); |
| * |
| * caps = gst_caps_from_string("video/x-theora"); |
| * gst_encoding_container_profile_add_profile(prof, |
| * (GstEncodingProfile*) gst_encoding_video_profile_new(caps, NULL, NULL, 0)); |
| * gst_caps_unref (caps); |
| * |
| * caps = gst_caps_from_string("audio/x-vorbis"); |
| * gst_encoding_container_profile_add_profile(prof, |
| * (GstEncodingProfile*) gst_encoding_audio_profile_new(caps, NULL, NULL, 0)); |
| * gst_caps_unref (caps); |
| * |
| * return (GstEncodingProfile*) prof; |
| *} |
| * |
| * ]| |
| * |
| * # Example: Using an encoder preset with a profile |
| * |
| * |[ <!-- language="c" --> |
| * #include <gst/pbutils/encoding-profile.h> |
| * ... |
| * GstEncodingProfile * |
| * create_ogg_theora_profile(void) |
| *{ |
| * GstEncodingVideoProfile *v; |
| * GstEncodingAudioProfile *a; |
| * GstEncodingContainerProfile *prof; |
| * GstCaps *caps; |
| * GstPreset *preset; |
| * |
| * caps = gst_caps_from_string ("application/ogg"); |
| * prof = gst_encoding_container_profile_new ("Ogg audio/video", |
| * "Standard OGG/THEORA/VORBIS", |
| * caps, NULL); |
| * gst_caps_unref (caps); |
| * |
| * preset = GST_PRESET (gst_element_factory_make ("theoraenc", "theorapreset")); |
| * g_object_set (preset, "bitrate", 1000, NULL); |
| * // The preset will be saved on the filesystem, |
| * // so try to use a descriptive name |
| * gst_preset_save_preset (preset, "theora_bitrate_preset"); |
| * gst_object_unref (preset); |
| * |
| * caps = gst_caps_from_string ("video/x-theora"); |
| * v = gst_encoding_video_profile_new (caps, "theorapreset", NULL, 0); |
| * gst_encoding_container_profile_add_profile (prof, (GstEncodingProfile*) v); |
| * gst_caps_unref (caps); |
| * |
| * caps = gst_caps_from_string ("audio/x-vorbis"); |
| * a = gst_encoding_audio_profile_new (caps, NULL, NULL, 0); |
| * gst_encoding_container_profile_add_profile (prof, (GstEncodingProfile*) a); |
| * gst_caps_unref (caps); |
| * |
| * return (GstEncodingProfile*) prof; |
| *} |
| * |
| * ]| |
| * |
| * # Example: Listing categories, targets and profiles |
| * |
| * |[ <!-- language="C" --> |
| * #include <gst/pbutils/encoding-profile.h> |
| * ... |
| * GstEncodingProfile *prof; |
| * GList *categories, *tmpc; |
| * GList *targets, *tmpt; |
| * ... |
| * categories = gst_encoding_list_available_categories (); |
| * |
| * ... Show available categories to user ... |
| * |
| * for (tmpc = categories; tmpc; tmpc = tmpc->next) { |
| * gchar *category = (gchar *) tmpc->data; |
| * |
| * ... and we can list all targets within that category ... |
| * |
| * targets = gst_encoding_list_all_targets (category); |
| * |
| * ... and show a list to our users ... |
| * |
| * g_list_foreach (targets, (GFunc) gst_encoding_target_unref, NULL); |
| * g_list_free (targets); |
| * } |
| * |
| * g_list_foreach (categories, (GFunc) g_free, NULL); |
| * g_list_free (categories); |
| * |
| * ... |
| * ]| |
| * |
| * # Encoding Target |
| * |
| * On top of the notion of profiles, we implement the notion of EncodingTarget. |
| * Encoding Targets are basically a higher level of abstraction to define formats |
| * for specific target types. Those can define several GstEncodingProfiles with |
| * different names, for example one for transcoding in full HD, another one for |
| * low res, etc.. which are defined in the same encoding target. |
| * |
| * Basically if you wan to encode a stream to send it to, say, youtube you should |
| * have a Youtube encoding target defined in the "online-service" category. |
| * |
| * ## Encoding target serialization format |
| * |
| * Encoding targets are serialized in a KeyFile like files. |
| * |
| * |[ |
| * [GStreamer Encoding Target] |
| * name : <name> |
| * category : <category> |
| * \description : <description> #translatable |
| * |
| * [profile-<profile1name>] |
| * name : <name> |
| * \description : <description> #optional |
| * format : <format> |
| * preset : <preset> |
| * |
| * [streamprofile-<id>] |
| * parent : <encodingprofile.name>[,<encodingprofile.name>..] |
| * \type : <type> # "audio", "video", "text" |
| * format : <format> |
| * preset : <preset> |
| * restriction : <restriction> |
| * presence : <presence> |
| * pass : <pass> |
| * variableframerate : <variableframerate> |
| * ]| |
| * |
| * # Location of encoding target files |
| * |
| * $GST_DATADIR/gstreamer-GST_API_VERSION/encoding-profile |
| * $HOME/gstreamer-GST_API_VERSION/encoding-profile |
| * |
| * There also is a GST_ENCODING_TARGET_PATH environment variable |
| * defining a list of folder containing encoding target files. |
| * |
| * ## Naming convention |
| * |
| * |[ |
| * $(target.category)/$(target.name).gep |
| * ]| |
| * |
| * # Naming restrictions: |
| * |
| * * lowercase ASCII letter for the first character |
| * * Same for all other characters + numerics + hyphens |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| # include "config.h" |
| #endif |
| |
| #include "encoding-profile.h" |
| #include "encoding-target.h" |
| |
| #include <string.h> |
| |
| /* GstEncodingProfile API */ |
| |
| struct _GstEncodingProfile |
| { |
| GObject parent; |
| |
| /*< public > */ |
| gchar *name; |
| gchar *description; |
| GstCaps *format; |
| gchar *preset; |
| gchar *preset_name; |
| guint presence; |
| GstCaps *restriction; |
| gboolean allow_dynamic_output; |
| gboolean enabled; |
| }; |
| |
| struct _GstEncodingProfileClass |
| { |
| GObjectClass parent_class; |
| |
| void (*copy) (GstEncodingProfile * self, GstEncodingProfile * copy); |
| }; |
| |
| enum |
| { |
| FIRST_PROPERTY, |
| PROP_RESTRICTION_CAPS, |
| LAST_PROPERTY |
| }; |
| |
| static GParamSpec *_properties[LAST_PROPERTY]; |
| |
| static void string_to_profile_transform (const GValue * src_value, |
| GValue * dest_value); |
| static gboolean gst_encoding_profile_deserialize_valfunc (GValue * value, |
| const gchar * s); |
| |
| static void gst_encoding_profile_class_init (GstEncodingProfileClass * klass); |
| static gpointer gst_encoding_profile_parent_class = NULL; |
| |
| static void |
| gst_encoding_profile_class_intern_init (gpointer klass) |
| { |
| gst_encoding_profile_parent_class = g_type_class_peek_parent (klass); |
| gst_encoding_profile_class_init ((GstEncodingProfileClass *) klass); |
| } |
| |
| GType |
| gst_encoding_profile_get_type (void) |
| { |
| static volatile gsize g_define_type_id__volatile = 0; |
| |
| if (g_once_init_enter (&g_define_type_id__volatile)) { |
| GType g_define_type_id = g_type_register_static_simple (G_TYPE_OBJECT, |
| g_intern_static_string ("GstEncodingProfile"), |
| sizeof (GstEncodingProfileClass), |
| (GClassInitFunc) gst_encoding_profile_class_intern_init, |
| sizeof (GstEncodingProfile), |
| NULL, |
| (GTypeFlags) 0); |
| static GstValueTable gstvtable = { |
| G_TYPE_NONE, |
| (GstValueCompareFunc) NULL, |
| (GstValueSerializeFunc) NULL, |
| (GstValueDeserializeFunc) gst_encoding_profile_deserialize_valfunc |
| }; |
| |
| gstvtable.type = g_define_type_id; |
| |
| /* Register a STRING=>PROFILE GValueTransformFunc */ |
| g_value_register_transform_func (G_TYPE_STRING, g_define_type_id, |
| string_to_profile_transform); |
| /* Register gst-specific GValue functions */ |
| gst_value_register (&gstvtable); |
| |
| g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); |
| } |
| return g_define_type_id__volatile; |
| } |
| |
| |
| static void |
| _encoding_profile_get_property (GObject * object, guint prop_id, |
| GValue * value, GParamSpec * pspec) |
| { |
| GstEncodingProfile *prof = (GstEncodingProfile *) object; |
| |
| switch (prop_id) { |
| case PROP_RESTRICTION_CAPS: |
| gst_value_set_caps (value, prof->restriction); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| static void |
| _encoding_profile_set_property (GObject * object, guint prop_id, |
| const GValue * value, GParamSpec * pspec) |
| { |
| GstEncodingProfile *prof = (GstEncodingProfile *) object; |
| |
| switch (prop_id) { |
| case PROP_RESTRICTION_CAPS: |
| gst_encoding_profile_set_restriction (prof, gst_caps_copy |
| (gst_value_get_caps (value))); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| static void |
| gst_encoding_profile_finalize (GObject * object) |
| { |
| GstEncodingProfile *prof = (GstEncodingProfile *) object; |
| g_free (prof->name); |
| if (prof->format) |
| gst_caps_unref (prof->format); |
| g_free (prof->preset); |
| g_free (prof->description); |
| if (prof->restriction) |
| gst_caps_unref (prof->restriction); |
| g_free (prof->preset_name); |
| } |
| |
| static void |
| gst_encoding_profile_class_init (GstEncodingProfileClass * klass) |
| { |
| GObjectClass *gobject_class = (GObjectClass *) klass; |
| |
| gobject_class->finalize = gst_encoding_profile_finalize; |
| |
| gobject_class->set_property = _encoding_profile_set_property; |
| gobject_class->get_property = _encoding_profile_get_property; |
| |
| _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]); |
| |
| } |
| |
| /** |
| * gst_encoding_profile_get_name: |
| * @profile: a #GstEncodingProfile |
| * |
| * Returns: the name of the profile, can be %NULL. |
| */ |
| const gchar * |
| gst_encoding_profile_get_name (GstEncodingProfile * profile) |
| { |
| g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), NULL); |
| |
| return profile->name; |
| } |
| |
| /** |
| * gst_encoding_profile_get_description: |
| * @profile: a #GstEncodingProfile |
| * |
| * Returns: the description of the profile, can be %NULL. |
| */ |
| const gchar * |
| gst_encoding_profile_get_description (GstEncodingProfile * profile) |
| { |
| g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), NULL); |
| |
| return profile->description; |
| } |
| |
| /** |
| * gst_encoding_profile_get_format: |
| * @profile: a #GstEncodingProfile |
| * |
| * Returns: (transfer full): the #GstCaps corresponding to the media format used |
| * in the profile. Unref after usage. |
| */ |
| GstCaps * |
| gst_encoding_profile_get_format (GstEncodingProfile * profile) |
| { |
| g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), NULL); |
| |
| return (profile->format ? gst_caps_ref (profile->format) : NULL); |
| } |
| |
| /** |
| * gst_encoding_profile_get_preset: |
| * @profile: a #GstEncodingProfile |
| * |
| * Returns: the name of the #GstPreset to be used in the profile. |
| * This is the name that has been set when saving the preset. |
| */ |
| const gchar * |
| gst_encoding_profile_get_preset (GstEncodingProfile * profile) |
| { |
| g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), NULL); |
| |
| return profile->preset; |
| } |
| |
| /** |
| * gst_encoding_profile_get_preset_name: |
| * @profile: a #GstEncodingProfile |
| * |
| * Returns: the name of the #GstPreset factory to be used in the profile. |
| */ |
| const gchar * |
| gst_encoding_profile_get_preset_name (GstEncodingProfile * profile) |
| { |
| g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), NULL); |
| |
| return profile->preset_name; |
| } |
| |
| /** |
| * gst_encoding_profile_get_presence: |
| * @profile: a #GstEncodingProfile |
| * |
| * Returns: The number of times the profile is used in its parent |
| * container profile. If 0, it is not a mandatory stream. |
| */ |
| guint |
| gst_encoding_profile_get_presence (GstEncodingProfile * profile) |
| { |
| g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), 0); |
| |
| return profile->presence; |
| } |
| |
| /** |
| * gst_encoding_profile_get_enabled: |
| * @profile: a #GstEncodingProfile |
| * |
| * Returns: Whther @profile is enabled or not |
| * |
| * Since 1.6 |
| */ |
| gboolean |
| gst_encoding_profile_is_enabled (GstEncodingProfile * profile) |
| { |
| g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE); |
| |
| return profile->enabled; |
| } |
| |
| /** |
| * gst_encoding_profile_get_restriction: |
| * @profile: a #GstEncodingProfile |
| * |
| * Returns: (transfer full): The restriction #GstCaps to apply before the encoder |
| * that will be used in the profile. The fields present in restriction caps are |
| * properties of the raw stream (that is before encoding), such as height and |
| * width for video and depth and sampling rate for audio. Does not apply to |
| * #GstEncodingContainerProfile (since there is no corresponding raw stream). |
| * Can be %NULL. Unref after usage. |
| */ |
| GstCaps * |
| gst_encoding_profile_get_restriction (GstEncodingProfile * profile) |
| { |
| g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), NULL); |
| |
| |
| return (profile->restriction ? gst_caps_ref (profile->restriction) : NULL); |
| } |
| |
| /** |
| * gst_encoding_profile_set_name: |
| * @profile: a #GstEncodingProfile |
| * @name: (allow-none): the name to set on the profile |
| * |
| * Set @name as the given name for the @profile. A copy of @name will be made |
| * internally. |
| */ |
| void |
| gst_encoding_profile_set_name (GstEncodingProfile * profile, const gchar * name) |
| { |
| g_return_if_fail (GST_IS_ENCODING_PROFILE (profile)); |
| |
| g_free (profile->name); |
| profile->name = g_strdup (name); |
| } |
| |
| /** |
| * gst_encoding_profile_set_description: |
| * @profile: a #GstEncodingProfile |
| * @description: (allow-none): the description to set on the profile |
| * |
| * Set @description as the given description for the @profile. A copy of |
| * @description will be made internally. |
| */ |
| void |
| gst_encoding_profile_set_description (GstEncodingProfile * profile, |
| const gchar * description) |
| { |
| g_return_if_fail (GST_IS_ENCODING_PROFILE (profile)); |
| |
| g_free (profile->description); |
| profile->description = g_strdup (description); |
| } |
| |
| /** |
| * gst_encoding_profile_set_format: |
| * @profile: a #GstEncodingProfile |
| * @format: (transfer none): the media format to use in the profile. |
| * |
| * Sets the media format used in the profile. |
| */ |
| void |
| gst_encoding_profile_set_format (GstEncodingProfile * profile, GstCaps * format) |
| { |
| g_return_if_fail (GST_IS_ENCODING_PROFILE (profile)); |
| |
| if (profile->format) |
| gst_caps_unref (profile->format); |
| profile->format = gst_caps_ref (format); |
| } |
| |
| /** |
| * gst_encoding_profile_get_allow_dynamic_output: |
| * @profile: a #GstEncodingProfile |
| * |
| * Get whether the format that has been negotiated in at some point can be renegotiated |
| * later during the encoding. |
| */ |
| gboolean |
| gst_encoding_profile_get_allow_dynamic_output (GstEncodingProfile * profile) |
| { |
| g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE); |
| |
| return profile->allow_dynamic_output; |
| } |
| |
| /** |
| * gst_encoding_profile_set_allow_dynamic_output: |
| * @profile: a #GstEncodingProfile |
| * @allow_dynamic_output: Whether the format that has been negotiated first can be renegotiated |
| * during the encoding |
| * |
| * Sets whether the format that has been negotiated in at some point can be renegotiated |
| * later during the encoding. |
| */ |
| void |
| gst_encoding_profile_set_allow_dynamic_output (GstEncodingProfile * profile, |
| gboolean allow_dynamic_output) |
| { |
| g_return_if_fail (GST_IS_ENCODING_PROFILE (profile)); |
| |
| profile->allow_dynamic_output = allow_dynamic_output; |
| } |
| |
| /** |
| * gst_encoding_profile_set_preset: |
| * @profile: a #GstEncodingProfile |
| * @preset: (nullable): the element preset to use |
| * |
| * Sets the name of the #GstElement that implements the #GstPreset interface |
| * to use for the profile. |
| * This is the name that has been set when saving the preset. |
| */ |
| void |
| gst_encoding_profile_set_preset (GstEncodingProfile * profile, |
| const gchar * preset) |
| { |
| g_return_if_fail (GST_IS_ENCODING_PROFILE (profile)); |
| |
| g_free (profile->preset); |
| profile->preset = g_strdup (preset); |
| } |
| |
| /** |
| * gst_encoding_profile_set_preset_name: |
| * @profile: a #GstEncodingProfile |
| * @preset_name: (allow-none): The name of the preset to use in this @profile. |
| * |
| * Sets the name of the #GstPreset's factory to be used in the profile. |
| */ |
| void |
| gst_encoding_profile_set_preset_name (GstEncodingProfile * profile, |
| const gchar * preset_name) |
| { |
| g_return_if_fail (GST_IS_ENCODING_PROFILE (profile)); |
| |
| g_free (profile->preset_name); |
| profile->preset_name = g_strdup (preset_name); |
| } |
| |
| /** |
| * gst_encoding_profile_set_presence: |
| * @profile: a #GstEncodingProfile |
| * @presence: the number of time the profile can be used |
| * |
| * Set the number of time the profile is used in its parent |
| * container profile. If 0, it is not a mandatory stream |
| */ |
| void |
| gst_encoding_profile_set_presence (GstEncodingProfile * profile, guint presence) |
| { |
| g_return_if_fail (GST_IS_ENCODING_PROFILE (profile)); |
| |
| profile->presence = presence; |
| } |
| |
| /** |
| * gst_encoding_profile_set_enabled: |
| * @profile: a #GstEncodingProfile |
| * @enabled: %FALSE to disable #profile, %TRUE to enable it |
| * |
| * Set whether the profile should be used or not. |
| * |
| * Since 1.6 |
| */ |
| void |
| gst_encoding_profile_set_enabled (GstEncodingProfile * profile, |
| gboolean enabled) |
| { |
| g_return_if_fail (GST_IS_ENCODING_PROFILE (profile)); |
| |
| profile->enabled = enabled; |
| } |
| |
| /** |
| * gst_encoding_profile_set_restriction: |
| * @profile: a #GstEncodingProfile |
| * @restriction: (allow-none) (transfer full): the restriction to apply |
| * |
| * Set the restriction #GstCaps to apply before the encoder |
| * that will be used in the profile. See gst_encoding_profile_get_restriction() |
| * for more about restrictions. Does not apply to #GstEncodingContainerProfile. |
| */ |
| void |
| gst_encoding_profile_set_restriction (GstEncodingProfile * profile, |
| GstCaps * restriction) |
| { |
| g_return_if_fail (GST_IS_CAPS (restriction)); |
| g_return_if_fail (GST_IS_ENCODING_PROFILE (profile)); |
| |
| if (profile->restriction) |
| gst_caps_unref (profile->restriction); |
| profile->restriction = restriction; |
| |
| g_object_notify_by_pspec (G_OBJECT (profile), |
| _properties[PROP_RESTRICTION_CAPS]); |
| } |
| |
| /* Container profiles */ |
| |
| struct _GstEncodingContainerProfile |
| { |
| GstEncodingProfile parent; |
| |
| GList *encodingprofiles; |
| }; |
| |
| struct _GstEncodingContainerProfileClass |
| { |
| GstEncodingProfileClass parent; |
| }; |
| |
| G_DEFINE_TYPE (GstEncodingContainerProfile, gst_encoding_container_profile, |
| GST_TYPE_ENCODING_PROFILE); |
| |
| static void |
| gst_encoding_container_profile_init (GstEncodingContainerProfile * prof) |
| { |
| /* Nothing to initialize */ |
| } |
| |
| static void |
| gst_encoding_container_profile_finalize (GObject * object) |
| { |
| GstEncodingContainerProfile *prof = (GstEncodingContainerProfile *) object; |
| |
| g_list_foreach (prof->encodingprofiles, (GFunc) g_object_unref, NULL); |
| g_list_free (prof->encodingprofiles); |
| |
| G_OBJECT_CLASS (gst_encoding_container_profile_parent_class)->finalize |
| ((GObject *) prof); |
| } |
| |
| static void |
| gst_encoding_container_profile_copy (GstEncodingProfile * profile, |
| GstEncodingProfile * copy_profile) |
| { |
| GstEncodingContainerProfile *self = GST_ENCODING_CONTAINER_PROFILE (profile), |
| *copy = GST_ENCODING_CONTAINER_PROFILE (copy_profile); |
| GList *tmp; |
| |
| for (tmp = self->encodingprofiles; tmp; tmp = tmp->next) { |
| gst_encoding_container_profile_add_profile (copy, |
| gst_encoding_profile_copy (tmp->data)); |
| } |
| } |
| |
| static void |
| gst_encoding_container_profile_class_init (GstEncodingContainerProfileClass * k) |
| { |
| GObjectClass *gobject_class = (GObjectClass *) k; |
| |
| gobject_class->finalize = gst_encoding_container_profile_finalize; |
| |
| ((GstEncodingProfileClass *) k)->copy = gst_encoding_container_profile_copy; |
| } |
| |
| /** |
| * gst_encoding_container_profile_get_profiles: |
| * @profile: a #GstEncodingContainerProfile |
| * |
| * Returns: (element-type GstPbutils.EncodingProfile) (transfer none): |
| * the list of contained #GstEncodingProfile. |
| */ |
| const GList * |
| gst_encoding_container_profile_get_profiles (GstEncodingContainerProfile * |
| profile) |
| { |
| g_return_val_if_fail (GST_IS_ENCODING_CONTAINER_PROFILE (profile), NULL); |
| |
| return profile->encodingprofiles; |
| } |
| |
| /* Video profiles */ |
| |
| struct _GstEncodingVideoProfile |
| { |
| GstEncodingProfile parent; |
| |
| guint pass; |
| gboolean variableframerate; |
| }; |
| |
| struct _GstEncodingVideoProfileClass |
| { |
| GstEncodingProfileClass parent; |
| }; |
| |
| G_DEFINE_TYPE (GstEncodingVideoProfile, gst_encoding_video_profile, |
| GST_TYPE_ENCODING_PROFILE); |
| |
| static void |
| gst_encoding_video_profile_copy (GstEncodingProfile * profile, |
| GstEncodingProfile * copy_profile) |
| { |
| GstEncodingVideoProfile *self = GST_ENCODING_VIDEO_PROFILE (profile), |
| *copy = GST_ENCODING_VIDEO_PROFILE (copy_profile); |
| |
| copy->pass = self->pass; |
| copy->variableframerate = self->variableframerate; |
| } |
| |
| static void |
| gst_encoding_video_profile_init (GstEncodingVideoProfile * prof) |
| { |
| /* Nothing to initialize */ |
| } |
| |
| static void |
| gst_encoding_video_profile_class_init (GstEncodingVideoProfileClass * klass) |
| { |
| ((GstEncodingProfileClass *) klass)->copy = gst_encoding_video_profile_copy; |
| } |
| |
| /** |
| * gst_encoding_video_profile_get_pass: |
| * @prof: a #GstEncodingVideoProfile |
| * |
| * Get the pass number if this is part of a multi-pass profile. |
| * |
| * Returns: The pass number. Starts at 1 for multi-pass. 0 if this is |
| * not a multi-pass profile |
| */ |
| guint |
| gst_encoding_video_profile_get_pass (GstEncodingVideoProfile * prof) |
| { |
| g_return_val_if_fail (GST_IS_ENCODING_VIDEO_PROFILE (prof), 0); |
| |
| return prof->pass; |
| } |
| |
| /** |
| * gst_encoding_video_profile_get_variableframerate: |
| * @prof: a #GstEncodingVideoProfile |
| * |
| * Returns: Whether non-constant video framerate is allowed for encoding. |
| */ |
| gboolean |
| gst_encoding_video_profile_get_variableframerate (GstEncodingVideoProfile * |
| prof) |
| { |
| g_return_val_if_fail (GST_IS_ENCODING_VIDEO_PROFILE (prof), FALSE); |
| |
| return prof->variableframerate; |
| } |
| |
| /** |
| * gst_encoding_video_profile_set_pass: |
| * @prof: a #GstEncodingVideoProfile |
| * @pass: the pass number for this profile |
| * |
| * Sets the pass number of this video profile. The first pass profile should have |
| * this value set to 1. If this video profile isn't part of a multi-pass profile, |
| * you may set it to 0 (the default value). |
| */ |
| void |
| gst_encoding_video_profile_set_pass (GstEncodingVideoProfile * prof, guint pass) |
| { |
| g_return_if_fail (GST_IS_ENCODING_VIDEO_PROFILE (prof)); |
| |
| prof->pass = pass; |
| } |
| |
| /** |
| * gst_encoding_video_profile_set_variableframerate: |
| * @prof: a #GstEncodingVideoProfile |
| * @variableframerate: a boolean |
| * |
| * If set to %TRUE, then the incoming stream will be allowed to have non-constant |
| * framerate. If set to %FALSE (default value), then the incoming stream will |
| * be normalized by dropping/duplicating frames in order to produce a |
| * constance framerate. |
| */ |
| void |
| gst_encoding_video_profile_set_variableframerate (GstEncodingVideoProfile * |
| prof, gboolean variableframerate) |
| { |
| g_return_if_fail (GST_IS_ENCODING_VIDEO_PROFILE (prof)); |
| |
| prof->variableframerate = variableframerate; |
| } |
| |
| /* Audio profiles */ |
| |
| struct _GstEncodingAudioProfile |
| { |
| GstEncodingProfile parent; |
| }; |
| |
| struct _GstEncodingAudioProfileClass |
| { |
| GstEncodingProfileClass parent; |
| }; |
| |
| G_DEFINE_TYPE (GstEncodingAudioProfile, gst_encoding_audio_profile, |
| GST_TYPE_ENCODING_PROFILE); |
| |
| static void |
| gst_encoding_audio_profile_init (GstEncodingAudioProfile * prof) |
| { |
| /* Nothing to initialize */ |
| } |
| |
| static void |
| gst_encoding_audio_profile_class_init (GstEncodingAudioProfileClass * klass) |
| { |
| } |
| |
| static inline gboolean |
| _gst_caps_is_equal_safe (GstCaps * a, GstCaps * b) |
| { |
| if (a == b) |
| return TRUE; |
| if ((a == NULL) || (b == NULL)) |
| return FALSE; |
| return gst_caps_is_equal (a, b); |
| } |
| |
| static gint |
| _compare_container_encoding_profiles (GstEncodingContainerProfile * ca, |
| GstEncodingContainerProfile * cb) |
| { |
| GList *tmp; |
| |
| if (g_list_length (ca->encodingprofiles) != |
| g_list_length (cb->encodingprofiles)) |
| return -1; |
| |
| for (tmp = ca->encodingprofiles; tmp; tmp = tmp->next) { |
| GstEncodingProfile *prof = (GstEncodingProfile *) tmp->data; |
| if (!gst_encoding_container_profile_contains_profile (ca, prof)) |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static gint |
| _compare_encoding_profiles (const GstEncodingProfile * a, |
| const GstEncodingProfile * b) |
| { |
| if ((G_TYPE_FROM_INSTANCE (a) != G_TYPE_FROM_INSTANCE (b)) || |
| !_gst_caps_is_equal_safe (a->format, b->format) || |
| (g_strcmp0 (a->preset, b->preset) != 0) || |
| (g_strcmp0 (a->preset_name, b->preset_name) != 0) || |
| (g_strcmp0 (a->name, b->name) != 0) || |
| (g_strcmp0 (a->description, b->description) != 0)) |
| return -1; |
| |
| if (GST_IS_ENCODING_CONTAINER_PROFILE (a)) |
| return |
| _compare_container_encoding_profiles (GST_ENCODING_CONTAINER_PROFILE |
| (a), GST_ENCODING_CONTAINER_PROFILE (b)); |
| |
| if (GST_IS_ENCODING_VIDEO_PROFILE (a)) { |
| GstEncodingVideoProfile *va = (GstEncodingVideoProfile *) a; |
| GstEncodingVideoProfile *vb = (GstEncodingVideoProfile *) b; |
| |
| if ((va->pass != vb->pass) |
| || (va->variableframerate != vb->variableframerate)) |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * gst_encoding_container_profile_contains_profile: |
| * @container: a #GstEncodingContainerProfile |
| * @profile: a #GstEncodingProfile |
| * |
| * Checks if @container contains a #GstEncodingProfile identical to |
| * @profile. |
| * |
| * Returns: %TRUE if @container contains a #GstEncodingProfile identical |
| * to @profile, else %FALSE. |
| */ |
| gboolean |
| gst_encoding_container_profile_contains_profile (GstEncodingContainerProfile * |
| container, GstEncodingProfile * profile) |
| { |
| g_return_val_if_fail (GST_IS_ENCODING_CONTAINER_PROFILE (container), FALSE); |
| g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE); |
| |
| return (g_list_find_custom (container->encodingprofiles, profile, |
| (GCompareFunc) _compare_encoding_profiles) != NULL); |
| } |
| |
| /** |
| * gst_encoding_container_profile_add_profile: |
| * @container: the #GstEncodingContainerProfile to use |
| * @profile: (transfer full): the #GstEncodingProfile to add. |
| * |
| * Add a #GstEncodingProfile to the list of profiles handled by @container. |
| * |
| * No copy of @profile will be made, if you wish to use it elsewhere after this |
| * method you should increment its reference count. |
| * |
| * Returns: %TRUE if the @stream was properly added, else %FALSE. |
| */ |
| gboolean |
| gst_encoding_container_profile_add_profile (GstEncodingContainerProfile * |
| container, GstEncodingProfile * profile) |
| { |
| g_return_val_if_fail (GST_IS_ENCODING_CONTAINER_PROFILE (container), FALSE); |
| g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE); |
| |
| if (g_list_find_custom (container->encodingprofiles, profile, |
| (GCompareFunc) _compare_encoding_profiles)) { |
| GST_ERROR |
| ("Encoding profile already contains an identical GstEncodingProfile"); |
| return FALSE; |
| } |
| |
| container->encodingprofiles = |
| g_list_append (container->encodingprofiles, profile); |
| |
| return TRUE; |
| } |
| |
| static GstEncodingProfile * |
| common_creation (GType objtype, GstCaps * format, const gchar * preset, |
| const gchar * name, const gchar * description, GstCaps * restriction, |
| guint presence) |
| { |
| GstEncodingProfile *prof; |
| |
| prof = (GstEncodingProfile *) g_object_new (objtype, NULL); |
| |
| if (name) |
| prof->name = g_strdup (name); |
| if (description) |
| prof->description = g_strdup (description); |
| if (preset) |
| prof->preset = g_strdup (preset); |
| if (format) |
| prof->format = gst_caps_ref (format); |
| if (restriction) |
| prof->restriction = gst_caps_ref (restriction); |
| prof->presence = presence; |
| prof->preset_name = NULL; |
| prof->allow_dynamic_output = TRUE; |
| prof->enabled = TRUE; |
| |
| return prof; |
| } |
| |
| /** |
| * gst_encoding_container_profile_new: |
| * @name: (allow-none): The name of the container profile, can be %NULL |
| * @description: (allow-none): The description of the container profile, |
| * can be %NULL |
| * @format: (transfer none): The format to use for this profile |
| * @preset: (allow-none): The preset to use for this profile. |
| * |
| * Creates a new #GstEncodingContainerProfile. |
| * |
| * Returns: The newly created #GstEncodingContainerProfile. |
| */ |
| GstEncodingContainerProfile * |
| gst_encoding_container_profile_new (const gchar * name, |
| const gchar * description, GstCaps * format, const gchar * preset) |
| { |
| g_return_val_if_fail (GST_IS_CAPS (format), NULL); |
| |
| return (GstEncodingContainerProfile *) |
| common_creation (GST_TYPE_ENCODING_CONTAINER_PROFILE, format, preset, |
| name, description, NULL, 0); |
| } |
| |
| /** |
| * gst_encoding_video_profile_new: |
| * @format: (transfer none): the #GstCaps |
| * @preset: (allow-none): the preset(s) to use on the encoder, can be %NULL |
| * @restriction: (allow-none): the #GstCaps used to restrict the input to the encoder, can be |
| * NULL. See gst_encoding_profile_get_restriction() for more details. |
| * @presence: the number of time this stream must be used. 0 means any number of |
| * times (including never) |
| * |
| * Creates a new #GstEncodingVideoProfile |
| * |
| * All provided allocatable arguments will be internally copied, so can be |
| * safely freed/unreferenced after calling this method. |
| * |
| * If you wish to control the pass number (in case of multi-pass scenarios), |
| * please refer to the gst_encoding_video_profile_set_pass() documentation. |
| * |
| * If you wish to use/force a constant framerate please refer to the |
| * gst_encoding_video_profile_set_variableframerate() documentation. |
| * |
| * Returns: the newly created #GstEncodingVideoProfile. |
| */ |
| GstEncodingVideoProfile * |
| gst_encoding_video_profile_new (GstCaps * format, const gchar * preset, |
| GstCaps * restriction, guint presence) |
| { |
| return (GstEncodingVideoProfile *) |
| common_creation (GST_TYPE_ENCODING_VIDEO_PROFILE, format, preset, NULL, |
| NULL, restriction, presence); |
| } |
| |
| /** |
| * gst_encoding_audio_profile_new: |
| * @format: (transfer none): the #GstCaps |
| * @preset: (allow-none): the preset(s) to use on the encoder, can be %NULL |
| * @restriction: (allow-none): the #GstCaps used to restrict the input to the encoder, can be |
| * NULL. See gst_encoding_profile_get_restriction() for more details. |
| * @presence: the number of time this stream must be used. 0 means any number of |
| * times (including never) |
| * |
| * Creates a new #GstEncodingAudioProfile |
| * |
| * All provided allocatable arguments will be internally copied, so can be |
| * safely freed/unreferenced after calling this method. |
| * |
| * Returns: the newly created #GstEncodingAudioProfile. |
| */ |
| GstEncodingAudioProfile * |
| gst_encoding_audio_profile_new (GstCaps * format, const gchar * preset, |
| GstCaps * restriction, guint presence) |
| { |
| return (GstEncodingAudioProfile *) |
| common_creation (GST_TYPE_ENCODING_AUDIO_PROFILE, format, preset, NULL, |
| NULL, restriction, presence); |
| } |
| |
| |
| /** |
| * gst_encoding_profile_is_equal: |
| * @a: a #GstEncodingProfile |
| * @b: a #GstEncodingProfile |
| * |
| * Checks whether the two #GstEncodingProfile are equal |
| * |
| * Returns: %TRUE if @a and @b are equal, else %FALSE. |
| */ |
| gboolean |
| gst_encoding_profile_is_equal (GstEncodingProfile * a, GstEncodingProfile * b) |
| { |
| g_return_val_if_fail (GST_IS_ENCODING_PROFILE (a), FALSE); |
| g_return_val_if_fail (GST_IS_ENCODING_PROFILE (b), FALSE); |
| |
| return (_compare_encoding_profiles (a, b) == 0); |
| } |
| |
| |
| /** |
| * gst_encoding_profile_get_input_caps: |
| * @profile: a #GstEncodingProfile |
| * |
| * Computes the full output caps that this @profile will be able to consume. |
| * |
| * Returns: (transfer full): The full caps the given @profile can consume. Call |
| * gst_caps_unref() when you are done with the caps. |
| */ |
| GstCaps * |
| gst_encoding_profile_get_input_caps (GstEncodingProfile * profile) |
| { |
| GstCaps *out, *tmp; |
| GList *ltmp; |
| GstStructure *st, *outst; |
| GQuark out_name; |
| guint i, len; |
| GstCaps *fcaps; |
| |
| g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), NULL); |
| |
| if (GST_IS_ENCODING_CONTAINER_PROFILE (profile)) { |
| GstCaps *res = gst_caps_new_empty (); |
| |
| for (ltmp = GST_ENCODING_CONTAINER_PROFILE (profile)->encodingprofiles; |
| ltmp; ltmp = ltmp->next) { |
| GstEncodingProfile *sprof = (GstEncodingProfile *) ltmp->data; |
| res = gst_caps_merge (res, gst_encoding_profile_get_input_caps (sprof)); |
| } |
| return res; |
| } |
| |
| fcaps = profile->format; |
| |
| /* fast-path */ |
| if ((profile->restriction == NULL) || gst_caps_is_any (profile->restriction)) |
| return gst_caps_ref (fcaps); |
| |
| /* Combine the format with the restriction caps */ |
| outst = gst_caps_get_structure (fcaps, 0); |
| out_name = gst_structure_get_name_id (outst); |
| tmp = gst_caps_new_empty (); |
| len = gst_caps_get_size (profile->restriction); |
| |
| for (i = 0; i < len; i++) { |
| st = gst_structure_copy (gst_caps_get_structure (profile->restriction, i)); |
| st->name = out_name; |
| gst_caps_append_structure (tmp, st); |
| } |
| |
| out = gst_caps_intersect (tmp, fcaps); |
| gst_caps_unref (tmp); |
| |
| return out; |
| } |
| |
| /** |
| * gst_encoding_profile_get_type_nick: |
| * @profile: a #GstEncodingProfile |
| * |
| * Returns: the human-readable name of the type of @profile. |
| */ |
| const gchar * |
| gst_encoding_profile_get_type_nick (GstEncodingProfile * profile) |
| { |
| if (GST_IS_ENCODING_CONTAINER_PROFILE (profile)) |
| return "container"; |
| if (GST_IS_ENCODING_VIDEO_PROFILE (profile)) |
| return "video"; |
| if (GST_IS_ENCODING_AUDIO_PROFILE (profile)) |
| return "audio"; |
| return NULL; |
| } |
| |
| extern const gchar *pb_utils_get_file_extension_from_caps (const GstCaps * |
| caps); |
| gboolean pb_utils_is_tag (const GstCaps * caps); |
| |
| static gboolean |
| gst_encoding_profile_has_format (GstEncodingProfile * profile, |
| const gchar * media_type) |
| { |
| GstCaps *caps; |
| gboolean ret; |
| |
| g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE); |
| |
| caps = gst_encoding_profile_get_format (profile); |
| ret = gst_structure_has_name (gst_caps_get_structure (caps, 0), media_type); |
| gst_caps_unref (caps); |
| |
| return ret; |
| } |
| |
| static gboolean |
| gst_encoding_container_profile_has_video (GstEncodingContainerProfile * profile) |
| { |
| const GList *l; |
| |
| g_return_val_if_fail (GST_IS_ENCODING_CONTAINER_PROFILE (profile), FALSE); |
| |
| for (l = profile->encodingprofiles; l != NULL; l = l->next) { |
| if (GST_IS_ENCODING_VIDEO_PROFILE (l->data)) |
| return TRUE; |
| if (GST_IS_ENCODING_CONTAINER_PROFILE (l->data) && |
| gst_encoding_container_profile_has_video (l->data)) |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| /** |
| * gst_encoding_profile_get_file_extension: |
| * @profile: a #GstEncodingProfile |
| * |
| * Returns: a suitable file extension for @profile, or NULL. |
| */ |
| const gchar * |
| gst_encoding_profile_get_file_extension (GstEncodingProfile * profile) |
| { |
| GstEncodingContainerProfile *cprofile; |
| const gchar *ext = NULL; |
| gboolean has_video; |
| GstCaps *caps; |
| guint num_children; |
| |
| g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), NULL); |
| |
| caps = gst_encoding_profile_get_format (profile); |
| ext = pb_utils_get_file_extension_from_caps (caps); |
| |
| if (!GST_IS_ENCODING_CONTAINER_PROFILE (profile)) |
| goto done; |
| |
| cprofile = GST_ENCODING_CONTAINER_PROFILE (profile); |
| |
| num_children = g_list_length (cprofile->encodingprofiles); |
| |
| /* if it's a tag container profile (e.g. id3mux/apemux), we need |
| * to look at what's inside it */ |
| if (pb_utils_is_tag (caps)) { |
| GST_DEBUG ("tag container profile"); |
| if (num_children == 1) { |
| GstEncodingProfile *child_profile = cprofile->encodingprofiles->data; |
| |
| ext = gst_encoding_profile_get_file_extension (child_profile); |
| } else { |
| GST_WARNING ("expected exactly one child profile with tag profile"); |
| } |
| goto done; |
| } |
| |
| if (num_children == 0) |
| goto done; |
| |
| /* special cases */ |
| has_video = gst_encoding_container_profile_has_video (cprofile); |
| |
| /* Ogg */ |
| if (strcmp (ext, "ogg") == 0) { |
| /* ogg with video => .ogv */ |
| if (has_video) { |
| ext = "ogv"; |
| goto done; |
| } |
| /* ogg with just speex audio => .spx */ |
| if (num_children == 1) { |
| GstEncodingProfile *child_profile = cprofile->encodingprofiles->data; |
| |
| if (GST_IS_ENCODING_AUDIO_PROFILE (child_profile) && |
| gst_encoding_profile_has_format (child_profile, "audio/x-speex")) { |
| ext = "spx"; |
| goto done; |
| } |
| } |
| /* does anyone actually use .oga for ogg audio files? */ |
| goto done; |
| } |
| |
| /* Matroska */ |
| if (has_video && strcmp (ext, "mka") == 0) { |
| ext = "mkv"; |
| goto done; |
| } |
| |
| /* Windows Media / ASF */ |
| if (gst_encoding_profile_has_format (profile, "video/x-ms-asf")) { |
| const GList *l; |
| guint num_wmv = 0, num_wma = 0, num_other = 0; |
| |
| for (l = cprofile->encodingprofiles; l != NULL; l = l->next) { |
| if (gst_encoding_profile_has_format (l->data, "video/x-wmv")) |
| ++num_wmv; |
| else if (gst_encoding_profile_has_format (l->data, "audio/x-wma")) |
| ++num_wma; |
| else |
| ++num_other; |
| } |
| |
| if (num_other > 0) |
| ext = "asf"; |
| else if (num_wmv > 0) |
| ext = "wmv"; |
| else if (num_wma > 0) |
| ext = "wma"; |
| |
| goto done; |
| } |
| |
| done: |
| |
| GST_INFO ("caps %" GST_PTR_FORMAT ", ext: %s", caps, GST_STR_NULL (ext)); |
| gst_caps_unref (caps); |
| return ext; |
| } |
| |
| /** |
| * gst_encoding_profile_find: |
| * @targetname: (transfer none): The name of the target |
| * @profilename: (transfer none): (allow-none): The name of the profile, if %NULL |
| * provided, it will default to the encoding profile called `default`. |
| * @category: (transfer none) (allow-none): The target category. Can be %NULL |
| * |
| * Find the #GstEncodingProfile with the specified name and category. |
| * |
| * Returns: (transfer full): The matching #GstEncodingProfile or %NULL. |
| */ |
| GstEncodingProfile * |
| gst_encoding_profile_find (const gchar * targetname, const gchar * profilename, |
| const gchar * category) |
| { |
| GstEncodingProfile *res = NULL; |
| GstEncodingTarget *target; |
| |
| g_return_val_if_fail (targetname != NULL, NULL); |
| |
| target = gst_encoding_target_load (targetname, category, NULL); |
| if (target) { |
| res = |
| gst_encoding_target_get_profile (target, |
| profilename ? profilename : "default"); |
| gst_encoding_target_unref (target); |
| } |
| |
| return res; |
| } |
| |
| static GstEncodingProfile * |
| combo_search (const gchar * pname) |
| { |
| GstEncodingProfile *res = NULL; |
| gchar **split; |
| gint split_length; |
| |
| /* Splitup */ |
| split = g_strsplit (pname, "/", 3); |
| split_length = g_strv_length (split); |
| if (split_length > 3) |
| 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 GstCaps * |
| get_profile_format_from_possible_factory_name (const gchar * factory_desc, |
| gchar ** new_factory_name, GstCaps ** restrictions) |
| { |
| GList *tmp; |
| GstCaps *caps = NULL, *tmpcaps = gst_caps_from_string (factory_desc); |
| GstStructure *tmpstruct; |
| GstElementFactory *fact = 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 (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; |
| const gchar *sname = |
| gst_structure_get_name (gst_caps_get_structure (caps, 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]); |
| } else { |
| restrictioncaps = NULL; |
| strcaps = g_strdup (restriction_format[0]); |
| } |
| g_strfreev (restriction_format); |
| |
| preset_v = g_strsplit (strcaps, "+", 0); |
| if (preset_v[1]) { |
| strpresence = preset_v[1]; |
| g_free (strcaps); |
| strcaps = g_strdup (preset_v[0]); |
| } else { |
| strpresence = preset_v[0]; |
| } |
| |
| strpresence_v = g_strsplit (strpresence, "|", 0); |
| if (strpresence_v[1]) { /* We have a presence */ |
| gchar *endptr; |
| |
| if (preset_v[1]) { /* We have preset and presence */ |
| preset_name = g_strdup (strpresence_v[0]); |
| } else { /* We have a presence but no preset */ |
| g_free (strcaps); |
| strcaps = g_strdup (strpresence_v[0]); |
| } |
| |
| presence = g_ascii_strtoll (strpresence_v[1], &endptr, 10); |
| if (endptr == strpresence_v[1]) { |
| GST_ERROR ("Wrong presence %s", strpresence_v[1]); |
| |
| return NULL; |
| } |
| } else { /* We have no presence */ |
| if (preset_v[1]) { /* Not presence but preset */ |
| preset_name = g_strdup (preset_v[1]); |
| g_free (strcaps); |
| strcaps = g_strdup (preset_v[0]); |
| } /* Else we have no presence nor preset */ |
| } |
| g_strfreev (strpresence_v); |
| g_strfreev (preset_v); |
| |
| GST_DEBUG ("Creating preset with restrictions: %" GST_PTR_FORMAT |
| ", caps: %s, preset %s, presence %d", restrictioncaps, strcaps, |
| preset_name ? preset_name : "none", presence); |
| |
| caps = gst_caps_from_string (strcaps); |
| 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) { |
| 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)) { |
| GST_ERROR ("Can not create a preset for caps: %s", strcaps_v[i]); |
| |
| return NULL; |
| } |
| } else { |
| res = profile; |
| } |
| } |
| g_strfreev (strcaps_v); |
| |
| return res; |
| } |
| |
| static GstEncodingProfile * |
| profile_from_string (const gchar * string) |
| { |
| GstEncodingProfile *profile; |
| gchar *filename_end; |
| |
| profile = combo_search (string); |
| |
| if (profile) |
| return profile; |
| |
| filename_end = g_strrstr (string, ".gep"); |
| if (filename_end) { |
| GstEncodingTarget *target; |
| gchar *profilename = NULL, *filename; |
| |
| if (filename_end[4] == ':') |
| profilename = g_strdup (&filename_end[5]); |
| |
| if (filename_end[4] == '\0' || profilename) { |
| filename = g_strndup (string, filename_end - string + strlen (".gep")); |
| |
| target = gst_encoding_target_load_from_file (filename, NULL); |
| if (target) { |
| profile = gst_encoding_target_get_profile (target, |
| profilename ? profilename : "default"); |
| gst_encoding_target_unref (target); |
| } |
| |
| g_free (profilename); |
| g_free (filename); |
| } |
| } |
| |
| if (!profile) |
| profile = parse_encoding_profile (string); |
| |
| return profile; |
| } |
| |
| /* GValue transform function */ |
| static void |
| string_to_profile_transform (const GValue * src_value, GValue * dest_value) |
| { |
| const gchar *profilename; |
| GstEncodingProfile *profile; |
| |
| profilename = g_value_get_string (src_value); |
| |
| profile = profile_from_string (profilename); |
| |
| if (profile) |
| g_value_take_object (dest_value, (GObject *) profile); |
| } |
| |
| static gboolean |
| gst_encoding_profile_deserialize_valfunc (GValue * value, const gchar * s) |
| { |
| GstEncodingProfile *profile; |
| |
| profile = profile_from_string (s); |
| |
| if (profile) { |
| g_value_take_object (value, (GObject *) profile); |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| static gboolean |
| add_stream_to_profile (GstEncodingContainerProfile * profile, |
| GstDiscovererStreamInfo * sinfo) |
| { |
| GstEncodingProfile *sprofile = NULL; |
| GstStructure *s; |
| GstCaps *caps; |
| |
| caps = gst_discoverer_stream_info_get_caps (sinfo); |
| |
| /* Should unify this with copy_and_clean_caps() */ |
| s = gst_caps_get_structure (caps, 0); |
| if (gst_structure_has_field (s, "codec_data") |
| || gst_structure_has_field (s, "streamheader") |
| || gst_structure_has_field (s, "parsed") |
| || gst_structure_has_field (s, "framed") |
| || gst_structure_has_field (s, "stream-format") |
| || gst_structure_has_field (s, "alignment")) { |
| caps = gst_caps_make_writable (caps); |
| s = gst_caps_get_structure (caps, 0); |
| gst_structure_remove_field (s, "codec_data"); |
| gst_structure_remove_field (s, "streamheader"); |
| gst_structure_remove_field (s, "parsed"); |
| gst_structure_remove_field (s, "framed"); |
| gst_structure_remove_field (s, "stream-format"); |
| gst_structure_remove_field (s, "alignment"); |
| } |
| |
| GST_LOG ("Stream: %" GST_PTR_FORMAT, caps); |
| if (GST_IS_DISCOVERER_AUDIO_INFO (sinfo)) { |
| sprofile = |
| (GstEncodingProfile *) gst_encoding_audio_profile_new (caps, NULL, |
| NULL, 0); |
| } else if (GST_IS_DISCOVERER_VIDEO_INFO (sinfo)) { |
| sprofile = |
| (GstEncodingProfile *) gst_encoding_video_profile_new (caps, NULL, |
| NULL, 0); |
| } else if (GST_IS_DISCOVERER_CONTAINER_INFO (sinfo)) { |
| GList *streams, *stream; |
| guint n_streams = 0; |
| |
| streams = |
| gst_discoverer_container_info_get_streams (GST_DISCOVERER_CONTAINER_INFO |
| (sinfo)); |
| for (stream = streams; stream; stream = stream->next) { |
| if (add_stream_to_profile (profile, |
| (GstDiscovererStreamInfo *) stream->data)) |
| n_streams++; |
| } |
| gst_discoverer_stream_info_list_free (streams); |
| gst_caps_unref (caps); |
| |
| return n_streams != 0; |
| } else { |
| GST_WARNING ("Ignoring stream of type '%s'", |
| g_type_name (G_OBJECT_TYPE (sinfo))); |
| /* subtitles or other ? ignore for now */ |
| } |
| if (sprofile) |
| gst_encoding_container_profile_add_profile (profile, sprofile); |
| else |
| GST_ERROR ("Failed to create stream profile from caps %" GST_PTR_FORMAT, |
| caps); |
| gst_caps_unref (caps); |
| |
| return sprofile != NULL; |
| } |
| |
| /** |
| * gst_encoding_profile_from_discoverer: |
| * @info: (transfer none): The #GstDiscovererInfo to read from |
| * |
| * Creates a #GstEncodingProfile matching the formats from the given |
| * #GstDiscovererInfo. Streams other than audio or video (eg, |
| * subtitles), are currently ignored. |
| * |
| * Returns: (transfer full): The new #GstEncodingProfile or %NULL. |
| */ |
| GstEncodingProfile * |
| gst_encoding_profile_from_discoverer (GstDiscovererInfo * info) |
| { |
| GstEncodingContainerProfile *profile; |
| GstDiscovererStreamInfo *sinfo; |
| GList *streams, *stream; |
| GstCaps *caps = NULL; |
| guint n_streams = 0; |
| |
| if (!info || gst_discoverer_info_get_result (info) != GST_DISCOVERER_OK) |
| return NULL; |
| |
| sinfo = gst_discoverer_info_get_stream_info (info); |
| if (!sinfo) |
| return NULL; |
| |
| caps = gst_discoverer_stream_info_get_caps (sinfo); |
| GST_LOG ("Container: %" GST_PTR_FORMAT, caps); |
| profile = |
| gst_encoding_container_profile_new ("auto-generated", |
| "Automatically generated from GstDiscovererInfo", caps, NULL); |
| gst_caps_unref (caps); |
| if (!profile) { |
| GST_ERROR ("Failed to create container profile from caps %" GST_PTR_FORMAT, |
| caps); |
| return NULL; |
| } |
| |
| streams = |
| gst_discoverer_container_info_get_streams (GST_DISCOVERER_CONTAINER_INFO |
| (sinfo)); |
| for (stream = streams; stream; stream = stream->next) { |
| if (add_stream_to_profile (profile, |
| (GstDiscovererStreamInfo *) stream->data)) |
| n_streams++; |
| } |
| gst_discoverer_stream_info_list_free (streams); |
| |
| if (n_streams == 0) { |
| GST_ERROR ("Failed to add any streams"); |
| g_object_unref (profile); |
| return NULL; |
| } |
| |
| return (GstEncodingProfile *) profile; |
| } |
| |
| /** |
| * gst_encoding_profile_copy: |
| * @self: The #GstEncodingProfile to copy |
| * |
| * Makes a deep copy of @self |
| * |
| * Returns: (transfer full): The copy of @self |
| * |
| * Since 1.12 |
| */ |
| GstEncodingProfile * |
| gst_encoding_profile_copy (GstEncodingProfile * self) |
| { |
| GstEncodingProfileClass *klass = |
| (GstEncodingProfileClass *) G_OBJECT_GET_CLASS (self); |
| GstEncodingProfile *copy = |
| common_creation (G_OBJECT_TYPE (self), self->format, self->preset, |
| self->name, self->description, self->restriction, self->presence); |
| |
| copy->enabled = self->enabled; |
| copy->allow_dynamic_output = self->allow_dynamic_output; |
| gst_encoding_profile_set_preset_name (copy, self->preset_name); |
| gst_encoding_profile_set_description (copy, self->description); |
| |
| if (klass->copy) |
| klass->copy (self, copy); |
| |
| return copy; |
| } |