diff --git a/ChangeLog b/ChangeLog index 76f5018d6f..090b37ec25 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2004-10-01 Wim Taymans + + * gst/playback/README: + * gst/playback/gstdecodebin.c: (gst_decode_bin_factory_filter), + (gst_decode_bin_init), (find_compatibles), (close_pad_link), + (try_to_link_1), (no_more_pads), (close_link), (type_found): + Add some debug info to decodebin, update README + 2004-10-01 Ronald S. Bultje * ext/dvdnav/dvdnavsrc.c: (dvdnav_handle_navigation_event): diff --git a/gst/playback/README b/gst/playback/README index c0553cafe2..fad169d439 100644 --- a/gst/playback/README +++ b/gst/playback/README @@ -7,9 +7,7 @@ decoderbin: TODO - reuse of decoderbin, cleanup in READY state - - debugging - threading after demuxing? - - better factory selection, based on Demuxer/Decoder types and ranks. - error handling baseplaybin: diff --git a/gst/playback/gstdecodebin.c b/gst/playback/gstdecodebin.c index dbb1f3b3bf..201b41daaa 100644 --- a/gst/playback/gstdecodebin.c +++ b/gst/playback/gstdecodebin.c @@ -1,5 +1,5 @@ /* GStreamer - * Copyright (C) <1999> Erik Walthinsen + * Copyright (C) <2004> Wim Taymans * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -51,26 +51,28 @@ typedef struct _GstDecodeBinClass GstDecodeBinClass; struct _GstDecodeBin { - GstBin bin; + GstBin bin; /* we extend GstBin */ - GstElement *typefind; + GstElement *typefind; /* this holds the typefind object */ - gboolean threaded; - GList *dynamics; + gboolean threaded; /* indicating threaded execution is desired */ + GList *dynamics; /* list of dynamic connections */ - GList *factories; + GList *factories; /* factories we can use for selecting elements */ gint numpads; - GList *elements; + GList *elements; /* elements we added in autoplugging */ - guint have_type_id; + guint have_type_id; /* signal id for the typefind element */ }; struct _GstDecodeBinClass { GstBinClass parent_class; + /* signal we fire when a new pad has been decoded into raw audio/video */ void (*new_decoded_pad) (GstElement * element, GstPad * pad, gboolean last); + /* signal fired when we found a pad that we cannot decode */ void (*unknown_type) (GstElement * element, GstCaps * caps); }; @@ -89,12 +91,14 @@ enum LAST_SIGNAL }; +/* this structure is created for all dynamic pads that could get created + * at runtime */ typedef struct { - gint np_sig_id; - gint nmp_sig_id; - GstElement *element; - GstDecodeBin *decode_bin; + gint np_sig_id; /* signal id of new_pad */ + gint nmp_sig_id; /* signal id of no_more_pads */ + GstElement *element; /* the element sending the signal */ + GstDecodeBin *decode_bin; /* pointer to ourself */ } GstDynamic; @@ -196,12 +200,19 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass) GST_DEBUG_FUNCPTR (gst_decode_bin_change_state); } +/* check if the bin is dynamic. + * + * If there are no outstanding dynamic connections, the bin is + * considered to be non-dynamic. + */ static gboolean gst_decode_bin_is_dynamic (GstDecodeBin * decode_bin) { return decode_bin->dynamics != NULL; } +/* the filter function for selecting the elements we can use in + * autoplugging */ static gboolean gst_decode_bin_factory_filter (GstPluginFeature * feature, GstDecodeBin * decode_bin) @@ -209,14 +220,17 @@ gst_decode_bin_factory_filter (GstPluginFeature * feature, guint rank; const gchar *klass; + /* we only care about element factories */ if (!GST_IS_ELEMENT_FACTORY (feature)) return FALSE; klass = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (feature)); + /* only demuxers and decoders can play */ if (strstr (klass, "Demux") == NULL && strstr (klass, "Decoder") == NULL) { return FALSE; } + /* only select elements with autoplugging rank */ rank = gst_plugin_feature_get_rank (feature); if (rank < GST_RANK_MARGINAL) return FALSE; @@ -224,6 +238,7 @@ gst_decode_bin_factory_filter (GstPluginFeature * feature, return TRUE; } +/* function used to sort element features */ static gint compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2) { @@ -241,24 +256,33 @@ gst_decode_bin_init (GstDecodeBin * decode_bin) { GList *factories; + /* first filter out the interesting element factories */ factories = gst_registry_pool_feature_filter ( (GstPluginFeatureFilter) gst_decode_bin_factory_filter, FALSE, decode_bin); + /* sort them according to their ranks */ decode_bin->factories = g_list_sort (factories, (GCompareFunc) compare_ranks); + /* do some debugging */ g_list_foreach (decode_bin->factories, (GFunc) print_feature, NULL); + /* we create the typefind element only once */ decode_bin->typefind = gst_element_factory_make ("typefind", "typefind"); if (!decode_bin->typefind) { - g_warning ("can't find typefind element"); - } - gst_bin_add (GST_BIN (decode_bin), decode_bin->typefind); - gst_element_add_ghost_pad (GST_ELEMENT (decode_bin), - gst_element_get_pad (decode_bin->typefind, "sink"), "sink"); + g_warning ("can't find typefind element, decodebin will not work"); + } else { + /* add the typefind element */ + gst_bin_add (GST_BIN (decode_bin), decode_bin->typefind); + /* ghost the sink pad to ourself */ + gst_element_add_ghost_pad (GST_ELEMENT (decode_bin), + gst_element_get_pad (decode_bin->typefind, "sink"), "sink"); - decode_bin->have_type_id = - g_signal_connect (G_OBJECT (decode_bin->typefind), "have_type", - G_CALLBACK (type_found), decode_bin); + /* connect a signal to find out when the typefind element found + * a type */ + decode_bin->have_type_id = + g_signal_connect (G_OBJECT (decode_bin->typefind), "have_type", + G_CALLBACK (type_found), decode_bin); + } decode_bin->dynamics = NULL; } @@ -291,28 +315,37 @@ gst_decode_bin_dispose (GObject * object) } } +/* this function runs through the element factories and returns a list + * of all elements that are able to sink the given caps + */ static GList * find_compatibles (GstDecodeBin * decode_bin, const GstCaps * caps) { GList *factories; GList *to_try = NULL; + /* loop over all the factories */ for (factories = decode_bin->factories; factories; factories = g_list_next (factories)) { GstElementFactory *factory = GST_ELEMENT_FACTORY (factories->data); const GList *templates; GList *walk; + /* get the templates from the element factory */ templates = gst_element_factory_get_pad_templates (factory); for (walk = (GList *) templates; walk; walk = g_list_next (walk)) { GstPadTemplate *templ = GST_PAD_TEMPLATE (walk->data); + /* we only care about the sink templates */ if (templ->direction == GST_PAD_SINK) { GstCaps *intersect; + /* try to intersect the caps with the caps of the template */ intersect = gst_caps_intersect (caps, gst_pad_template_get_caps (templ)); + /* check if the intersection is empty */ if (!gst_caps_is_empty (intersect)) { + /* non empty intersection, we can use this element */ to_try = g_list_append (to_try, factory); } gst_caps_free (intersect); @@ -322,6 +355,15 @@ find_compatibles (GstDecodeBin * decode_bin, const GstCaps * caps) return to_try; } +/* given a pad and a caps from an element, find the list of elements + * that could connect to the pad + * + * If the pad has a raw format, this function will create a ghostpad + * for the pad onto the decodebin. + * + * If no compatible elements could be found, this function will signal + * the unknown_type signal. + */ static void close_pad_link (GstElement * element, GstPad * pad, GstCaps * caps, GstDecodeBin * decode_bin) @@ -330,29 +372,34 @@ close_pad_link (GstElement * element, GstPad * pad, GstCaps * caps, GstStructure *structure; const gchar *mimetype; + /* the caps is empty, this means the pad has no type, we can only + * decide to fire the unknown_type signal. */ if (gst_caps_is_empty (caps)) { g_signal_emit (G_OBJECT (decode_bin), gst_decode_bin_signals[SIGNAL_UNKNOWN_TYPE], 0, caps); return; } + /* FIXME, iterate over more structures? */ structure = gst_caps_get_structure (caps, 0); mimetype = gst_structure_get_name (structure); - /* first see if this is raw */ + /* first see if this is raw. If the type is raw, we can + * create a ghostpad for this pad. */ if (g_str_has_prefix (mimetype, "video/x-raw") || g_str_has_prefix (mimetype, "audio/x-raw")) { gchar *padname; GstPad *ghost; gboolean dynamic; + /* make a unique name for this new pad */ padname = g_strdup_printf ("src%d", decode_bin->numpads); decode_bin->numpads++; - gst_element_add_ghost_pad (GST_ELEMENT (decode_bin), pad, padname); - - ghost = gst_element_get_pad (GST_ELEMENT (decode_bin), padname); + /* make it a ghostpad */ + ghost = gst_element_add_ghost_pad (GST_ELEMENT (decode_bin), pad, padname); + /* see if any more pending dynamic connections exist */ dynamic = gst_decode_bin_is_dynamic (decode_bin); /* our own signal with an extra flag that this is the only pad */ @@ -363,85 +410,128 @@ close_pad_link (GstElement * element, GstPad * pad, GstCaps * caps, return; } - /* then continue plugging */ + /* then continue plugging, first find all compatible elements */ to_try = find_compatibles (decode_bin, caps); if (to_try == NULL) { + /* no compatible elements, fire the unknown_type signal, we cannot go + * on */ g_signal_emit (G_OBJECT (decode_bin), gst_decode_bin_signals[SIGNAL_UNKNOWN_TYPE], 0, caps); return; } + /* now try to link the elements in the to_try list to the pad */ try_to_link_1 (decode_bin, pad, to_try); } +/* given a list of element factories, try to link one of the factories + * to the given pad */ static GstElement * try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories) { GList *walk; + /* loop over the factories */ for (walk = factories; walk; walk = g_list_next (walk)) { GstElementFactory *factory = GST_ELEMENT_FACTORY (walk->data); GstElement *element; gboolean ret; - GST_DEBUG ("trying to link %s", + GST_DEBUG_OBJECT (decode_bin, "trying to link %s", gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory))); + /* make an element from the factory first */ element = gst_element_factory_create (factory, NULL); - if (element == NULL) + if (element == NULL) { + /* hmm, strange */ + GST_WARNING_OBJECT (decode_bin, "could not create an element from %s", + gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory))); continue; + } - GST_DEBUG ("adding %s", gst_element_get_name (element)); + /* now add the element to the bin first */ + GST_DEBUG_OBJECT (decode_bin, "adding %s", gst_element_get_name (element)); gst_bin_add (GST_BIN (decode_bin), element); + /* keep out own list of elements */ decode_bin->elements = g_list_prepend (decode_bin->elements, element); + /* try to link the given pad to a sinkpad */ + /* FIXME, find the sinkpad by looping over the pads instead of + * looking it up by name */ ret = gst_pad_link (pad, gst_element_get_pad (element, "sink")); if (ret) { const gchar *klass; GstElementFactory *factory; + /* The link worked, now figure out what it was that we connected */ factory = gst_element_get_factory (element); klass = gst_element_factory_get_klass (factory); + /* check if we can use threads */ if (decode_bin->threaded) { if (strstr (klass, "Demux") != NULL) { /* FIXME, do something with threads here */ } } + /* now that we added the element we can try to continue autoplugging + * on it until we have a raw type */ close_link (element, decode_bin); + /* change the state of the element to that of the parent */ gst_element_sync_state_with_parent (element); return element; } else { + /* this element did not work, remove it again and continue trying + * other elements */ gst_bin_remove (GST_BIN (decode_bin), element); } } return NULL; } +/* This function will be called when a dynamic pad is created on an element. + * We try to continue autoplugging on this new pad. */ static void new_pad (GstElement * element, GstPad * pad, GstDynamic * dynamic) { close_pad_link (element, pad, gst_pad_get_caps (pad), dynamic->decode_bin); } +/* this signal is fired when an element signals the no_more_pads signal. + * This means that the element will not generate more dynamic pads and + * we can remove the element from the list of dynamic elements. When we + * have no more dynamic elements in the pipeline, we can fire a no_more_pads + * signal ourselves. */ static void no_more_pads (GstElement * element, GstDynamic * dynamic) { GstDecodeBin *decode_bin = dynamic->decode_bin; - GST_DEBUG ("decodebin: no more pads"); + GST_DEBUG_OBJECT (decode_bin, "no more pads on element %s", + gst_element_get_name (element)); + /* disconnect signals */ g_signal_handler_disconnect (G_OBJECT (dynamic->element), dynamic->np_sig_id); g_signal_handler_disconnect (G_OBJECT (dynamic->element), dynamic->nmp_sig_id); + /* remove the element from the list of dynamic elements */ decode_bin->dynamics = g_list_remove (decode_bin->dynamics, dynamic); g_free (dynamic); - if (decode_bin->dynamics == NULL) + /* if we have no more dynamic elements, we have no change of creating + * more pads, so we fire the no_more_pads signal */ + if (decode_bin->dynamics == NULL) { + GST_DEBUG_OBJECT (decode_bin, + "no more dynamic elements, signaling no_more_pads"); gst_element_no_more_pads (GST_ELEMENT (decode_bin)); + } else { + GST_DEBUG_OBJECT (decode_bin, "we have more dynamic elements"); + } } +/* this function inspects the given element and tries to connect something + * on the srcpads. If there are dynamic pads, it sets up a signal handler to + * continue autoplugging when they become available */ static void close_link (GstElement * element, GstDecodeBin * decode_bin) { @@ -449,43 +539,74 @@ close_link (GstElement * element, GstDecodeBin * decode_bin) gboolean dynamic = FALSE; GList *to_connect = NULL; + GST_DEBUG_OBJECT (decode_bin, "closing links with element %s", + gst_element_get_name (element)); + + /* loop over all the padtemplates */ for (pads = gst_element_get_pad_template_list (element); pads; pads = g_list_next (pads)) { GstPadTemplate *templ = GST_PAD_TEMPLATE (pads->data); + const gchar *templ_name; + /* we are only interested in source pads */ if (GST_PAD_TEMPLATE_DIRECTION (templ) != GST_PAD_SRC) continue; + templ_name = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ); + GST_DEBUG_OBJECT (decode_bin, "got a source pad template %s", templ_name); + + /* figure out what kind of pad this is */ switch (GST_PAD_TEMPLATE_PRESENCE (templ)) { case GST_PAD_ALWAYS: { - GstPad *pad = gst_element_get_pad (element, - GST_PAD_TEMPLATE_NAME_TEMPLATE (templ)); + /* get the pad that we need to autoplug */ + GstPad *pad = gst_element_get_pad (element, templ_name); if (pad) { + GST_DEBUG_OBJECT (decode_bin, "got the pad for always template %s", + templ_name); + /* here is the pad, we need to autoplug it */ to_connect = g_list_prepend (to_connect, pad); + } else { + /* strange, pad is marked as always but it's not + * there. Fix the element */ + GST_WARNING_OBJECT (decode_bin, + "could not get the pad for always template %s", templ_name); } break; } case GST_PAD_SOMETIMES: { - GstPad *pad = gst_element_get_pad (element, - GST_PAD_TEMPLATE_NAME_TEMPLATE (templ)); + /* try to get the pad to see if it is already created or + * not */ + GstPad *pad = gst_element_get_pad (element, templ_name); if (pad) { + GST_DEBUG_OBJECT (decode_bin, "got the pad for sometimes template %s", + templ_name); + /* the pad is created, we need to autoplug it */ to_connect = g_list_prepend (to_connect, pad); } else { + GST_DEBUG_OBJECT (decode_bin, + "did not get the sometimes pad of template %s", templ_name); + /* we have an element that will create dynamic pads */ dynamic = TRUE; } break; } case GST_PAD_REQUEST: + /* ignore request pads */ + GST_DEBUG_OBJECT (decode_bin, "ignoring request padtemplate %s", + templ_name); break; } } if (dynamic) { GstDynamic *dyn; + GST_DEBUG_OBJECT (decode_bin, "got a dynamic element here"); + /* ok, this element has dynamic pads, set up the signal handlers to be + * notified of them */ dyn = g_new0 (GstDynamic, 1); dyn->np_sig_id = g_signal_connect (G_OBJECT (element), "new-pad", G_CALLBACK (new_pad), dyn); @@ -494,29 +615,46 @@ close_link (GstElement * element, GstDecodeBin * decode_bin) dyn->element = element; dyn->decode_bin = decode_bin; + /* and add this element to the dynamic elements */ decode_bin->dynamics = g_list_prepend (decode_bin->dynamics, dyn); } + /* now loop over all the pads we need to connect */ for (pads = to_connect; pads; pads = g_list_next (pads)) { GstPad *pad = GST_PAD (pads->data); + GST_DEBUG_OBJECT (decode_bin, "closing pad link for %s", + gst_pad_get_name (pad)); + + /* continue autoplugging on the pads */ close_pad_link (element, pad, gst_pad_get_caps (pad), decode_bin); } g_list_free (to_connect); } +/* this is the signal handler for the typefind element have_type signal. + * It tries to continue autoplugging on the typefind src pad */ static void type_found (GstElement * typefind, guint probability, GstCaps * caps, GstDecodeBin * decode_bin) { gboolean dynamic; + GST_DEBUG_OBJECT (decode_bin, "typefind found caps %" GST_PTR_FORMAT, caps); + + /* autoplug the new pad with the caps that the signal gave us */ close_pad_link (typefind, gst_element_get_pad (typefind, "src"), caps, decode_bin); dynamic = gst_decode_bin_is_dynamic (decode_bin); if (dynamic == FALSE) { + GST_DEBUG_OBJECT (decode_bin, "we have no dynamic elements anymore"); + /* if we have no dynamic elements, we know that no new pads + * will be created and we can signal out no_more_pads signal */ gst_element_no_more_pads (GST_ELEMENT (decode_bin)); + } else { + /* more dynamic elements exist that could create new pads */ + GST_DEBUG_OBJECT (decode_bin, "we more dynamic elements"); } }