From 7346764b8edbb5f16ba33718678c84a5d9195dfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 3 Jan 2025 15:15:57 +0200 Subject: [PATCH] play: Distinguish missing plugin errors and include more details in error/warning messages Include the URI (and if possible) stream-id in the messages. These are provided by uridecodebin3 / decodebin3 in most cases but there is fallback code to guess them otherwise. For missing plugin errors also the installer details are included. The URI is included in all message types. Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3547 Part-of: --- girs/GstPlay-1.0.gir | 196 +++++++- .../gst-libs/gst/play/gstplay.c | 438 ++++++++++++++++-- .../gst-libs/gst/play/gstplay.h | 31 +- 3 files changed, 623 insertions(+), 42 deletions(-) diff --git a/girs/GstPlay-1.0.gir b/girs/GstPlay-1.0.gir index 40bb287587..9fabc6df09 100644 --- a/girs/GstPlay-1.0.gir +++ b/girs/GstPlay-1.0.gir @@ -1900,6 +1900,34 @@ See also #gst_play_get_message_bus() + + Reads the stream ID the play message @msg applies to, if any. + + + The stream ID this message applies to + + + + + A #GstMessage + + + + + + Reads the URI the play message @msg applies to. + + + The URI this message applies to + + + + + A #GstMessage + + + + Parse the given buffering @msg and extract the corresponding value @@ -1971,7 +1999,11 @@ See also #gst_play_get_message_bus() - Parse the given error @msg and extract the corresponding #GError. + Parse the given error @msg and extract the corresponding #GError. + +Since 1.26 the details will always contain the URI this refers to in an +"uri" field of type string, and (if known) the string "stream-id" it is +referring to. @@ -1991,6 +2023,35 @@ See also #gst_play_get_message_bus() + + Parses missing plugin descriptions and installer details from a +GST_PLAY_ERROR_MISSING_PLUGIN error message. + +Both arrays will have the same length, and strings at the same index +correspond to each other. + +The installer details can be passed to gst_install_plugins_sync() or +gst_install_plugins_async(). + + + %TRUE if the message contained a missing-plugin error. + + + + + A #GstMessage + + + + a %NULL-terminated array of descriptions + + + + a %NULL-terminated array of installer details + + + + Parse the given media-info-updated @msg and extract the corresponding media information @@ -2149,7 +2210,11 @@ See also #gst_play_get_message_bus() - Parse the given warning @msg and extract the corresponding #GError + Parse the given warning @msg and extract the corresponding #GError. + +Since 1.26 the details will always contain the URI this refers to in an +"uri" field of type string, and (if known) the string "stream-id" it is +referring to. @@ -2169,6 +2234,35 @@ See also #gst_play_get_message_bus() + + Parses missing plugin descriptions and installer details from a +GST_PLAY_ERROR_MISSING_PLUGIN warning message. + +Both arrays will have the same length, and strings at the same index +correspond to each other. + +The installer details can be passed to gst_install_plugins_sync() or +gst_install_plugins_async(). + + + %TRUE if the message contained a missing-plugin error. + + + + + A #GstMessage + + + + a %NULL-terminated array of descriptions + + + + a %NULL-terminated array of installer details + + + + @@ -2928,6 +3022,34 @@ freed using gst_play_visualization_free(). + + Reads the stream ID the play message @msg applies to, if any. + + + The stream ID this message applies to + + + + + A #GstMessage + + + + + + Reads the URI the play message @msg applies to. + + + The URI this message applies to + + + + + A #GstMessage + + + + Parse the given buffering @msg and extract the corresponding value @@ -2999,7 +3121,11 @@ freed using gst_play_visualization_free(). - Parse the given error @msg and extract the corresponding #GError. + Parse the given error @msg and extract the corresponding #GError. + +Since 1.26 the details will always contain the URI this refers to in an +"uri" field of type string, and (if known) the string "stream-id" it is +referring to. @@ -3019,6 +3145,35 @@ freed using gst_play_visualization_free(). + + Parses missing plugin descriptions and installer details from a +GST_PLAY_ERROR_MISSING_PLUGIN error message. + +Both arrays will have the same length, and strings at the same index +correspond to each other. + +The installer details can be passed to gst_install_plugins_sync() or +gst_install_plugins_async(). + + + %TRUE if the message contained a missing-plugin error. + + + + + A #GstMessage + + + + a %NULL-terminated array of descriptions + + + + a %NULL-terminated array of installer details + + + + Parse the given media-info-updated @msg and extract the corresponding media information @@ -3177,7 +3332,11 @@ freed using gst_play_visualization_free(). - Parse the given warning @msg and extract the corresponding #GError + Parse the given warning @msg and extract the corresponding #GError. + +Since 1.26 the details will always contain the URI this refers to in an +"uri" field of type string, and (if known) the string "stream-id" it is +referring to. @@ -3197,6 +3356,35 @@ freed using gst_play_visualization_free(). + + Parses missing plugin descriptions and installer details from a +GST_PLAY_ERROR_MISSING_PLUGIN warning message. + +Both arrays will have the same length, and strings at the same index +correspond to each other. + +The installer details can be passed to gst_install_plugins_sync() or +gst_install_plugins_async(). + + + %TRUE if the message contained a missing-plugin error. + + + + + A #GstMessage + + + + a %NULL-terminated array of descriptions + + + + a %NULL-terminated array of installer details + + + + Gets a string representing the given state. diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/play/gstplay.c b/subprojects/gst-plugins-bad/gst-libs/gst/play/gstplay.c index ef93821451..cc02f7ec3b 100644 --- a/subprojects/gst-plugins-bad/gst-libs/gst/play/gstplay.c +++ b/subprojects/gst-plugins-bad/gst-libs/gst/play/gstplay.c @@ -188,6 +188,8 @@ struct _GstPlay GstStructure *config; + GList *missing_plugin_messages; + /* Protected by lock */ gboolean seek_pending; /* Only set from main context */ GstClockTime last_seek_time; /* Only set from main context */ @@ -273,6 +275,8 @@ static void remove_seek_source (GstPlay * self); static gboolean query_position (GstPlay * self, GstClockTime * position); +static void gst_play_set_uri_details (GstPlay * self, GstStructure * details); + static void gst_play_init (GstPlay * self) { @@ -318,6 +322,7 @@ api_bus_post_message (GstPlay * self, GstPlayMessage message_type, const gchar * firstfield, ...) { GstStructure *message_data = NULL; + GstStructure *details = NULL; GstMessage *msg = NULL; va_list varargs; @@ -332,8 +337,26 @@ api_bus_post_message (GstPlay * self, GstPlayMessage message_type, msg = gst_message_new_custom (GST_MESSAGE_APPLICATION, GST_OBJECT (self), message_data); - GST_DEBUG ("Created message with payload: [ %" GST_PTR_FORMAT " ]", - message_data); + + // ERROR/WARNING messages store the details in differently named fields for + // backwards compatibility + if (message_type == GST_PLAY_MESSAGE_ERROR) { + const GValue *v = gst_structure_get_value (message_data, + GST_PLAY_MESSAGE_DATA_ERROR_DETAILS); + details = g_value_get_boxed (v); + } else if (message_type == GST_PLAY_MESSAGE_WARNING) { + const GValue *v = gst_structure_get_value (message_data, + GST_PLAY_MESSAGE_DATA_WARNING_DETAILS); + details = g_value_get_boxed (v); + } + + if (!details) + details = gst_message_writable_details (msg); + + gst_play_set_uri_details (self, details); + + GST_DEBUG_OBJECT (self, + "Created message with payload: [ %" GST_PTR_FORMAT " ]", message_data); gst_bus_post (self->api_bus, msg); } @@ -931,49 +954,43 @@ remove_ready_timeout_source (GstPlay * self) static void -on_error (GstPlay * self, GError * err, const GstStructure * details) +on_error (GstPlay * self, GError * err, GstStructure * details) { #ifndef GST_DISABLE_GST_DEBUG - GstStructure *extra_details = NULL; gchar *dot_data = NULL; #endif GST_ERROR_OBJECT (self, "Error: %s (%s, %d)", err->message, g_quark_to_string (err->domain), err->code); + if (!details) + details = gst_structure_new_static_str_empty ("error-details"); + #ifndef GST_DISABLE_GST_DEBUG - if (details != NULL) { - extra_details = gst_structure_copy (details); - } else { - extra_details = gst_structure_new_static_str_empty ("error-details"); - } if (gst_play_config_get_pipeline_dump_in_error_details (self->config)) { dot_data = gst_debug_bin_to_dot_data (GST_BIN_CAST (self->playbin), GST_DEBUG_GRAPH_SHOW_ALL); - gst_structure_set (extra_details, "pipeline-dump", G_TYPE_STRING, dot_data, - NULL); + gst_structure_set (details, "pipeline-dump", G_TYPE_STRING, dot_data, NULL); } #endif api_bus_post_message (self, GST_PLAY_MESSAGE_ERROR, GST_PLAY_MESSAGE_DATA_ERROR, G_TYPE_ERROR, err, - GST_PLAY_MESSAGE_DATA_ERROR_DETAILS, GST_TYPE_STRUCTURE, -#ifndef GST_DISABLE_GST_DEBUG - extra_details -#else - details -#endif - , NULL); + GST_PLAY_MESSAGE_DATA_ERROR_DETAILS, GST_TYPE_STRUCTURE, details, NULL); #ifndef GST_DISABLE_GST_DEBUG g_free (dot_data); - gst_structure_free (extra_details); #endif + gst_structure_free (details); g_error_free (err); remove_tick_source (self); remove_ready_timeout_source (self); + g_list_free_full (self->missing_plugin_messages, + (GDestroyNotify) gst_message_unref); + self->missing_plugin_messages = NULL; + self->target_state = GST_STATE_NULL; self->current_state = GST_STATE_NULL; self->is_live = FALSE; @@ -1013,18 +1030,109 @@ dump_dot_file (GstPlay * self, const gchar * name) g_free (full_name); } +static void +gst_play_set_missing_plugin_details (GstPlay * self, GstStructure * details) +{ + GValue missing_plugin_details = G_VALUE_INIT; + + g_value_init (&missing_plugin_details, GST_TYPE_ARRAY); + + for (GList * l = self->missing_plugin_messages; l; l = l->next) { + GstMessage *missing_plugin_message = l->data; + GValue v = G_VALUE_INIT; + GstStructure *s; + gchar *description, *installer_details; + + description = + gst_missing_plugin_message_get_description (missing_plugin_message); + installer_details = + gst_missing_plugin_message_get_installer_detail + (missing_plugin_message); + + s = gst_structure_new_static_str ("missing-plugin-detail", "description", + G_TYPE_STRING, description, "installer-details", G_TYPE_STRING, + installer_details, NULL); + g_value_init (&v, GST_TYPE_STRUCTURE); + g_value_take_boxed (&v, s); + gst_value_array_append_and_take_value (&missing_plugin_details, &v); + + g_free (description); + g_free (installer_details); + } + + + gst_structure_take_value_static_str (details, "missing-plugin-details", + &missing_plugin_details); +} + +static void +gst_play_set_uri_details (GstPlay * self, GstStructure * details) +{ + if (!gst_structure_has_field (details, "uri")) { + gchar *uri; + + g_object_get (self->playbin, "current-uri", &uri, NULL); + if (!uri) + g_object_get (self->playbin, "uri", &uri, NULL); + if (!uri) + uri = g_strdup (self->uri); + gst_structure_set (details, "uri", G_TYPE_STRING, uri, NULL); + g_free (uri); + } +} + +static void +gst_play_set_stream_id_details (GstPlay * self, GstMessage * msg, + GstStructure * details) +{ + if (!gst_structure_has_field (details, "stream-id")) { + GstPad *pad = NULL; + gchar *stream_id; + + if (GST_IS_ELEMENT (GST_MESSAGE_SRC (msg))) { + GstElement *element = GST_ELEMENT (GST_MESSAGE_SRC (msg)); + + // If the message src has only one sinkpad (or is a source element) + // grab the stream id from there + GST_OBJECT_LOCK (element); + if (element->numsinkpads == 1) { + pad = gst_object_ref (element->sinkpads->data); + } else if (element->numsinkpads == 0 && element->numsrcpads > 0) { + pad = gst_object_ref (element->srcpads->data); + } + GST_OBJECT_UNLOCK (element); + } else if (GST_IS_PAD (GST_MESSAGE_SRC (msg))) { + pad = gst_object_ref (GST_PAD (GST_MESSAGE_SRC (msg))); + } + + if (pad) { + stream_id = gst_pad_get_stream_id (pad); + if (stream_id) + gst_structure_set (details, "stream-id", G_TYPE_STRING, stream_id, + NULL); + g_free (stream_id); + gst_object_unref (pad); + } + } +} + static void error_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data) { GstPlay *self = GST_PLAY (user_data); GError *err, *play_err; gchar *name, *debug, *message, *full_message; - const GstStructure *details = NULL; + GstStructure *details = NULL; + GstPlayError play_error = GST_PLAY_ERROR_FAILED; dump_dot_file (self, "error"); gst_message_parse_error (msg, &err, &debug); - gst_message_parse_error_details (msg, &details); + gst_message_parse_error_details (msg, (const GstStructure **) &details); + if (details) + details = gst_structure_copy (details); + else + details = gst_structure_new_static_str_empty ("message-details"); name = gst_object_get_path_string (msg->src); message = gst_error_get_message (err->domain, err->code); @@ -1042,8 +1150,15 @@ error_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data) if (debug != NULL) GST_ERROR_OBJECT (self, "Additional debug info: %s", debug); - play_err = - g_error_new_literal (GST_PLAY_ERROR, GST_PLAY_ERROR_FAILED, full_message); + gst_play_set_stream_id_details (self, msg, details); + if (g_error_matches (err, GST_CORE_ERROR, GST_CORE_ERROR_MISSING_PLUGIN) || + g_error_matches (err, GST_STREAM_ERROR, + GST_STREAM_ERROR_CODEC_NOT_FOUND)) { + play_error = GST_PLAY_ERROR_MISSING_PLUGIN; + gst_play_set_missing_plugin_details (self, details); + } + + play_err = g_error_new_literal (GST_PLAY_ERROR, play_error, full_message); on_error (self, play_err, details); g_clear_error (&err); @@ -1059,12 +1174,17 @@ warning_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data) GstPlay *self = GST_PLAY (user_data); GError *err, *play_err; gchar *name, *debug, *message, *full_message; - const GstStructure *details = NULL; + GstStructure *details = NULL; + GstPlayError play_error = GST_PLAY_ERROR_FAILED; dump_dot_file (self, "warning"); gst_message_parse_warning (msg, &err, &debug); - gst_message_parse_warning_details (msg, &details); + gst_message_parse_warning_details (msg, (const GstStructure **) &details); + if (details) + details = gst_structure_copy (details); + else + details = gst_structure_new_static_str_empty ("message-details"); name = gst_object_get_path_string (msg->src); message = gst_error_get_message (err->domain, err->code); @@ -1082,22 +1202,24 @@ warning_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data) if (debug != NULL) GST_WARNING_OBJECT (self, "Additional debug info: %s", debug); - play_err = - g_error_new_literal (GST_PLAY_ERROR, GST_PLAY_ERROR_FAILED, full_message); + gst_play_set_stream_id_details (self, msg, details); + if (g_error_matches (err, GST_CORE_ERROR, GST_CORE_ERROR_MISSING_PLUGIN) || + g_error_matches (err, GST_STREAM_ERROR, + GST_STREAM_ERROR_CODEC_NOT_FOUND)) { + play_error = GST_PLAY_ERROR_MISSING_PLUGIN; + gst_play_set_missing_plugin_details (self, details); + } + + play_err = g_error_new_literal (GST_PLAY_ERROR, play_error, full_message); GST_WARNING_OBJECT (self, "Warning: %s (%s, %d)", err->message, g_quark_to_string (err->domain), err->code); - if (details != NULL) { - api_bus_post_message (self, GST_PLAY_MESSAGE_WARNING, - GST_PLAY_MESSAGE_DATA_WARNING, G_TYPE_ERROR, play_err, - GST_PLAY_MESSAGE_DATA_WARNING_DETAILS, GST_TYPE_STRUCTURE, details, - NULL); - } else { - api_bus_post_message (self, GST_PLAY_MESSAGE_WARNING, - GST_PLAY_MESSAGE_DATA_WARNING, G_TYPE_ERROR, play_err, NULL); - } + api_bus_post_message (self, GST_PLAY_MESSAGE_WARNING, + GST_PLAY_MESSAGE_DATA_WARNING, G_TYPE_ERROR, play_err, + GST_PLAY_MESSAGE_DATA_WARNING_DETAILS, GST_TYPE_STRUCTURE, details, NULL); + gst_structure_free (details); g_clear_error (&play_err); g_clear_error (&err); g_free (debug); @@ -1606,6 +1728,9 @@ element_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data) else if (target_state == GST_STATE_PLAYING) gst_play_play_internal (self); } + } else if (gst_is_missing_plugin_message (msg)) { + self->missing_plugin_messages = + g_list_prepend (self->missing_plugin_messages, gst_message_ref (msg)); } } @@ -2472,6 +2597,10 @@ gst_play_main (gpointer data) remove_tick_source (self); remove_ready_timeout_source (self); + g_list_free_full (self->missing_plugin_messages, + (GDestroyNotify) gst_message_unref); + self->missing_plugin_messages = NULL; + g_mutex_lock (&self->lock); if (self->media_info) { g_object_unref (self->media_info); @@ -2718,6 +2847,10 @@ gst_play_stop_internal (GstPlay * self, gboolean transient) tick_cb (self); remove_tick_source (self); + g_list_free_full (self->missing_plugin_messages, + (GDestroyNotify) gst_message_unref); + self->missing_plugin_messages = NULL; + add_ready_timeout_source (self); self->target_state = GST_STATE_NULL; @@ -4337,6 +4470,8 @@ gst_play_error_get_name (GstPlayError error) switch (error) { case GST_PLAY_ERROR_FAILED: return "failed"; + case GST_PLAY_ERROR_MISSING_PLUGIN: + return "missing-plugin"; } g_assert_not_reached (); @@ -4732,6 +4867,94 @@ gst_play_message_parse_type (GstMessage * msg, GstPlayMessage * type) type, NULL); } +/** + * gst_play_message_get_uri: + * @msg: A #GstMessage + * + * Reads the URI the play message @msg applies to. + * + * Returns: (transfer none): The URI this message applies to + * + * Since: 1.26 + */ +const gchar * +gst_play_message_get_uri (GstMessage * msg) +{ + const GstStructure *details = NULL; + const gchar *uri; + GstPlayMessage msg_type; + + g_return_val_if_fail (gst_play_is_play_message (msg), NULL); + + gst_play_message_parse_type (msg, &msg_type); + + // ERROR/WARNING messages store the details in differently named fields for + // backwards compatibility + if (msg_type == GST_PLAY_MESSAGE_ERROR) { + const GstStructure *s = gst_message_get_structure (msg); + const GValue *v = + gst_structure_get_value (s, GST_PLAY_MESSAGE_DATA_ERROR_DETAILS); + details = g_value_get_boxed (v); + } else if (msg_type == GST_PLAY_MESSAGE_WARNING) { + const GstStructure *s = gst_message_get_structure (msg); + const GValue *v = + gst_structure_get_value (s, GST_PLAY_MESSAGE_DATA_WARNING_DETAILS); + details = g_value_get_boxed (v); + } + + if (!details) + details = gst_message_get_details (msg); + + g_return_val_if_fail (details, NULL); + uri = gst_structure_get_string (details, "uri"); + g_return_val_if_fail (uri, NULL); + + return uri; +} + +/** + * gst_play_message_get_stream_id: + * @msg: A #GstMessage + * + * Reads the stream ID the play message @msg applies to, if any. + * + * Returns: (transfer none) (nullable): The stream ID this message applies to + * + * Since: 1.26 + */ +const gchar * +gst_play_message_get_stream_id (GstMessage * msg) +{ + const GstStructure *details = NULL; + const gchar *stream_id; + GstPlayMessage msg_type; + + g_return_val_if_fail (gst_play_is_play_message (msg), NULL); + + gst_play_message_parse_type (msg, &msg_type); + + // ERROR/WARNING messages store the details in differently named fields for + // backwards compatibility + if (msg_type == GST_PLAY_MESSAGE_ERROR) { + const GstStructure *s = gst_message_get_structure (msg); + const GValue *v = + gst_structure_get_value (s, GST_PLAY_MESSAGE_DATA_ERROR_DETAILS); + details = g_value_get_boxed (v); + } else if (msg_type == GST_PLAY_MESSAGE_WARNING) { + const GstStructure *s = gst_message_get_structure (msg); + const GValue *v = + gst_structure_get_value (s, GST_PLAY_MESSAGE_DATA_WARNING_DETAILS); + details = g_value_get_boxed (v); + } + + if (!details) + details = gst_message_get_details (msg); + g_return_val_if_fail (details, NULL); + stream_id = gst_structure_get_string (details, "stream-id"); + + return stream_id; +} + /** * gst_play_message_parse_uri_loaded: * @msg: A #GstMessage @@ -4857,6 +5080,10 @@ gst_play_message_parse_buffering_percent (GstMessage * msg, guint * percent) * * Parse the given error @msg and extract the corresponding #GError. * + * Since 1.26 the details will always contain the URI this refers to in an + * "uri" field of type string, and (if known) the string "stream-id" it is + * referring to. + * * Since: 1.20 */ void @@ -4869,13 +5096,118 @@ gst_play_message_parse_error (GstMessage * msg, GError ** error, GST_PLAY_MESSAGE_DATA_ERROR_DETAILS, GST_TYPE_STRUCTURE, details); } +static gboolean +gst_play_message_parse_missing_plugin (GstMessage * msg, + GstPlayMessage msg_type, gchar *** descriptions, + gchar *** installer_details) +{ + const GError *err; + const GValue *v, *details_array; + const GstStructure *s, *details; + guint n_details; + + if (descriptions) + *descriptions = NULL; + if (installer_details) + *installer_details = NULL; + + s = gst_message_get_structure (msg); + + v = gst_structure_get_value (s, + msg_type == + GST_PLAY_MESSAGE_ERROR ? GST_PLAY_MESSAGE_DATA_ERROR : + GST_PLAY_MESSAGE_DATA_WARNING); + if (!v) + return FALSE; + err = g_value_get_boxed (v); + if (!err) + return FALSE; + + if (!g_error_matches (err, GST_PLAY_ERROR, GST_PLAY_ERROR_MISSING_PLUGIN)) + return FALSE; + + v = gst_structure_get_value (s, + msg_type == + GST_PLAY_MESSAGE_ERROR ? GST_PLAY_MESSAGE_DATA_ERROR_DETAILS : + GST_PLAY_MESSAGE_DATA_WARNING_DETAILS); + if (!v) + return FALSE; + details = g_value_get_boxed (v); + if (!details) + return FALSE; + + details_array = gst_structure_get_value (details, "missing-plugin-details"); + + n_details = gst_value_array_get_size (details_array); + if (descriptions) + *descriptions = g_new0 (gchar *, n_details + 1); + if (installer_details) + *installer_details = g_new0 (gchar *, n_details + 1); + + for (guint i = 0; i < n_details; i++) { + const GValue *details_v = gst_value_array_get_value (details_array, i); + const GstStructure *details_s = g_value_get_boxed (details_v); + gchar *str; + + if (descriptions) { + gst_structure_get (details_s, "description", G_TYPE_STRING, &str, NULL); + (*descriptions)[i] = str; + } + + if (installer_details) { + gst_structure_get (details_s, "installer-details", G_TYPE_STRING, &str, + NULL); + (*installer_details)[i] = str; + } + } + + return TRUE; + +} + +/** + * gst_play_message_parse_error_missing_plugin: + * @msg: A #GstMessage + * @descriptions: (out) (optional) (transfer full): a %NULL-terminated array of descriptions + * @installer_details: (out) (optional) (nullable) (transfer full): a %NULL-terminated array of installer details + * + * Parses missing plugin descriptions and installer details from a + * GST_PLAY_ERROR_MISSING_PLUGIN error message. + * + * Both arrays will have the same length, and strings at the same index + * correspond to each other. + * + * The installer details can be passed to gst_install_plugins_sync() or + * gst_install_plugins_async(). + * + * Returns: %TRUE if the message contained a missing-plugin error. + * + * Since: 1.26 + */ +gboolean +gst_play_message_parse_error_missing_plugin (GstMessage * msg, + gchar *** descriptions, gchar *** installer_details) +{ + GstPlayMessage msg_type; + + gst_play_message_parse_type (msg, &msg_type); + g_return_val_if_fail (msg_type == GST_PLAY_MESSAGE_ERROR, FALSE); + + return gst_play_message_parse_missing_plugin (msg, msg_type, descriptions, + installer_details); +} + /** * gst_play_message_parse_warning: * @msg: A #GstMessage * @error: (out) (optional) (transfer full): the resulting warning * @details: (out) (optional) (nullable) (transfer full): A #GstStructure containing additional details about the warning * - * Parse the given warning @msg and extract the corresponding #GError + * Parse the given warning @msg and extract the corresponding #GError. + * + * Since 1.26 the details will always contain the URI this refers to in an + * "uri" field of type string, and (if known) the string "stream-id" it is + * referring to. * * Since: 1.20 */ @@ -4889,6 +5221,38 @@ gst_play_message_parse_warning (GstMessage * msg, GError ** error, GST_PLAY_MESSAGE_DATA_WARNING_DETAILS, GST_TYPE_STRUCTURE, details); } +/** + * gst_play_message_parse_warning_missing_plugin: + * @msg: A #GstMessage + * @descriptions: (out) (optional) (transfer full): a %NULL-terminated array of descriptions + * @installer_details: (out) (optional) (nullable) (transfer full): a %NULL-terminated array of installer details + * + * Parses missing plugin descriptions and installer details from a + * GST_PLAY_ERROR_MISSING_PLUGIN warning message. + * + * Both arrays will have the same length, and strings at the same index + * correspond to each other. + * + * The installer details can be passed to gst_install_plugins_sync() or + * gst_install_plugins_async(). + * + * Returns: %TRUE if the message contained a missing-plugin error. + * + * Since: 1.26 + */ +gboolean +gst_play_message_parse_warning_missing_plugin (GstMessage * msg, + gchar *** descriptions, gchar *** installer_details) +{ + GstPlayMessage msg_type; + + gst_play_message_parse_type (msg, &msg_type); + g_return_val_if_fail (msg_type == GST_PLAY_MESSAGE_WARNING, FALSE); + + return gst_play_message_parse_missing_plugin (msg, msg_type, descriptions, + installer_details); +} + /** * gst_play_message_parse_video_dimensions_changed: * @msg: A #GstMessage diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/play/gstplay.h b/subprojects/gst-plugins-bad/gst-libs/gst/play/gstplay.h index 9157414cf4..b82a41eae3 100644 --- a/subprojects/gst-plugins-bad/gst-libs/gst/play/gstplay.h +++ b/subprojects/gst-plugins-bad/gst-libs/gst/play/gstplay.h @@ -137,11 +137,28 @@ GType gst_play_error_get_type (void); /** * GstPlayError: * @GST_PLAY_ERROR_FAILED: generic error. + * @GST_PLAY_ERROR_MISSING_PLUGIN: playback requires additional plugins (Since: 1.26). * * Since: 1.20 */ typedef enum { - GST_PLAY_ERROR_FAILED = 0 + GST_PLAY_ERROR_FAILED = 0, + + /** + * GST_PLAY_ERROR_MISSING_PLUGIN: + * + * Playback requires additional plugins. Information about the missing + * plugins can be retrieved from the message details. + * + * The details will contain the the missing plugin details in a field of + * type %GstArray named "missing-plugin-details". This array will contain + * %GstStructure with string "description" and a string "installer-details". + * + * The "installer-details" can be passed to gst_install_plugins_async(). + * + * Since: 1.26 + */ + GST_PLAY_ERROR_MISSING_PLUGIN } GstPlayError; GST_PLAY_API @@ -431,6 +448,12 @@ gboolean gst_play_is_play_message (GstMessage *ms GST_PLAY_API void gst_play_message_parse_type (GstMessage *msg, GstPlayMessage *type); +GST_PLAY_API +const gchar * gst_play_message_get_uri (GstMessage *msg); + +GST_PLAY_API +const gchar * gst_play_message_get_stream_id (GstMessage *msg); + GST_PLAY_API void gst_play_message_parse_uri_loaded (GstMessage *msg, gchar **uri); @@ -455,9 +478,15 @@ void gst_play_message_parse_buffering (GstMessage *ms GST_PLAY_API void gst_play_message_parse_error (GstMessage *msg, GError **error, GstStructure **details); +GST_PLAY_API +gboolean gst_play_message_parse_error_missing_plugin (GstMessage *msg, gchar *** descriptions, gchar *** installer_details); + GST_PLAY_API void gst_play_message_parse_warning (GstMessage *msg, GError **error, GstStructure **details); +GST_PLAY_API +gboolean gst_play_message_parse_warning_missing_plugin (GstMessage *msg, gchar *** descriptions, gchar *** installer_details); + GST_PLAY_API void gst_play_message_parse_video_dimensions_changed (GstMessage *msg, guint *width, guint *height);