| /* GStreamer base utils library missing plugins support | |
| * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net> | |
| * | |
| * 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:gstpbutilsmissingplugins | |
| * @title: Missing plugins | |
| * @short_description: Create, recognise and parse missing-plugins messages | |
| * | |
| * Functions to create, recognise and parse missing-plugins messages for | |
| * applications and elements. | |
| * | |
| * Missing-plugin messages are posted on the bus by elements like decodebin | |
| * or playbin if they can't find an appropriate source element or decoder | |
| * element. The application can use these messages for two things: | |
| * | |
| * * concise error/problem reporting to the user mentioning what exactly | |
| * is missing, see gst_missing_plugin_message_get_description() | |
| * | |
| * * initiate installation of missing plugins, see | |
| * gst_missing_plugin_message_get_installer_detail() and | |
| * gst_install_plugins_async() | |
| * | |
| * Applications may also create missing-plugin messages themselves to install | |
| * required elements that are missing, using the install mechanism mentioned | |
| * above. | |
| * | |
| */ | |
| #ifdef HAVE_CONFIG_H | |
| # include "config.h" | |
| #endif | |
| #ifdef HAVE_SYS_TYPES_H | |
| # include <sys/types.h> | |
| #endif | |
| #ifdef HAVE_UNISTD_H | |
| # include <unistd.h> /* getpid on UNIX */ | |
| #endif | |
| #include <glib/gi18n-lib.h> | |
| #include "pbutils.h" | |
| #include "pbutils-private.h" | |
| #include <string.h> | |
| #ifdef G_OS_WIN32 | |
| #define WIN32_LEAN_AND_MEAN | |
| #include <windows.h> | |
| #include <processthreadsapi.h> | |
| #endif | |
| #ifndef GST_DISABLE_GST_DEBUG | |
| #define GST_CAT_DEFAULT gst_pb_utils_missing_plugins_ensure_debug_category() | |
| static GstDebugCategory * | |
| gst_pb_utils_missing_plugins_ensure_debug_category (void) | |
| { | |
| static gsize cat_gonce = 0; | |
| if (g_once_init_enter (&cat_gonce)) { | |
| GstDebugCategory *cat = NULL; | |
| GST_DEBUG_CATEGORY_INIT (cat, "missing-plugins", 0, | |
| "GstPbUtils missing plugins helper"); | |
| g_once_init_leave (&cat_gonce, (gsize) cat); | |
| } | |
| return (GstDebugCategory *) cat_gonce; | |
| } | |
| #endif /* GST_DISABLE_GST_DEBUG */ | |
| /* use glib's abstraction once it's landed | |
| * https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2475 */ | |
| #ifdef G_OS_WIN32 | |
| static inline DWORD | |
| _gst_getpid (void) | |
| { | |
| return GetCurrentProcessId (); | |
| } | |
| #else | |
| static inline pid_t | |
| _gst_getpid (void) | |
| { | |
| return getpid (); | |
| } | |
| #endif | |
| #define GST_DETAIL_STRING_MARKER "gstreamer" | |
| typedef enum | |
| { | |
| GST_MISSING_TYPE_UNKNOWN = 0, | |
| GST_MISSING_TYPE_URISOURCE, | |
| GST_MISSING_TYPE_URISINK, | |
| GST_MISSING_TYPE_ELEMENT, | |
| GST_MISSING_TYPE_DECODER, | |
| GST_MISSING_TYPE_ENCODER | |
| } GstMissingType; | |
| static const struct | |
| { | |
| GstMissingType type; | |
| const gchar type_string[12]; | |
| } missing_type_mapping[] = { | |
| { | |
| GST_MISSING_TYPE_URISOURCE, "urisource"}, { | |
| GST_MISSING_TYPE_URISINK, "urisink"}, { | |
| GST_MISSING_TYPE_ELEMENT, "element"}, { | |
| GST_MISSING_TYPE_DECODER, "decoder"}, { | |
| GST_MISSING_TYPE_ENCODER, "encoder"} | |
| }; | |
| static GstMissingType | |
| missing_structure_get_type (const GstStructure * s) | |
| { | |
| const gchar *type; | |
| guint i; | |
| type = gst_structure_get_string (s, "type"); | |
| g_return_val_if_fail (type != NULL, GST_MISSING_TYPE_UNKNOWN); | |
| for (i = 0; i < G_N_ELEMENTS (missing_type_mapping); ++i) { | |
| if (strcmp (missing_type_mapping[i].type_string, type) == 0) | |
| return missing_type_mapping[i].type; | |
| } | |
| return GST_MISSING_TYPE_UNKNOWN; | |
| } | |
| GstCaps * | |
| copy_and_clean_caps (const GstCaps * caps) | |
| { | |
| GstStructure *s; | |
| GstCaps *ret; | |
| ret = gst_caps_copy (caps); | |
| /* make caps easier to interpret, remove common fields that are likely | |
| * to be irrelevant for determining the right plugin (ie. mostly fields | |
| * where template caps usually have the standard MIN - MAX range as value) */ | |
| s = gst_caps_get_structure (ret, 0); | |
| gst_structure_remove_field (s, "codec_data"); | |
| gst_structure_remove_field (s, "streamheader"); | |
| gst_structure_remove_field (s, "palette_data"); | |
| gst_structure_remove_field (s, "pixel-aspect-ratio"); | |
| gst_structure_remove_field (s, "framerate"); | |
| gst_structure_remove_field (s, "leaf_size"); | |
| gst_structure_remove_field (s, "packet_size"); | |
| gst_structure_remove_field (s, "block_align"); | |
| gst_structure_remove_field (s, "metadata-interval"); /* icy caps */ | |
| /* decoders/encoders almost always handle the usual width/height/channel/rate | |
| * range (and if we don't remove this then the app will have a much harder | |
| * time blacklisting formats it has unsuccessfully tried to install before) */ | |
| gst_structure_remove_field (s, "width"); | |
| gst_structure_remove_field (s, "depth"); | |
| gst_structure_remove_field (s, "height"); | |
| gst_structure_remove_field (s, "channels"); | |
| gst_structure_remove_field (s, "rate"); | |
| /* parsed, framed, stream-format and alignment are going to be handled by | |
| * parsers and not relevant for decoders/encoders usually */ | |
| 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"); | |
| /* rtp fields */ | |
| gst_structure_remove_field (s, "config"); | |
| gst_structure_remove_field (s, "clock-rate"); | |
| gst_structure_remove_field (s, "timestamp-offset"); | |
| gst_structure_remove_field (s, "maxps"); | |
| gst_structure_remove_field (s, "seqnum-offset"); | |
| gst_structure_remove_field (s, "npt-start"); | |
| gst_structure_remove_field (s, "npt-stop"); | |
| gst_structure_remove_field (s, "play-speed"); | |
| gst_structure_remove_field (s, "play-scale"); | |
| gst_structure_remove_field (s, "dynamic_range"); | |
| return ret; | |
| } | |
| /** | |
| * gst_missing_uri_source_message_new: | |
| * @element: the #GstElement posting the message | |
| * @protocol: the URI protocol the missing source needs to implement, | |
| * e.g. "http" or "mms" | |
| * | |
| * Creates a missing-plugin message for @element to notify the application | |
| * that a source element for a particular URI protocol is missing. This | |
| * function is mainly for use in plugins. | |
| * | |
| * Returns: (transfer full): a new #GstMessage | |
| */ | |
| GstMessage * | |
| gst_missing_uri_source_message_new (GstElement * element, | |
| const gchar * protocol) | |
| { | |
| GstStructure *s; | |
| gchar *description; | |
| g_return_val_if_fail (element != NULL, NULL); | |
| g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); | |
| g_return_val_if_fail (protocol != NULL, NULL); | |
| description = gst_pb_utils_get_source_description (protocol); | |
| s = gst_structure_new ("missing-plugin", "type", G_TYPE_STRING, | |
| "urisource", "detail", G_TYPE_STRING, protocol, "name", G_TYPE_STRING, | |
| description, NULL); | |
| g_free (description); | |
| return gst_message_new_element (GST_OBJECT_CAST (element), s); | |
| } | |
| /** | |
| * gst_missing_uri_sink_message_new: | |
| * @element: the #GstElement posting the message | |
| * @protocol: the URI protocol the missing sink needs to implement, | |
| * e.g. "http" or "smb" | |
| * | |
| * Creates a missing-plugin message for @element to notify the application | |
| * that a sink element for a particular URI protocol is missing. This | |
| * function is mainly for use in plugins. | |
| * | |
| * Returns: (transfer full): a new #GstMessage | |
| */ | |
| GstMessage * | |
| gst_missing_uri_sink_message_new (GstElement * element, const gchar * protocol) | |
| { | |
| GstStructure *s; | |
| gchar *description; | |
| g_return_val_if_fail (element != NULL, NULL); | |
| g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); | |
| g_return_val_if_fail (protocol != NULL, NULL); | |
| description = gst_pb_utils_get_sink_description (protocol); | |
| s = gst_structure_new ("missing-plugin", "type", G_TYPE_STRING, | |
| "urisink", "detail", G_TYPE_STRING, protocol, "name", G_TYPE_STRING, | |
| description, NULL); | |
| g_free (description); | |
| return gst_message_new_element (GST_OBJECT_CAST (element), s); | |
| } | |
| /** | |
| * gst_missing_element_message_new: | |
| * @element: the #GstElement posting the message | |
| * @factory_name: the name of the missing element (element factory), | |
| * e.g. "videoscale" or "cdparanoiasrc" | |
| * | |
| * Creates a missing-plugin message for @element to notify the application | |
| * that a certain required element is missing. This function is mainly for | |
| * use in plugins. | |
| * | |
| * Returns: (transfer full): a new #GstMessage | |
| */ | |
| GstMessage * | |
| gst_missing_element_message_new (GstElement * element, | |
| const gchar * factory_name) | |
| { | |
| GstStructure *s; | |
| gchar *description; | |
| g_return_val_if_fail (element != NULL, NULL); | |
| g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); | |
| g_return_val_if_fail (factory_name != NULL, NULL); | |
| description = gst_pb_utils_get_element_description (factory_name); | |
| s = gst_structure_new ("missing-plugin", "type", G_TYPE_STRING, | |
| "element", "detail", G_TYPE_STRING, factory_name, "name", G_TYPE_STRING, | |
| description, NULL); | |
| g_free (description); | |
| return gst_message_new_element (GST_OBJECT_CAST (element), s); | |
| } | |
| /** | |
| * gst_missing_decoder_message_new: | |
| * @element: the #GstElement posting the message | |
| * @decode_caps: the (fixed) caps for which a decoder element is needed | |
| * | |
| * Creates a missing-plugin message for @element to notify the application | |
| * that a decoder element for a particular set of (fixed) caps is missing. | |
| * This function is mainly for use in plugins. | |
| * | |
| * Returns: (transfer full): a new #GstMessage | |
| */ | |
| GstMessage * | |
| gst_missing_decoder_message_new (GstElement * element, | |
| const GstCaps * decode_caps) | |
| { | |
| GstStructure *s; | |
| GstCaps *caps; | |
| gchar *description; | |
| g_return_val_if_fail (element != NULL, NULL); | |
| g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); | |
| g_return_val_if_fail (decode_caps != NULL, NULL); | |
| g_return_val_if_fail (GST_IS_CAPS (decode_caps), NULL); | |
| g_return_val_if_fail (!gst_caps_is_any (decode_caps), NULL); | |
| g_return_val_if_fail (!gst_caps_is_empty (decode_caps), NULL); | |
| g_return_val_if_fail (gst_caps_is_fixed (decode_caps), NULL); | |
| description = gst_pb_utils_get_decoder_description (decode_caps); | |
| caps = copy_and_clean_caps (decode_caps); | |
| s = gst_structure_new ("missing-plugin", "type", G_TYPE_STRING, | |
| "decoder", "detail", GST_TYPE_CAPS, caps, "name", G_TYPE_STRING, | |
| description, NULL); | |
| gst_caps_unref (caps); | |
| g_free (description); | |
| return gst_message_new_element (GST_OBJECT_CAST (element), s); | |
| } | |
| /** | |
| * gst_missing_encoder_message_new: | |
| * @element: the #GstElement posting the message | |
| * @encode_caps: the (fixed) caps for which an encoder element is needed | |
| * | |
| * Creates a missing-plugin message for @element to notify the application | |
| * that an encoder element for a particular set of (fixed) caps is missing. | |
| * This function is mainly for use in plugins. | |
| * | |
| * Returns: (transfer full): a new #GstMessage | |
| */ | |
| GstMessage * | |
| gst_missing_encoder_message_new (GstElement * element, | |
| const GstCaps * encode_caps) | |
| { | |
| GstStructure *s; | |
| GstCaps *caps; | |
| gchar *description; | |
| g_return_val_if_fail (element != NULL, NULL); | |
| g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); | |
| g_return_val_if_fail (encode_caps != NULL, NULL); | |
| g_return_val_if_fail (GST_IS_CAPS (encode_caps), NULL); | |
| g_return_val_if_fail (!gst_caps_is_any (encode_caps), NULL); | |
| g_return_val_if_fail (!gst_caps_is_empty (encode_caps), NULL); | |
| g_return_val_if_fail (gst_caps_is_fixed (encode_caps), NULL); | |
| description = gst_pb_utils_get_encoder_description (encode_caps); | |
| caps = copy_and_clean_caps (encode_caps); | |
| s = gst_structure_new ("missing-plugin", "type", G_TYPE_STRING, | |
| "encoder", "detail", GST_TYPE_CAPS, caps, "name", G_TYPE_STRING, | |
| description, NULL); | |
| gst_caps_unref (caps); | |
| g_free (description); | |
| return gst_message_new_element (GST_OBJECT_CAST (element), s); | |
| } | |
| static gboolean | |
| missing_structure_get_string_detail (const GstStructure * s, gchar ** p_detail) | |
| { | |
| const gchar *detail; | |
| GType detail_type; | |
| *p_detail = NULL; | |
| detail_type = gst_structure_get_field_type (s, "detail"); | |
| if (!g_type_is_a (detail_type, G_TYPE_STRING)) { | |
| GST_WARNING ("expected 'detail' field to be of G_TYPE_STRING"); | |
| return FALSE; | |
| } | |
| detail = gst_structure_get_string (s, "detail"); | |
| if (detail == NULL || *detail == '\0') { | |
| GST_WARNING ("empty 'detail' field"); | |
| return FALSE; | |
| } | |
| *p_detail = g_strdup (detail); | |
| return TRUE; | |
| } | |
| static gboolean | |
| missing_structure_get_caps_detail (const GstStructure * s, GstCaps ** p_caps) | |
| { | |
| const GstCaps *caps; | |
| const GValue *val; | |
| GType detail_type; | |
| *p_caps = NULL; | |
| detail_type = gst_structure_get_field_type (s, "detail"); | |
| if (!g_type_is_a (detail_type, GST_TYPE_CAPS)) { | |
| GST_WARNING ("expected 'detail' field to be of GST_TYPE_CAPS"); | |
| return FALSE; | |
| } | |
| val = gst_structure_get_value (s, "detail"); | |
| caps = gst_value_get_caps (val); | |
| if (gst_caps_is_empty (caps) || gst_caps_is_any (caps)) { | |
| GST_WARNING ("EMPTY or ANY caps not allowed"); | |
| return FALSE; | |
| } | |
| *p_caps = gst_caps_copy (caps); | |
| return TRUE; | |
| } | |
| /** | |
| * gst_missing_plugin_message_get_installer_detail: | |
| * @msg: a missing-plugin #GstMessage of type #GST_MESSAGE_ELEMENT | |
| * | |
| * Returns an opaque string containing all the details about the missing | |
| * element to be passed to an external installer called via | |
| * gst_install_plugins_async() or gst_install_plugins_sync(). | |
| * | |
| * This function is mainly for applications that call external plugin | |
| * installation mechanisms using one of the two above-mentioned functions. | |
| * | |
| * Returns: (nullable): a newly-allocated detail string, or NULL on error. Free string | |
| * with g_free() when not needed any longer. | |
| */ | |
| gchar * | |
| gst_missing_plugin_message_get_installer_detail (GstMessage * msg) | |
| { | |
| GstMissingType missing_type; | |
| const gchar *progname; | |
| const gchar *type; | |
| GString *str = NULL; | |
| gchar *detail = NULL; | |
| gchar *desc; | |
| const GstStructure *structure; | |
| g_return_val_if_fail (gst_is_missing_plugin_message (msg), NULL); | |
| structure = gst_message_get_structure (msg); | |
| GST_LOG ("Parsing missing-plugin message: %" GST_PTR_FORMAT, structure); | |
| missing_type = missing_structure_get_type (structure); | |
| if (missing_type == GST_MISSING_TYPE_UNKNOWN) { | |
| GST_WARNING ("couldn't parse 'type' field"); | |
| goto error; | |
| } | |
| type = gst_structure_get_string (structure, "type"); | |
| g_assert (type != NULL); /* validity already checked above */ | |
| /* FIXME: use gst_installer_detail_new() here too */ | |
| str = g_string_new (GST_DETAIL_STRING_MARKER "|"); | |
| g_string_append_printf (str, "%s|", GST_API_VERSION); | |
| progname = (const gchar *) g_get_prgname (); | |
| if (progname) { | |
| g_string_append_printf (str, "%s|", progname); | |
| } else { | |
| g_string_append_printf (str, "pid/%lu|", (gulong) _gst_getpid ()); | |
| } | |
| desc = gst_missing_plugin_message_get_description (msg); | |
| if (desc) { | |
| g_strdelimit (desc, "|", '#'); | |
| g_string_append_printf (str, "%s|", desc); | |
| g_free (desc); | |
| } else { | |
| g_string_append (str, "|"); | |
| } | |
| switch (missing_type) { | |
| case GST_MISSING_TYPE_URISOURCE: | |
| case GST_MISSING_TYPE_URISINK: | |
| case GST_MISSING_TYPE_ELEMENT: | |
| if (!missing_structure_get_string_detail (structure, &detail)) | |
| goto error; | |
| break; | |
| case GST_MISSING_TYPE_DECODER: | |
| case GST_MISSING_TYPE_ENCODER:{ | |
| GstCaps *caps = NULL; | |
| if (!missing_structure_get_caps_detail (structure, &caps)) | |
| goto error; | |
| detail = gst_caps_to_string (caps); | |
| gst_caps_unref (caps); | |
| break; | |
| } | |
| default: | |
| g_return_val_if_reached (NULL); | |
| } | |
| g_string_append_printf (str, "%s-%s", type, detail); | |
| g_free (detail); | |
| return g_string_free (str, FALSE); | |
| /* ERRORS */ | |
| error: | |
| { | |
| GST_WARNING ("Failed to parse missing-plugin msg: %" GST_PTR_FORMAT, msg); | |
| if (str) | |
| g_string_free (str, TRUE); | |
| return NULL; | |
| } | |
| } | |
| /** | |
| * gst_missing_plugin_message_get_description: | |
| * @msg: a missing-plugin #GstMessage of type #GST_MESSAGE_ELEMENT | |
| * | |
| * Returns a localised string describing the missing feature, for use in | |
| * error dialogs and the like. Should never return NULL unless @msg is not | |
| * a valid missing-plugin message. | |
| * | |
| * This function is mainly for applications that need a human-readable string | |
| * describing a missing plugin, given a previously collected missing-plugin | |
| * message | |
| * | |
| * Returns: a newly-allocated description string. Free | |
| * string with g_free() when not needed any longer. | |
| */ | |
| gchar * | |
| gst_missing_plugin_message_get_description (GstMessage * msg) | |
| { | |
| GstMissingType missing_type; | |
| const gchar *desc; | |
| gchar *ret = NULL; | |
| const GstStructure *structure; | |
| g_return_val_if_fail (gst_is_missing_plugin_message (msg), NULL); | |
| structure = gst_message_get_structure (msg); | |
| GST_LOG ("Parsing missing-plugin message: %" GST_PTR_FORMAT, structure); | |
| desc = gst_structure_get_string (structure, "name"); | |
| if (desc != NULL && *desc != '\0') { | |
| ret = g_strdup (desc); | |
| goto done; | |
| } | |
| /* fallback #1 */ | |
| missing_type = missing_structure_get_type (structure); | |
| switch (missing_type) { | |
| case GST_MISSING_TYPE_URISOURCE: | |
| case GST_MISSING_TYPE_URISINK: | |
| case GST_MISSING_TYPE_ELEMENT:{ | |
| gchar *detail = NULL; | |
| if (missing_structure_get_string_detail (structure, &detail)) { | |
| if (missing_type == GST_MISSING_TYPE_URISOURCE) | |
| ret = gst_pb_utils_get_source_description (detail); | |
| else if (missing_type == GST_MISSING_TYPE_URISINK) | |
| ret = gst_pb_utils_get_sink_description (detail); | |
| else | |
| ret = gst_pb_utils_get_element_description (detail); | |
| g_free (detail); | |
| } | |
| break; | |
| } | |
| case GST_MISSING_TYPE_DECODER: | |
| case GST_MISSING_TYPE_ENCODER:{ | |
| GstCaps *caps = NULL; | |
| if (missing_structure_get_caps_detail (structure, &caps)) { | |
| if (missing_type == GST_MISSING_TYPE_DECODER) | |
| ret = gst_pb_utils_get_decoder_description (caps); | |
| else | |
| ret = gst_pb_utils_get_encoder_description (caps); | |
| gst_caps_unref (caps); | |
| } | |
| break; | |
| } | |
| default: | |
| break; | |
| } | |
| if (ret) | |
| goto done; | |
| /* fallback #2 */ | |
| switch (missing_type) { | |
| case GST_MISSING_TYPE_URISOURCE: | |
| desc = _("Unknown source element"); | |
| break; | |
| case GST_MISSING_TYPE_URISINK: | |
| desc = _("Unknown sink element"); | |
| break; | |
| case GST_MISSING_TYPE_ELEMENT: | |
| desc = _("Unknown element"); | |
| break; | |
| case GST_MISSING_TYPE_DECODER: | |
| desc = _("Unknown decoder element"); | |
| break; | |
| case GST_MISSING_TYPE_ENCODER: | |
| desc = _("Unknown encoder element"); | |
| break; | |
| default: | |
| /* we should really never get here, but we better still return | |
| * something if we do */ | |
| desc = _("Plugin or element of unknown type"); | |
| break; | |
| } | |
| ret = g_strdup (desc); | |
| done: | |
| GST_LOG ("returning '%s'", ret); | |
| return ret; | |
| } | |
| /** | |
| * gst_is_missing_plugin_message: | |
| * @msg: a #GstMessage | |
| * | |
| * Checks whether @msg is a missing plugins message. | |
| * | |
| * Returns: %TRUE if @msg is a missing-plugins message, otherwise %FALSE. | |
| */ | |
| gboolean | |
| gst_is_missing_plugin_message (GstMessage * msg) | |
| { | |
| const GstStructure *structure; | |
| g_return_val_if_fail (msg != NULL, FALSE); | |
| g_return_val_if_fail (GST_IS_MESSAGE (msg), FALSE); | |
| structure = gst_message_get_structure (msg); | |
| if (GST_MESSAGE_TYPE (msg) != GST_MESSAGE_ELEMENT || structure == NULL) | |
| return FALSE; | |
| return gst_structure_has_name (structure, "missing-plugin"); | |
| } | |
| /* takes ownership of the description */ | |
| static gchar * | |
| gst_installer_detail_new (gchar * description, const gchar * type, | |
| const gchar * detail) | |
| { | |
| const gchar *progname; | |
| GString *s; | |
| s = g_string_new (GST_DETAIL_STRING_MARKER "|"); | |
| g_string_append_printf (s, "%s|", GST_API_VERSION); | |
| progname = (const gchar *) g_get_prgname (); | |
| if (progname) { | |
| g_string_append_printf (s, "%s|", progname); | |
| } else { | |
| g_string_append_printf (s, "pid/%lu|", (gulong) _gst_getpid ()); | |
| } | |
| if (description) { | |
| g_strdelimit (description, "|", '#'); | |
| g_string_append_printf (s, "%s|", description); | |
| g_free (description); | |
| } else { | |
| g_string_append (s, "|"); | |
| } | |
| g_string_append_printf (s, "%s-%s", type, detail); | |
| return g_string_free (s, FALSE); | |
| } | |
| /** | |
| * gst_missing_uri_source_installer_detail_new: | |
| * @protocol: the URI protocol the missing source needs to implement, | |
| * e.g. "http" or "mms" | |
| * | |
| * Returns an opaque string containing all the details about the missing | |
| * element to be passed to an external installer called via | |
| * gst_install_plugins_async() or gst_install_plugins_sync(). | |
| * | |
| * This function is mainly for applications that call external plugin | |
| * installation mechanisms using one of the two above-mentioned functions in | |
| * the case where the application knows exactly what kind of plugin it is | |
| * missing. | |
| * | |
| * Returns: (transfer full): a newly-allocated detail string. Free string | |
| * with g_free() when not needed any longer. | |
| */ | |
| gchar * | |
| gst_missing_uri_source_installer_detail_new (const gchar * protocol) | |
| { | |
| gchar *desc; | |
| g_return_val_if_fail (protocol != NULL, NULL); | |
| desc = gst_pb_utils_get_source_description (protocol); | |
| return gst_installer_detail_new (desc, "urisource", protocol); | |
| } | |
| /** | |
| * gst_missing_uri_sink_installer_detail_new: | |
| * @protocol: the URI protocol the missing source needs to implement, | |
| * e.g. "http" or "mms" | |
| * | |
| * Returns an opaque string containing all the details about the missing | |
| * element to be passed to an external installer called via | |
| * gst_install_plugins_async() or gst_install_plugins_sync(). | |
| * | |
| * This function is mainly for applications that call external plugin | |
| * installation mechanisms using one of the two above-mentioned functions in | |
| * the case where the application knows exactly what kind of plugin it is | |
| * missing. | |
| * | |
| * Returns: (transfer full): a newly-allocated detail string. Free string | |
| * with g_free() when not needed any longer. | |
| */ | |
| gchar * | |
| gst_missing_uri_sink_installer_detail_new (const gchar * protocol) | |
| { | |
| gchar *desc; | |
| g_return_val_if_fail (protocol != NULL, NULL); | |
| desc = gst_pb_utils_get_sink_description (protocol); | |
| return gst_installer_detail_new (desc, "urisink", protocol); | |
| } | |
| /** | |
| * gst_missing_element_installer_detail_new: | |
| * @factory_name: the name of the missing element (element factory), | |
| * e.g. "videoscale" or "cdparanoiasrc" | |
| * | |
| * Returns an opaque string containing all the details about the missing | |
| * element to be passed to an external installer called via | |
| * gst_install_plugins_async() or gst_install_plugins_sync(). | |
| * | |
| * This function is mainly for applications that call external plugin | |
| * installation mechanisms using one of the two above-mentioned functions in | |
| * the case where the application knows exactly what kind of plugin it is | |
| * missing. | |
| * | |
| * Returns: (transfer full): a newly-allocated detail string. Free string | |
| * with g_free() when not needed any longer. | |
| */ | |
| gchar * | |
| gst_missing_element_installer_detail_new (const gchar * factory_name) | |
| { | |
| gchar *desc; | |
| g_return_val_if_fail (factory_name != NULL, NULL); | |
| desc = gst_pb_utils_get_element_description (factory_name); | |
| return gst_installer_detail_new (desc, "element", factory_name); | |
| } | |
| /** | |
| * gst_missing_decoder_installer_detail_new: | |
| * @decode_caps: the (fixed) caps for which a decoder element is needed | |
| * | |
| * Returns an opaque string containing all the details about the missing | |
| * element to be passed to an external installer called via | |
| * gst_install_plugins_async() or gst_install_plugins_sync(). | |
| * | |
| * This function is mainly for applications that call external plugin | |
| * installation mechanisms using one of the two above-mentioned functions in | |
| * the case where the application knows exactly what kind of plugin it is | |
| * missing. | |
| * | |
| * Returns: (transfer full): a newly-allocated detail string. Free string | |
| * with g_free() when not needed any longer. | |
| */ | |
| gchar * | |
| gst_missing_decoder_installer_detail_new (const GstCaps * decode_caps) | |
| { | |
| GstCaps *caps; | |
| gchar *detail_str, *caps_str, *desc; | |
| g_return_val_if_fail (decode_caps != NULL, NULL); | |
| g_return_val_if_fail (GST_IS_CAPS (decode_caps), NULL); | |
| g_return_val_if_fail (!gst_caps_is_any (decode_caps), NULL); | |
| g_return_val_if_fail (!gst_caps_is_empty (decode_caps), NULL); | |
| g_return_val_if_fail (gst_caps_is_fixed (decode_caps), NULL); | |
| desc = gst_pb_utils_get_decoder_description (decode_caps); | |
| caps = copy_and_clean_caps (decode_caps); | |
| caps_str = gst_caps_to_string (caps); | |
| detail_str = gst_installer_detail_new (desc, "decoder", caps_str); | |
| g_free (caps_str); | |
| gst_caps_unref (caps); | |
| return detail_str; | |
| } | |
| /** | |
| * gst_missing_encoder_installer_detail_new: | |
| * @encode_caps: the (fixed) caps for which an encoder element is needed | |
| * | |
| * Returns an opaque string containing all the details about the missing | |
| * element to be passed to an external installer called via | |
| * gst_install_plugins_async() or gst_install_plugins_sync(). | |
| * | |
| * This function is mainly for applications that call external plugin | |
| * installation mechanisms using one of the two above-mentioned functions in | |
| * the case where the application knows exactly what kind of plugin it is | |
| * missing. | |
| * | |
| * Returns: (transfer full): a newly-allocated detail string. Free string | |
| * with g_free() when not needed any longer. | |
| */ | |
| gchar * | |
| gst_missing_encoder_installer_detail_new (const GstCaps * encode_caps) | |
| { | |
| GstCaps *caps; | |
| gchar *detail_str, *caps_str, *desc; | |
| g_return_val_if_fail (encode_caps != NULL, NULL); | |
| g_return_val_if_fail (GST_IS_CAPS (encode_caps), NULL); | |
| g_return_val_if_fail (!gst_caps_is_any (encode_caps), NULL); | |
| g_return_val_if_fail (!gst_caps_is_empty (encode_caps), NULL); | |
| g_return_val_if_fail (gst_caps_is_fixed (encode_caps), NULL); | |
| desc = gst_pb_utils_get_encoder_description (encode_caps); | |
| caps = copy_and_clean_caps (encode_caps); | |
| caps_str = gst_caps_to_string (caps); | |
| detail_str = gst_installer_detail_new (desc, "encoder", caps_str); | |
| g_free (caps_str); | |
| gst_caps_unref (caps); | |
| return detail_str; | |
| } |