decodebin: Include information from the error messages of tried but failed elements in the missing plugin errors

This commit is contained in:
Sebastian Dröge 2014-08-25 20:59:40 +03:00
parent 22a138b716
commit a5cf0a4572
2 changed files with 184 additions and 22 deletions

View file

@ -183,6 +183,7 @@ struct _GstDecodeBin
gboolean expose_allstreams; /* Whether to expose unknow type streams or not */
GList *filtered; /* elements for which error messages are filtered */
GList *filtered_errors; /* filtered error messages */
GList *buffering_status; /* element currently buffering messages */
};
@ -445,6 +446,7 @@ struct _GstDecodeChain
e.g. no suitable decoder could be found
e.g. stream got EOS without buffers
*/
gchar *deadend_details;
GstCaps *endcaps; /* Caps that were used when linking to the endpad
or that resulted in the deadend
*/
@ -462,7 +464,8 @@ static GstDecodeGroup *gst_decode_group_new (GstDecodeBin * dbin,
GstDecodeChain * chain);
static gboolean gst_decode_chain_is_complete (GstDecodeChain * chain);
static gboolean gst_decode_chain_expose (GstDecodeChain * chain,
GList ** endpads, gboolean * missing_plugin, gboolean * last_group);
GList ** endpads, gboolean * missing_plugin,
GString * missing_plugin_details, gboolean * last_group);
static gboolean gst_decode_chain_is_drained (GstDecodeChain * chain);
static gboolean gst_decode_chain_reset_buffering (GstDecodeChain * chain);
static gboolean gst_decode_group_is_complete (GstDecodeGroup * group);
@ -1419,7 +1422,7 @@ static gboolean is_adaptive_demuxer_element (GstElement * srcelement);
static gboolean connect_pad (GstDecodeBin * dbin, GstElement * src,
GstDecodePad * dpad, GstPad * pad, GstCaps * caps, GValueArray * factories,
GstDecodeChain * chain);
GstDecodeChain * chain, gchar ** deadend_details);
static GList *connect_element (GstDecodeBin * dbin, GstDecodeElement * delem,
GstDecodeChain * chain);
static void expose_pad (GstDecodeBin * dbin, GstElement * src,
@ -1488,6 +1491,7 @@ analyze_new_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad,
const gchar *classification;
gboolean is_parser_converter = FALSE;
gboolean res;
gchar *deadend_details = NULL;
GST_DEBUG_OBJECT (dbin, "Pad %s:%s caps:%" GST_PTR_FORMAT,
GST_DEBUG_PAD_NAME (pad), caps);
@ -1756,7 +1760,9 @@ analyze_new_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad,
/* 1.h else continue autoplugging something from the list. */
GST_LOG_OBJECT (pad, "Let's continue discovery on this pad");
res = connect_pad (dbin, src, dpad, pad, caps, factories, chain);
res =
connect_pad (dbin, src, dpad, pad, caps, factories, chain,
&deadend_details);
/* Need to unref the capsfilter srcpad here if
* we inserted a capsfilter */
@ -1804,6 +1810,7 @@ unknown_type:
{
GST_LOG_OBJECT (pad, "Unknown type, posting message and firing signal");
chain->deadend_details = deadend_details;
chain->deadend = TRUE;
chain->endcaps = caps;
gst_object_replace ((GstObject **) & chain->current_pad, NULL);
@ -1880,10 +1887,31 @@ add_error_filter (GstDecodeBin * dbin, GstElement * element)
}
static void
remove_error_filter (GstDecodeBin * dbin, GstElement * element)
remove_error_filter (GstDecodeBin * dbin, GstElement * element,
GstMessage ** error)
{
GList *l;
GST_OBJECT_LOCK (dbin);
dbin->filtered = g_list_remove (dbin->filtered, element);
if (error)
*error = NULL;
l = dbin->filtered_errors;
while (l) {
GstMessage *msg = l->data;
if (GST_MESSAGE_SRC (msg) == GST_OBJECT_CAST (element)) {
/* Get the last error of this element, i.e. the earliest */
if (error)
gst_message_replace (error, msg);
gst_message_unref (msg);
l = g_list_delete_link (dbin->filtered_errors, l);
} else {
l = l->next;
}
}
GST_OBJECT_UNLOCK (dbin);
}
@ -1921,6 +1949,28 @@ send_sticky_events (GstDecodeBin * dbin, GstPad * pad)
return data.ret;
}
static gchar *
error_message_to_string (GstMessage * msg)
{
GError *err;
gchar *debug, *message, *full_message;
gst_message_parse_error (msg, &err, &debug);
message = gst_error_get_message (err->domain, err->code);
if (debug)
full_message = g_strdup_printf ("%s\n%s\n%s", message, err->message, debug);
else
full_message = g_strdup_printf ("%s\n%s", message, err->message);
g_free (message);
g_free (debug);
g_clear_error (&err);
return full_message;
}
/* connect_pad:
*
* Try to connect the given pad to an element created from one of the factories,
@ -1934,11 +1984,12 @@ send_sticky_events (GstDecodeBin * dbin, GstPad * pad)
static gboolean
connect_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad,
GstPad * pad, GstCaps * caps, GValueArray * factories,
GstDecodeChain * chain)
GstDecodeChain * chain, gchar ** deadend_details)
{
gboolean res = FALSE;
GstPad *mqpad = NULL;
gboolean is_demuxer = chain->parent && !chain->elements; /* First pad after the demuxer */
GString *error_details = NULL;
g_return_val_if_fail (factories != NULL, FALSE);
g_return_val_if_fail (factories->n_values > 0, FALSE);
@ -1961,6 +2012,8 @@ connect_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad,
decode_pad_set_target (dpad, pad);
}
error_details = g_string_new ("");
/* 2. Try to create an element and link to it */
while (factories->n_values > 0) {
GstAutoplugSelectResult ret;
@ -2103,6 +2156,9 @@ connect_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad,
if ((element = gst_element_factory_create (factory, NULL)) == NULL) {
GST_WARNING_OBJECT (dbin, "Could not create an element from %s",
gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
g_string_append_printf (error_details,
"Could not create an element from %s\n",
gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
continue;
}
@ -2117,7 +2173,9 @@ connect_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad,
if (!(gst_bin_add (GST_BIN_CAST (dbin), element))) {
GST_WARNING_OBJECT (dbin, "Couldn't add %s to the bin",
GST_ELEMENT_NAME (element));
remove_error_filter (dbin, element);
remove_error_filter (dbin, element, NULL);
g_string_append_printf (error_details, "Couldn't add %s to the bin\n",
GST_ELEMENT_NAME (element));
gst_object_unref (element);
continue;
}
@ -2126,7 +2184,9 @@ connect_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad,
if (!(sinkpad = find_sink_pad (element))) {
GST_WARNING_OBJECT (dbin, "Element %s doesn't have a sink pad",
GST_ELEMENT_NAME (element));
remove_error_filter (dbin, element);
remove_error_filter (dbin, element, NULL);
g_string_append_printf (error_details,
"Element %s doesn't have a sink pad", GST_ELEMENT_NAME (element));
gst_bin_remove (GST_BIN (dbin), element);
continue;
}
@ -2135,7 +2195,9 @@ connect_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad,
if ((gst_pad_link (pad, sinkpad)) != GST_PAD_LINK_OK) {
GST_WARNING_OBJECT (dbin, "Link failed on pad %s:%s",
GST_DEBUG_PAD_NAME (sinkpad));
remove_error_filter (dbin, element);
remove_error_filter (dbin, element, NULL);
g_string_append_printf (error_details, "Link failed on pad %s:%s",
GST_DEBUG_PAD_NAME (sinkpad));
gst_object_unref (sinkpad);
gst_bin_remove (GST_BIN (dbin), element);
continue;
@ -2144,9 +2206,22 @@ connect_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad,
/* ... activate it ... */
if ((gst_element_set_state (element,
GST_STATE_READY)) == GST_STATE_CHANGE_FAILURE) {
GstMessage *error_msg;
GST_WARNING_OBJECT (dbin, "Couldn't set %s to READY",
GST_ELEMENT_NAME (element));
remove_error_filter (dbin, element);
remove_error_filter (dbin, element, &error_msg);
if (error_msg) {
gchar *error_string = error_message_to_string (error_msg);
g_string_append_printf (error_details, "Couldn't set %s to READY:\n%s",
GST_ELEMENT_NAME (element), error_string);
gst_message_unref (error_msg);
g_free (error_string);
} else {
g_string_append_printf (error_details, "Couldn't set %s to READY",
GST_ELEMENT_NAME (element));
}
gst_object_unref (sinkpad);
gst_bin_remove (GST_BIN (dbin), element);
continue;
@ -2155,10 +2230,24 @@ connect_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad,
/* check if we still accept the caps on the pad after setting
* the element to READY */
if (!gst_pad_query_accept_caps (sinkpad, caps)) {
GstMessage *error_msg;
GST_WARNING_OBJECT (dbin, "Element %s does not accept caps",
GST_ELEMENT_NAME (element));
remove_error_filter (dbin, element);
remove_error_filter (dbin, element, &error_msg);
if (error_msg) {
gchar *error_string = error_message_to_string (error_msg);
g_string_append_printf (error_details,
"Element %s does not accept caps:\n%s", GST_ELEMENT_NAME (element),
error_string);
gst_message_unref (error_msg);
g_free (error_string);
} else {
g_string_append_printf (error_details,
"Element %s does not accept caps", GST_ELEMENT_NAME (element));
}
gst_element_set_state (element, GST_STATE_NULL);
gst_object_unref (sinkpad);
@ -2281,6 +2370,7 @@ connect_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad,
!send_sticky_events (dbin, pad)) {
GstDecodeElement *dtmp = NULL;
GstElement *tmp = NULL;
GstMessage *error_msg;
GST_WARNING_OBJECT (dbin, "Couldn't set %s to PAUSED",
GST_ELEMENT_NAME (element));
@ -2289,7 +2379,18 @@ connect_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad,
g_list_free (to_connect);
to_connect = NULL;
remove_error_filter (dbin, element);
remove_error_filter (dbin, element, &error_msg);
if (error_msg) {
gchar *error_string = error_message_to_string (error_msg);
g_string_append_printf (error_details, "Couldn't set %s to PAUSED:\n%s",
GST_ELEMENT_NAME (element), error_string);
gst_message_unref (error_msg);
g_free (error_string);
} else {
g_string_append_printf (error_details, "Couldn't set %s to PAUSED",
GST_ELEMENT_NAME (element));
}
/* Remove all elements in this chain that were just added. No
* other thread could've added elements in the meantime */
@ -2348,7 +2449,7 @@ connect_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad,
/* Remove error filter now, from now on we can't gracefully
* handle errors of the element anymore */
remove_error_filter (dbin, element);
remove_error_filter (dbin, element, NULL);
/* Now let the bin handle the state */
gst_element_set_locked_state (element, FALSE);
@ -2386,6 +2487,11 @@ beach:
if (mqpad)
gst_object_unref (mqpad);
if (error_details)
*deadend_details = g_string_free (error_details, (error_details->len == 0));
else
*deadend_details = NULL;
return res;
}
@ -3091,6 +3197,8 @@ gst_decode_chain_free_internal (GstDecodeChain * chain, gboolean hide)
gst_caps_unref (chain->endcaps);
chain->endcaps = NULL;
}
g_free (chain->deadend_details);
chain->deadend_details = NULL;
GST_DEBUG_OBJECT (chain->dbin, "%s chain %p", (hide ? "Hidden" : "Freed"),
chain);
@ -4022,6 +4130,7 @@ gst_decode_bin_expose (GstDecodeBin * dbin)
{
GList *tmp, *endpads;
gboolean missing_plugin;
GString *missing_plugin_details;
gboolean already_exposed;
gboolean last_group;
@ -4031,6 +4140,8 @@ retry:
already_exposed = TRUE;
last_group = TRUE;
missing_plugin_details = g_string_new ("");
GST_DEBUG_OBJECT (dbin, "Exposing currently active chains/groups");
/* Don't expose if we're currently shutting down */
@ -4044,21 +4155,31 @@ retry:
/* Get the pads that we're going to expose and mark things as exposed */
if (!gst_decode_chain_expose (dbin->decode_chain, &endpads, &missing_plugin,
&last_group)) {
missing_plugin_details, &last_group)) {
g_list_foreach (endpads, (GFunc) gst_object_unref, NULL);
g_list_free (endpads);
g_string_free (missing_plugin_details, TRUE);
GST_ERROR_OBJECT (dbin, "Broken chain/group tree");
g_return_val_if_reached (FALSE);
return FALSE;
}
if (endpads == NULL) {
if (missing_plugin) {
GST_WARNING_OBJECT (dbin, "No suitable plugins found");
GST_ELEMENT_ERROR (dbin, CORE, MISSING_PLUGIN, (NULL),
("no suitable plugins found"));
if (missing_plugin_details->len > 0) {
gchar *details = g_string_free (missing_plugin_details, FALSE);
GST_ELEMENT_ERROR (dbin, CORE, MISSING_PLUGIN, (NULL),
("no suitable plugins found:\n%s", details));
g_free (details);
} else {
g_string_free (missing_plugin_details, TRUE);
GST_ELEMENT_ERROR (dbin, CORE, MISSING_PLUGIN, (NULL),
("no suitable plugins found"));
}
} else {
/* in this case, the stream ended without buffers,
* just post a warning */
g_string_free (missing_plugin_details, TRUE);
GST_WARNING_OBJECT (dbin, "All streams finished without buffers. "
"Last group: %d", last_group);
if (last_group) {
@ -4081,6 +4202,8 @@ retry:
return FALSE;
}
g_string_free (missing_plugin_details, TRUE);
/* Check if this was called when everything was exposed already */
for (tmp = endpads; tmp && already_exposed; tmp = tmp->next) {
GstDecodePad *dpad = tmp->data;
@ -4181,15 +4304,21 @@ retry:
*/
static gboolean
gst_decode_chain_expose (GstDecodeChain * chain, GList ** endpads,
gboolean * missing_plugin, gboolean * last_group)
gboolean * missing_plugin, GString * missing_plugin_details,
gboolean * last_group)
{
GstDecodeGroup *group;
GList *l;
GstDecodeBin *dbin;
if (chain->deadend) {
if (chain->endcaps)
if (chain->endcaps) {
if (chain->deadend_details) {
g_string_append (missing_plugin_details, chain->deadend_details);
g_string_append_c (missing_plugin_details, '\n');
}
*missing_plugin = TRUE;
}
return TRUE;
}
@ -4223,7 +4352,7 @@ gst_decode_chain_expose (GstDecodeChain * chain, GList ** endpads,
GstDecodeChain *childchain = l->data;
if (!gst_decode_chain_expose (childchain, endpads, missing_plugin,
last_group))
missing_plugin_details, last_group))
return FALSE;
}
@ -4701,6 +4830,9 @@ gst_decode_bin_handle_message (GstBin * bin, GstMessage * msg)
if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
GST_OBJECT_LOCK (dbin);
drop = (g_list_find (dbin->filtered, GST_MESSAGE_SRC (msg)) != NULL);
if (drop)
dbin->filtered_errors =
g_list_prepend (dbin->filtered_errors, gst_message_ref (msg));
GST_OBJECT_UNLOCK (dbin);
} else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_BUFFERING) {
gint perc, msg_perc;

View file

@ -110,6 +110,7 @@ struct _GstURIDecodeBin
guint src_np_sig_id; /* new-pad signal id */
guint src_nmp_sig_id; /* no-more-pads signal id */
gint pending;
GList *missing_plugin_errors;
gboolean async_pending; /* async-start has been emitted */
@ -986,8 +987,29 @@ done:
* decodebins had missing plugins for all of their streams!
*/
if (!decoder->streams || g_hash_table_size (decoder->streams) == 0) {
GST_ELEMENT_ERROR (decoder, CORE, MISSING_PLUGIN, (NULL),
("no suitable plugins found"));
if (decoder->missing_plugin_errors) {
GString *str = g_string_new ("");
GList *l;
for (l = decoder->missing_plugin_errors; l; l = l->next) {
GstMessage *msg = l->data;
gchar *debug;
gst_message_parse_error (msg, NULL, &debug);
g_string_append (str, debug);
g_free (debug);
gst_message_unref (msg);
}
g_list_free (decoder->missing_plugin_errors);
decoder->missing_plugin_errors = NULL;
GST_ELEMENT_ERROR (decoder, CORE, MISSING_PLUGIN, (NULL),
("no suitable plugins found:\n%s", str->str));
g_string_free (str, TRUE);
} else {
GST_ELEMENT_ERROR (decoder, CORE, MISSING_PLUGIN, (NULL),
("no suitable plugins found"));
}
} else {
gst_element_no_more_pads (GST_ELEMENT_CAST (decoder));
}
@ -2400,6 +2422,8 @@ handle_redirect_message (GstURIDecodeBin * dec, GstMessage * msg)
static void
handle_message (GstBin * bin, GstMessage * msg)
{
GstURIDecodeBin *dec = GST_URI_DECODE_BIN (bin);
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_ELEMENT:{
if (gst_message_has_name (msg, "redirect")) {
@ -2407,7 +2431,7 @@ handle_message (GstBin * bin, GstMessage * msg)
* the user of this element as it can in most cases just pick the first item
* of the sorted list as a good redirection candidate. It can of course
* choose something else from the list if it has a better way. */
msg = handle_redirect_message (GST_URI_DECODE_BIN (bin), msg);
msg = handle_redirect_message (dec, msg);
}
break;
}
@ -2422,6 +2446,9 @@ handle_message (GstBin * bin, GstMessage * msg)
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)) {
dec->missing_plugin_errors =
g_list_prepend (dec->missing_plugin_errors, gst_message_ref (msg));
no_more_pads_full (GST_ELEMENT (GST_MESSAGE_SRC (msg)), FALSE,
GST_URI_DECODE_BIN (bin));
gst_message_unref (msg);
@ -2767,6 +2794,9 @@ gst_uri_decode_bin_change_state (GstElement * element,
remove_decoders (decoder, FALSE);
remove_source (decoder);
do_async_done (decoder);
g_list_free_full (decoder->missing_plugin_errors,
(GDestroyNotify) gst_message_unref);
decoder->missing_plugin_errors = NULL;
break;
case GST_STATE_CHANGE_READY_TO_NULL:
GST_DEBUG ("ready to null");