gst_plugins_good/gst/autodetect/gstautovideosrc.c
changeset 27 d43ce56a1534
parent 23 29ecd5cb86b3
child 31 aec498aab1d3
equal deleted inserted replaced
23:29ecd5cb86b3 27:d43ce56a1534
     1 /* GStreamer
       
     2  * (c) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
       
     3  * (c) 2006 Jan Schmidt <thaytan@noraisin.net>
       
     4  * (c) 2008 Stefan Kost <ensonic@users.sf.net>
       
     5  *
       
     6  * This library is free software; you can redistribute it and/or
       
     7  * modify it under the terms of the GNU Library General Public
       
     8  * License as published by the Free Software Foundation; either
       
     9  * version 2 of the License, or (at your option) any later version.
       
    10  *
       
    11  * This library is distributed in the hope that it will be useful,
       
    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    14  * Library General Public License for more details.
       
    15  *
       
    16  * You should have received a copy of the GNU Library General Public
       
    17  * License along with this library; if not, write to the
       
    18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    19  * Boston, MA 02111-1307, USA.
       
    20  */
       
    21 
       
    22 /**
       
    23  * SECTION:element-autovideosrc
       
    24  * @see_also: autoaudiosrc, v4l2src, v4lsrc
       
    25  *
       
    26  * autovideosrc is a video src that automatically detects an appropriate
       
    27  * video source to use.  It does so by scanning the registry for all elements
       
    28  * that have <quote>Source</quote> and <quote>Video</quote> in the class field
       
    29  * of their element information, and also have a non-zero autoplugging rank.
       
    30  *
       
    31  * <refsect2>
       
    32  * <title>Example launch line</title>
       
    33  * |[
       
    34  * gst-launch -v -m autovideosrc ! xvimagesink
       
    35  * ]|
       
    36  * </refsect2>
       
    37  */
       
    38 
       
    39 #ifdef HAVE_CONFIG_H
       
    40 #include "config.h"
       
    41 #endif
       
    42 
       
    43 #include <string.h>
       
    44 
       
    45 #include "gstautovideosrc.h"
       
    46 #include "gstautodetect.h"
       
    47 
       
    48 /* Properties */
       
    49 enum
       
    50 {
       
    51   PROP_0,
       
    52   PROP_CAPS,
       
    53 };
       
    54 
       
    55 static GstStateChangeReturn
       
    56 gst_auto_video_src_change_state (GstElement * element,
       
    57     GstStateChange transition);
       
    58 static void gst_auto_video_src_dispose (GstAutoVideoSrc * src);
       
    59 static void gst_auto_video_src_clear_kid (GstAutoVideoSrc * src);
       
    60 
       
    61 static void gst_auto_video_src_set_property (GObject * object, guint prop_id,
       
    62     const GValue * value, GParamSpec * pspec);
       
    63 static void gst_auto_video_src_get_property (GObject * object, guint prop_id,
       
    64     GValue * value, GParamSpec * pspec);
       
    65 
       
    66 GST_BOILERPLATE (GstAutoVideoSrc, gst_auto_video_src, GstBin, GST_TYPE_BIN);
       
    67 
       
    68 static const GstElementDetails gst_auto_video_src_details =
       
    69 GST_ELEMENT_DETAILS ("Auto video source",
       
    70     "Source/Video",
       
    71     "Wrapper video source for automatically detected video source",
       
    72     "Ronald Bultje <rbultje@ronald.bitfreak.net>\n"
       
    73     "Jan Schmidt <thaytan@noraisin.net>\n"
       
    74     "Stefan Kost <ensonic@users.sf.net>");
       
    75 
       
    76 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
       
    77     GST_PAD_SRC,
       
    78     GST_PAD_ALWAYS,
       
    79     GST_STATIC_CAPS_ANY);
       
    80 
       
    81 static void
       
    82 gst_auto_video_src_base_init (gpointer klass)
       
    83 {
       
    84   GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
       
    85 
       
    86   gst_element_class_add_pad_template (eklass,
       
    87       gst_static_pad_template_get (&src_template));
       
    88   gst_element_class_set_details (eklass, &gst_auto_video_src_details);
       
    89 }
       
    90 
       
    91 static void
       
    92 gst_auto_video_src_class_init (GstAutoVideoSrcClass * klass)
       
    93 {
       
    94   GObjectClass *gobject_class;
       
    95   GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
       
    96 
       
    97   gobject_class = G_OBJECT_CLASS (klass);
       
    98   gobject_class->dispose =
       
    99       (GObjectFinalizeFunc) GST_DEBUG_FUNCPTR (gst_auto_video_src_dispose);
       
   100   eklass->change_state = GST_DEBUG_FUNCPTR (gst_auto_video_src_change_state);
       
   101   gobject_class->set_property =
       
   102       GST_DEBUG_FUNCPTR (gst_auto_video_src_set_property);
       
   103   gobject_class->get_property =
       
   104       GST_DEBUG_FUNCPTR (gst_auto_video_src_get_property);
       
   105 
       
   106   /**
       
   107    * GstAutoVideoSrc:filter-caps
       
   108    *
       
   109    * This property will filter out candidate sources that can handle the specified
       
   110    * caps. By default only video sources that support raw rgb and yuv video
       
   111    * are selected.
       
   112    *
       
   113    * This property can only be set before the element goes to the READY state.
       
   114    *
       
   115    * Since: 0.10.14
       
   116    **/
       
   117   g_object_class_install_property (gobject_class, PROP_CAPS,
       
   118       g_param_spec_boxed ("filter-caps", "Filter caps",
       
   119           "Filter src candidates using these caps.", GST_TYPE_CAPS,
       
   120           G_PARAM_READWRITE));
       
   121 }
       
   122 
       
   123 static void
       
   124 gst_auto_video_src_dispose (GstAutoVideoSrc * src)
       
   125 {
       
   126   gst_auto_video_src_clear_kid (src);
       
   127 
       
   128   if (src->filter_caps)
       
   129     gst_caps_unref (src->filter_caps);
       
   130   src->filter_caps = NULL;
       
   131 
       
   132   G_OBJECT_CLASS (parent_class)->dispose ((GObject *) src);
       
   133 }
       
   134 
       
   135 static void
       
   136 gst_auto_video_src_clear_kid (GstAutoVideoSrc * src)
       
   137 {
       
   138   if (src->kid) {
       
   139     gst_element_set_state (src->kid, GST_STATE_NULL);
       
   140     gst_bin_remove (GST_BIN (src), src->kid);
       
   141     src->kid = NULL;
       
   142   }
       
   143 }
       
   144 
       
   145 /*
       
   146  * Hack to make initial linking work; ideally, this'd work even when
       
   147  * no target has been assigned to the ghostpad yet.
       
   148  */
       
   149 
       
   150 static void
       
   151 gst_auto_video_src_reset (GstAutoVideoSrc * src)
       
   152 {
       
   153   GstPad *targetpad;
       
   154 
       
   155   /* Remove any existing element */
       
   156   gst_auto_video_src_clear_kid (src);
       
   157 
       
   158   /* fakesrc placeholder */
       
   159   src->kid = gst_element_factory_make ("fakesrc", "tempsrc");
       
   160   gst_bin_add (GST_BIN (src), src->kid);
       
   161 
       
   162   /* pad */
       
   163   targetpad = gst_element_get_static_pad (src->kid, "src");
       
   164   gst_ghost_pad_set_target (GST_GHOST_PAD (src->pad), targetpad);
       
   165   gst_object_unref (targetpad);
       
   166 }
       
   167 
       
   168 static GstStaticCaps raw_caps =
       
   169     GST_STATIC_CAPS ("video/x-raw-yuv; video/x-raw-rgb");
       
   170 
       
   171 static void
       
   172 gst_auto_video_src_init (GstAutoVideoSrc * src, GstAutoVideoSrcClass * g_class)
       
   173 {
       
   174   src->pad = gst_ghost_pad_new_no_target ("src", GST_PAD_SRC);
       
   175   gst_element_add_pad (GST_ELEMENT (src), src->pad);
       
   176 
       
   177   gst_auto_video_src_reset (src);
       
   178 
       
   179   /* set the default raw video caps */
       
   180   src->filter_caps = gst_static_caps_get (&raw_caps);
       
   181 
       
   182   /* mark as source */
       
   183   GST_OBJECT_FLAG_UNSET (src, GST_ELEMENT_IS_SINK);
       
   184 }
       
   185 
       
   186 static gboolean
       
   187 gst_auto_video_src_factory_filter (GstPluginFeature * feature, gpointer data)
       
   188 {
       
   189   guint rank;
       
   190   const gchar *klass;
       
   191 
       
   192   /* we only care about element factories */
       
   193   if (!GST_IS_ELEMENT_FACTORY (feature))
       
   194     return FALSE;
       
   195 
       
   196   /* video sources */
       
   197   klass = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (feature));
       
   198   if (!(strstr (klass, "Source") && strstr (klass, "Video")))
       
   199     return FALSE;
       
   200 
       
   201   /* only select elements with autoplugging rank */
       
   202   rank = gst_plugin_feature_get_rank (feature);
       
   203   if (rank < GST_RANK_MARGINAL)
       
   204     return FALSE;
       
   205 
       
   206   return TRUE;
       
   207 }
       
   208 
       
   209 static gint
       
   210 gst_auto_video_src_compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2)
       
   211 {
       
   212   gint diff;
       
   213 
       
   214   diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1);
       
   215   if (diff != 0)
       
   216     return diff;
       
   217   return strcmp (gst_plugin_feature_get_name (f2),
       
   218       gst_plugin_feature_get_name (f1));
       
   219 }
       
   220 
       
   221 static GstElement *
       
   222 gst_auto_video_src_create_element_with_pretty_name (GstAutoVideoSrc * src,
       
   223     GstElementFactory * factory)
       
   224 {
       
   225   GstElement *element;
       
   226   gchar *name, *marker;
       
   227 
       
   228   marker = g_strdup (GST_PLUGIN_FEATURE (factory)->name);
       
   229   if (g_str_has_suffix (marker, "src"))
       
   230     marker[strlen (marker) - 4] = '\0';
       
   231   if (g_str_has_prefix (marker, "gst"))
       
   232     g_memmove (marker, marker + 3, strlen (marker + 3) + 1);
       
   233   name = g_strdup_printf ("%s-actual-src-%s", GST_OBJECT_NAME (src), marker);
       
   234   g_free (marker);
       
   235 
       
   236   element = gst_element_factory_create (factory, name);
       
   237   g_free (name);
       
   238 
       
   239   return element;
       
   240 }
       
   241 
       
   242 static GstElement *
       
   243 gst_auto_video_src_find_best (GstAutoVideoSrc * src)
       
   244 {
       
   245   GList *list, *item;
       
   246   GstElement *choice = NULL;
       
   247   GstMessage *message = NULL;
       
   248   GSList *errors = NULL;
       
   249   GstBus *bus = gst_bus_new ();
       
   250   GstPad *el_pad = NULL;
       
   251   GstCaps *el_caps = NULL, *intersect = NULL;
       
   252   gboolean no_match = TRUE;
       
   253 
       
   254   list = gst_registry_feature_filter (gst_registry_get_default (),
       
   255       (GstPluginFeatureFilter) gst_auto_video_src_factory_filter, FALSE, src);
       
   256   list = g_list_sort (list, (GCompareFunc) gst_auto_video_src_compare_ranks);
       
   257 
       
   258   GST_LOG_OBJECT (src, "Trying to find usable video devices ...");
       
   259 
       
   260   for (item = list; item != NULL; item = item->next) {
       
   261     GstElementFactory *f = GST_ELEMENT_FACTORY (item->data);
       
   262     GstElement *el;
       
   263 
       
   264     if ((el = gst_auto_video_src_create_element_with_pretty_name (src, f))) {
       
   265       GstStateChangeReturn ret;
       
   266 
       
   267       GST_DEBUG_OBJECT (src, "Testing %s", GST_PLUGIN_FEATURE (f)->name);
       
   268 
       
   269       /* If AutoVideoSrc has been provided with filter caps,
       
   270        * accept only sources that match with the filter caps */
       
   271       if (src->filter_caps) {
       
   272         el_pad = gst_element_get_static_pad (GST_ELEMENT (el), "src");
       
   273         el_caps = gst_pad_get_caps (el_pad);
       
   274         gst_object_unref (el_pad);
       
   275         GST_DEBUG_OBJECT (src,
       
   276             "Checking caps: %" GST_PTR_FORMAT " vs. %" GST_PTR_FORMAT,
       
   277             src->filter_caps, el_caps);
       
   278         intersect = gst_caps_intersect (src->filter_caps, el_caps);
       
   279         no_match = gst_caps_is_empty (intersect);
       
   280         gst_caps_unref (el_caps);
       
   281         gst_caps_unref (intersect);
       
   282 
       
   283         if (no_match) {
       
   284           GST_DEBUG_OBJECT (src, "Incompatible caps");
       
   285           gst_object_unref (el);
       
   286           continue;
       
   287         } else {
       
   288           GST_DEBUG_OBJECT (src, "Found compatible caps");
       
   289         }
       
   290       }
       
   291 
       
   292       gst_element_set_bus (el, bus);
       
   293       ret = gst_element_set_state (el, GST_STATE_READY);
       
   294       if (ret == GST_STATE_CHANGE_SUCCESS) {
       
   295         GST_DEBUG_OBJECT (src, "This worked!");
       
   296         choice = el;
       
   297         break;
       
   298       }
       
   299 
       
   300       /* collect all error messages */
       
   301       while ((message = gst_bus_pop_filtered (bus, GST_MESSAGE_ERROR))) {
       
   302         GST_DEBUG_OBJECT (src, "error message %" GST_PTR_FORMAT, message);
       
   303         errors = g_slist_append (errors, message);
       
   304       }
       
   305 
       
   306       gst_element_set_state (el, GST_STATE_NULL);
       
   307       gst_object_unref (el);
       
   308     }
       
   309   }
       
   310 
       
   311   GST_DEBUG_OBJECT (src, "done trying");
       
   312   if (!choice) {
       
   313     if (errors) {
       
   314       /* FIXME: we forward the first error for now; but later on it might make
       
   315        * sense to actually analyse them */
       
   316       gst_message_ref (GST_MESSAGE (errors->data));
       
   317       GST_DEBUG_OBJECT (src, "reposting message %p", errors->data);
       
   318       gst_element_post_message (GST_ELEMENT (src), GST_MESSAGE (errors->data));
       
   319     } else {
       
   320       /* send warning message to application and use a fakesrc */
       
   321       GST_ELEMENT_WARNING (src, RESOURCE, NOT_FOUND, (NULL),
       
   322           ("Failed to find a usable video source"));
       
   323       choice = gst_element_factory_make ("fakesrc", "fake-video-src");
       
   324       if (g_object_class_find_property (G_OBJECT_GET_CLASS (choice), "sync"))
       
   325         g_object_set (choice, "sync", TRUE, NULL);
       
   326       gst_element_set_state (choice, GST_STATE_READY);
       
   327     }
       
   328   }
       
   329   gst_object_unref (bus);
       
   330   gst_plugin_feature_list_free (list);
       
   331   g_slist_foreach (errors, (GFunc) gst_mini_object_unref, NULL);
       
   332   g_slist_free (errors);
       
   333 
       
   334   return choice;
       
   335 }
       
   336 
       
   337 static gboolean
       
   338 gst_auto_video_src_detect (GstAutoVideoSrc * src)
       
   339 {
       
   340   GstElement *esrc;
       
   341   GstPad *targetpad;
       
   342 
       
   343   gst_auto_video_src_clear_kid (src);
       
   344 
       
   345   /* find element */
       
   346   GST_DEBUG_OBJECT (src, "Creating new kid");
       
   347   if (!(esrc = gst_auto_video_src_find_best (src)))
       
   348     goto no_src;
       
   349 
       
   350   src->kid = esrc;
       
   351   gst_bin_add (GST_BIN (src), esrc);
       
   352 
       
   353   /* attach ghost pad */
       
   354   GST_DEBUG_OBJECT (src, "Re-assigning ghostpad");
       
   355   targetpad = gst_element_get_static_pad (src->kid, "src");
       
   356   if (!gst_ghost_pad_set_target (GST_GHOST_PAD (src->pad), targetpad))
       
   357     goto target_failed;
       
   358 
       
   359   gst_object_unref (targetpad);
       
   360   GST_DEBUG_OBJECT (src, "done changing auto video source");
       
   361 
       
   362   return TRUE;
       
   363 
       
   364   /* ERRORS */
       
   365 no_src:
       
   366   {
       
   367     GST_ELEMENT_ERROR (src, LIBRARY, INIT, (NULL),
       
   368         ("Failed to find a supported video source"));
       
   369     return FALSE;
       
   370   }
       
   371 target_failed:
       
   372   {
       
   373     GST_ELEMENT_ERROR (src, LIBRARY, INIT, (NULL),
       
   374         ("Failed to set target pad"));
       
   375     gst_object_unref (targetpad);
       
   376     return FALSE;
       
   377   }
       
   378 }
       
   379 
       
   380 static GstStateChangeReturn
       
   381 gst_auto_video_src_change_state (GstElement * element,
       
   382     GstStateChange transition)
       
   383 {
       
   384   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
       
   385   GstAutoVideoSrc *src = GST_AUTO_VIDEO_SRC (element);
       
   386 
       
   387   switch (transition) {
       
   388     case GST_STATE_CHANGE_NULL_TO_READY:
       
   389       if (!gst_auto_video_src_detect (src))
       
   390         return GST_STATE_CHANGE_FAILURE;
       
   391       break;
       
   392     default:
       
   393       break;
       
   394   }
       
   395 
       
   396   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
       
   397 
       
   398   switch (transition) {
       
   399     case GST_STATE_CHANGE_READY_TO_NULL:
       
   400       gst_auto_video_src_reset (src);
       
   401       break;
       
   402     default:
       
   403       break;
       
   404   }
       
   405 
       
   406   return ret;
       
   407 }
       
   408 
       
   409 static void
       
   410 gst_auto_video_src_set_property (GObject * object, guint prop_id,
       
   411     const GValue * value, GParamSpec * pspec)
       
   412 {
       
   413   GstAutoVideoSrc *src = GST_AUTO_VIDEO_SRC (object);
       
   414 
       
   415   switch (prop_id) {
       
   416     case PROP_CAPS:
       
   417       if (src->filter_caps)
       
   418         gst_caps_unref (src->filter_caps);
       
   419       src->filter_caps = gst_caps_copy (gst_value_get_caps (value));
       
   420       break;
       
   421     default:
       
   422       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   423       break;
       
   424   }
       
   425 }
       
   426 
       
   427 static void
       
   428 gst_auto_video_src_get_property (GObject * object, guint prop_id,
       
   429     GValue * value, GParamSpec * pspec)
       
   430 {
       
   431   GstAutoVideoSrc *src = GST_AUTO_VIDEO_SRC (object);
       
   432 
       
   433   switch (prop_id) {
       
   434     case PROP_CAPS:{
       
   435       gst_value_set_caps (value, src->filter_caps);
       
   436       break;
       
   437     }
       
   438     default:
       
   439       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   440       break;
       
   441   }
       
   442 }