From 686ad73bfebc79864a9cfcac05415d15f69f9e11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Tue, 9 Jan 2007 14:33:24 +0000 Subject: [PATCH] gst/playback/: Post missing-plugin messages on the bus for missing sources and missing decoders/demuxers/depayloaders... Original commit message from CVS: * gst/playback/Makefile.am: * gst/playback/gstplaybasebin.c: (string_arr_has_str), (unknown_type), (setup_subtitle), (gen_source_element): * gst/playback/gstplaybin.c: (plugin_init): Post missing-plugin messages on the bus for missing sources and missing decoders/demuxers/depayloaders; fix error code used when we're missing an URI handler source; for media types that we are not handling on purpose at the moment, don't print "don't know how to handle xyz" messages to the terminal or post missing-plugin messages on the bus. * tests/check/elements/playbin.c: (create_playbin), (GST_START_TEST), (gst_codec_src_uri_get_type), (gst_codec_src_uri_get_protocols), (gst_codec_src_uri_get_uri), (gst_codec_src_uri_set_uri), (gst_codec_src_uri_handler_init), (gst_codec_src_init_type), (gst_codec_src_base_init), (gst_codec_src_create), (gst_codec_src_class_init), (gst_codec_src_init), (plugin_init), (playbin_suite): Add some tests for the missing-plugin stuff. --- ChangeLog | 22 +++ gst/playback/Makefile.am | 4 +- gst/playback/gstplaybasebin.c | 59 ++++++-- gst/playback/gstplaybin.c | 3 + tests/check/elements/playbin.c | 243 +++++++++++++++++++++++++++++++++ 5 files changed, 321 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index 409c77df6f..e4ed9c53ab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,25 @@ +2007-01-09 Tim-Philipp Müller + + * gst/playback/Makefile.am: + * gst/playback/gstplaybasebin.c: (string_arr_has_str), + (unknown_type), (setup_subtitle), (gen_source_element): + * gst/playback/gstplaybin.c: (plugin_init): + Post missing-plugin messages on the bus for missing sources and + missing decoders/demuxers/depayloaders; fix error code used when + we're missing an URI handler source; for media types that we are not + handling on purpose at the moment, don't print "don't know how to + handle xyz" messages to the terminal or post missing-plugin + messages on the bus. + + * tests/check/elements/playbin.c: (create_playbin), + (GST_START_TEST), (gst_codec_src_uri_get_type), + (gst_codec_src_uri_get_protocols), (gst_codec_src_uri_get_uri), + (gst_codec_src_uri_set_uri), (gst_codec_src_uri_handler_init), + (gst_codec_src_init_type), (gst_codec_src_base_init), + (gst_codec_src_create), (gst_codec_src_class_init), + (gst_codec_src_init), (plugin_init), (playbin_suite): + Add some tests for the missing-plugin stuff. + 2007-01-09 Tim-Philipp Müller * configure.ac: diff --git a/gst/playback/Makefile.am b/gst/playback/Makefile.am index e154b9498d..c395504a71 100644 --- a/gst/playback/Makefile.am +++ b/gst/playback/Makefile.am @@ -18,7 +18,9 @@ libgstplaybin_la_SOURCES = \ nodist_libgstplaybin_la_SOURCES = $(built_sources) libgstplaybin_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) libgstplaybin_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstplaybin_la_LIBADD = $(GST_LIBS) +libgstplaybin_la_LIBADD = \ + $(top_builddir)/gst-libs/gst/utils/libgstbaseutils-@GST_MAJORMINOR@.la \ + $(GST_LIBS) libgstdecodebin_la_SOURCES = gstdecodebin.c nodist_libgstdecodebin_la_SOURCES = $(built_sources) diff --git a/gst/playback/gstplaybasebin.c b/gst/playback/gstplaybasebin.c index 7e2ef7daea..52a34a1705 100644 --- a/gst/playback/gstplaybasebin.c +++ b/gst/playback/gstplaybasebin.c @@ -27,6 +27,8 @@ #include "gststreamselector.h" #include "gstplay-marshal.h" +#include + GST_DEBUG_CATEGORY_STATIC (gst_play_base_bin_debug); #define GST_CAT_DEFAULT gst_play_base_bin_debug @@ -911,6 +913,27 @@ add_stream (GstPlayBaseGroup * group, GstStreamInfo * info) } } +static gboolean +string_arr_has_str (const gchar * values[], const gchar * value) +{ + if (values && value) { + while (*values != NULL) { + if (strcmp (value, *values) == 0) + return TRUE; + ++values; + } + } + return FALSE; +} + +/* mime types we are not handling on purpose right now, don't post a + * missing-plugin message for these */ +static const gchar *blacklisted_mimes[] = { + "video/x-dvd-subpicture", NULL +}; + +#define IS_BLACKLISTED_MIME(type) (string_arr_has_str(blacklisted_mimes,type)) + /* * signal fired when an unknown stream is found. We create a new * UNKNOWN streaminfo object. @@ -919,14 +942,28 @@ static void unknown_type (GstElement * element, GstPad * pad, GstCaps * caps, GstPlayBaseBin * play_base_bin) { - gchar *capsstr; + const gchar *type_name; GstStreamInfo *info; GstPlayBaseGroup *group; - capsstr = gst_caps_to_string (caps); - GST_DEBUG_OBJECT (play_base_bin, "don't know how to handle %s", capsstr); - /* FIXME, g_message() ? */ - g_message ("don't know how to handle %s", capsstr); + type_name = gst_structure_get_name (gst_caps_get_structure (caps, 0)); + if (type_name && !IS_BLACKLISTED_MIME (type_name)) { + GstMessage *msg; + gchar *capsstr; + + capsstr = gst_caps_to_string (caps); + GST_DEBUG_OBJECT (play_base_bin, "don't know how to handle %s", capsstr); + /* FIXME, g_message() ? */ + g_message ("don't know how to handle %s", capsstr); + g_free (capsstr); + + msg = gst_missing_decoder_message_new (GST_ELEMENT (play_base_bin), caps); + gst_element_post_message (GST_ELEMENT_CAST (play_base_bin), msg); + } else { + /* don't spew stuff to the terminal or send message if it's blacklisted */ + GST_DEBUG_OBJECT (play_base_bin, "media type %s not handled on purpose, " + "not posting a missing-plugin message on the bus", type_name); + } GROUP_LOCK (play_base_bin); @@ -939,8 +976,6 @@ unknown_type (GstElement * element, GstPad * pad, GstCaps * caps, add_stream (group, info); GROUP_UNLOCK (play_base_bin); - - g_free (capsstr); } /* add a streaminfo that indicates that the stream is handled by the @@ -1386,7 +1421,7 @@ unknown_uri: gchar *prot = gst_uri_get_protocol (sub_uri); if (prot) { - GST_ELEMENT_ERROR (play_base_bin, RESOURCE, FAILED, + GST_ELEMENT_ERROR (play_base_bin, CORE, MISSING_PLUGIN, (_("No URI handler implemented for \"%s\"."), prot), (NULL)); g_free (prot); } else @@ -1503,7 +1538,13 @@ no_source: /* whoops, could not create the source element, dig a little deeper to * figure out what might be wrong. */ if (prot) { - GST_ELEMENT_ERROR (play_base_bin, RESOURCE, FAILED, + GstElement *this = GST_ELEMENT_CAST (play_base_bin); + GstMessage *msg; + + msg = gst_missing_uri_source_message_new (this, prot); + gst_element_post_message (this, msg); + + GST_ELEMENT_ERROR (play_base_bin, CORE, MISSING_PLUGIN, (_("No URI handler implemented for \"%s\"."), prot), (NULL)); g_free (prot); } else diff --git a/gst/playback/gstplaybin.c b/gst/playback/gstplaybin.c index 67e3ef4aa1..5f8edaabfd 100644 --- a/gst/playback/gstplaybin.c +++ b/gst/playback/gstplaybin.c @@ -241,6 +241,7 @@ #include #include +#include #include "gstplaybasebin.h" @@ -1879,6 +1880,8 @@ plugin_init (GstPlugin * plugin) { GST_DEBUG_CATEGORY_INIT (gst_play_bin_debug, "playbin", 0, "play bin"); + gst_base_utils_init (); + #ifdef ENABLE_NLS GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE, LOCALEDIR); diff --git a/tests/check/elements/playbin.c b/tests/check/elements/playbin.c index 4cebbe2a64..29de2a1666 100644 --- a/tests/check/elements/playbin.c +++ b/tests/check/elements/playbin.c @@ -161,6 +161,134 @@ GST_START_TEST (test_suburi_error_wrongproto) GST_END_TEST; +static GstElement * +create_playbin (const gchar * uri) +{ + GstElement *playbin, *fakesink1, *fakesink2; + + playbin = gst_element_factory_make ("playbin", "playbin"); + fail_unless (playbin != NULL, "Failed to create playbin element"); + + fakesink1 = gst_element_factory_make ("fakesink", NULL); + fail_unless (fakesink1 != NULL, "Failed to create fakesink element #1"); + + fakesink2 = gst_element_factory_make ("fakesink", NULL); + fail_unless (fakesink2 != NULL, "Failed to create fakesink element #2"); + + /* make them behave like normal sinks, even if not needed for the test */ + g_object_set (fakesink1, "sync", TRUE, NULL); + g_object_set (fakesink2, "sync", TRUE, NULL); + + g_object_set (playbin, "video-sink", fakesink1, NULL); + g_object_set (playbin, "audio-sink", fakesink2, NULL); + + g_object_set (playbin, "uri", uri, NULL); + + return playbin; +} + +GST_START_TEST (test_missing_urisource_handler) +{ + GstStructure *s; + GstMessage *msg; + GstElement *playbin; + GError *err = NULL; + GstBus *bus; + + playbin = create_playbin ("chocchipcookie://withahint.of/cinnamon"); + + fail_unless_equals_int (gst_element_set_state (playbin, GST_STATE_READY), + GST_STATE_CHANGE_SUCCESS); + fail_unless_equals_int (gst_element_set_state (playbin, GST_STATE_PAUSED), + GST_STATE_CHANGE_FAILURE); + + /* there should be at least a missing-plugin message on the bus now and an + * error message; the missing-plugin message should be first */ + bus = gst_element_get_bus (playbin); + + msg = gst_bus_poll (bus, GST_MESSAGE_ELEMENT | GST_MESSAGE_ERROR, -1); + fail_unless_equals_int (GST_MESSAGE_TYPE (msg), GST_MESSAGE_ELEMENT); + fail_unless (msg->structure != NULL); + s = msg->structure; + fail_unless (gst_structure_has_name (s, "missing-plugin")); + fail_unless (gst_structure_has_field_typed (s, "detail", G_TYPE_STRING)); + fail_unless_equals_string (gst_structure_get_string (s, "detail"), + "chocchipcookie"); + fail_unless (gst_structure_has_field_typed (s, "type", G_TYPE_STRING)); + fail_unless_equals_string (gst_structure_get_string (s, "type"), "urisource"); + gst_message_unref (msg); + + msg = gst_bus_poll (bus, GST_MESSAGE_ERROR, -1); + fail_unless_equals_int (GST_MESSAGE_TYPE (msg), GST_MESSAGE_ERROR); + + /* make sure the error is a CORE MISSING_PLUGIN one */ + gst_message_parse_error (msg, &err, NULL); + fail_unless (err != NULL); + fail_unless (err->domain == GST_CORE_ERROR, "error has wrong error domain " + "%s instead of core-error-quark", g_quark_to_string (err->domain)); + fail_unless (err->code == GST_CORE_ERROR_MISSING_PLUGIN, "error has wrong " + "code %u instead of GST_CORE_ERROR_MISSING_PLUGIN", err->code); + g_error_free (err); + gst_message_unref (msg); + gst_object_unref (bus); + + gst_element_set_state (playbin, GST_STATE_NULL); + gst_object_unref (playbin); +} + +GST_END_TEST; + +GST_START_TEST (test_missing_primary_decoder) +{ + GstStructure *s; + GstMessage *msg; + GstElement *playbin; + GError *err = NULL; + GstBus *bus; + + playbin = create_playbin ("codec://"); + + fail_unless_equals_int (gst_element_set_state (playbin, GST_STATE_READY), + GST_STATE_CHANGE_SUCCESS); + fail_unless_equals_int (gst_element_set_state (playbin, GST_STATE_PAUSED), + GST_STATE_CHANGE_ASYNC); + + /* there should soon be at least a missing-plugin message on the bus and an + * error message; the missing-plugin message should be first */ + bus = gst_element_get_bus (playbin); + + msg = gst_bus_poll (bus, GST_MESSAGE_ELEMENT | GST_MESSAGE_ERROR, -1); + fail_unless_equals_int (GST_MESSAGE_TYPE (msg), GST_MESSAGE_ELEMENT); + fail_unless (msg->structure != NULL); + s = msg->structure; + fail_unless (gst_structure_has_name (s, "missing-plugin")); + fail_unless (gst_structure_has_field_typed (s, "type", G_TYPE_STRING)); + fail_unless_equals_string (gst_structure_get_string (s, "type"), "decoder"); + fail_unless (gst_structure_has_field_typed (s, "detail", GST_TYPE_CAPS)); + gst_message_unref (msg); + + msg = gst_bus_poll (bus, GST_MESSAGE_ERROR, -1); + fail_unless_equals_int (GST_MESSAGE_TYPE (msg), GST_MESSAGE_ERROR); + + /* make sure the error is a STREAM CODEC_NOT_FOUND one */ + gst_message_parse_error (msg, &err, NULL); + fail_unless (err != NULL); + fail_unless (err->domain == GST_STREAM_ERROR, "error has wrong error domain " + "%s instead of stream-error-quark", g_quark_to_string (err->domain)); + fail_unless (err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND, "error has wrong " + "code %u instead of GST_STREAM_ERROR_CODEC_NOT_FOUND", err->code); + g_error_free (err); + gst_message_unref (msg); + gst_object_unref (bus); + + gst_element_set_state (playbin, GST_STATE_NULL); + gst_object_unref (playbin); +} + +GST_END_TEST; + +/*** redvideo:// source ***/ + static guint gst_red_video_src_uri_get_type (void) { @@ -268,6 +396,109 @@ gst_red_video_src_init (GstRedVideoSrc * src, GstRedVideoSrcClass * klass) { } +/*** codec:// source ***/ + +static guint +gst_codec_src_uri_get_type (void) +{ + return GST_URI_SRC; +} +static gchar ** +gst_codec_src_uri_get_protocols (void) +{ + static gchar *protocols[] = { "codec", NULL }; + + return protocols; +} + +static const gchar * +gst_codec_src_uri_get_uri (GstURIHandler * handler) +{ + return "codec://"; +} + +static gboolean +gst_codec_src_uri_set_uri (GstURIHandler * handler, const gchar * uri) +{ + return (uri != NULL && g_str_has_prefix (uri, "codec:")); +} + +static void +gst_codec_src_uri_handler_init (gpointer g_iface, gpointer iface_data) +{ + GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface; + + iface->get_type = gst_codec_src_uri_get_type; + iface->get_protocols = gst_codec_src_uri_get_protocols; + iface->get_uri = gst_codec_src_uri_get_uri; + iface->set_uri = gst_codec_src_uri_set_uri; +} + +static void +gst_codec_src_init_type (GType type) +{ + static const GInterfaceInfo uri_hdlr_info = { + gst_codec_src_uri_handler_init, NULL, NULL + }; + + g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &uri_hdlr_info); +} + +#undef parent_class +#define parent_class codec_src_parent_class + +typedef GstPushSrc GstCodecSrc; +typedef GstPushSrcClass GstCodecSrcClass; + +GST_BOILERPLATE_FULL (GstCodecSrc, gst_codec_src, GstPushSrc, + GST_TYPE_PUSH_SRC, gst_codec_src_init_type); + +static void +gst_codec_src_base_init (gpointer klass) +{ + static const GstElementDetails details = + GST_ELEMENT_DETAILS ("Codec Src", "Source/Video", "yep", "me"); + static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-codec") + ); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_templ)); + gst_element_class_set_details (element_class, &details); +} + +static GstFlowReturn +gst_codec_src_create (GstPushSrc * src, GstBuffer ** p_buf) +{ + GstBuffer *buf; + GstCaps *caps; + + buf = gst_buffer_new_and_alloc (20); + memset (GST_BUFFER_DATA (buf), 0, GST_BUFFER_SIZE (buf)); + + caps = gst_caps_new_simple ("application/x-codec", NULL); + gst_buffer_set_caps (buf, caps); + gst_caps_unref (caps); + + *p_buf = buf; + return GST_FLOW_OK; +} + +static void +gst_codec_src_class_init (GstCodecSrcClass * klass) +{ + GstPushSrcClass *pushsrc_class = GST_PUSH_SRC_CLASS (klass); + + pushsrc_class->create = gst_codec_src_create; +} + +static void +gst_codec_src_init (GstCodecSrc * src, GstCodecSrcClass * klass) +{ +} + static gboolean plugin_init (GstPlugin * plugin) { @@ -275,6 +506,10 @@ plugin_init (GstPlugin * plugin) gst_red_video_src_get_type ())) { return FALSE; } + if (!gst_element_register (plugin, "codecsrc", GST_RANK_PRIMARY, + gst_codec_src_get_type ())) { + return FALSE; + } return TRUE; } @@ -300,6 +535,14 @@ playbin_suite (void) tcase_add_test (tc_chain, test_suburi_error_wrongproto); tcase_add_test (tc_chain, test_suburi_error_invalidfile); tcase_add_test (tc_chain, test_suburi_error_unknowntype); + tcase_add_test (tc_chain, test_missing_urisource_handler); + tcase_add_test (tc_chain, test_missing_primary_decoder); + + /* one day we might also want to have the following checks: + * tcase_add_test (tc_chain, test_missing_secondary_decoder_one_fatal); + * tcase_add_test (tc_chain, test_missing_secondary_decoder_two_fatal); + * tcase_add_test (tc_chain, test_missing_secondary_decoder_two_with_preroll); + */ #endif return s;