Merge branch 'master' into 0.11
[platform/upstream/gstreamer.git] / gst / autodetect / gstautovideosrc.c
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 #define gst_auto_video_src_parent_class parent_class
67 G_DEFINE_TYPE (GstAutoVideoSrc, gst_auto_video_src, GST_TYPE_BIN);
68
69 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
70     GST_PAD_SRC,
71     GST_PAD_ALWAYS,
72     GST_STATIC_CAPS_ANY);
73
74 static void
75 gst_auto_video_src_class_init (GstAutoVideoSrcClass * klass)
76 {
77   GObjectClass *gobject_class;
78   GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
79
80   gobject_class = G_OBJECT_CLASS (klass);
81   gobject_class->dispose = (GObjectFinalizeFunc) gst_auto_video_src_dispose;
82   gobject_class->set_property = gst_auto_video_src_set_property;
83   gobject_class->get_property = gst_auto_video_src_get_property;
84
85   eklass->change_state = GST_DEBUG_FUNCPTR (gst_auto_video_src_change_state);
86
87   /**
88    * GstAutoVideoSrc:filter-caps
89    *
90    * This property will filter out candidate sources that can handle the specified
91    * caps. By default only video sources that support raw rgb and yuv video
92    * are selected.
93    *
94    * This property can only be set before the element goes to the READY state.
95    *
96    * Since: 0.10.14
97    **/
98   g_object_class_install_property (gobject_class, PROP_CAPS,
99       g_param_spec_boxed ("filter-caps", "Filter caps",
100           "Filter src candidates using these caps.", GST_TYPE_CAPS,
101           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
102
103   gst_element_class_add_pad_template (eklass,
104       gst_static_pad_template_get (&src_template));
105   gst_element_class_set_details_simple (eklass, "Auto video source",
106       "Source/Video",
107       "Wrapper video source for automatically detected video source",
108       "Jan Schmidt <thaytan@noraisin.net>, "
109       "Stefan Kost <ensonic@users.sf.net>");
110 }
111
112 static void
113 gst_auto_video_src_dispose (GstAutoVideoSrc * src)
114 {
115   gst_auto_video_src_clear_kid (src);
116
117   if (src->filter_caps)
118     gst_caps_unref (src->filter_caps);
119   src->filter_caps = NULL;
120
121   G_OBJECT_CLASS (parent_class)->dispose ((GObject *) src);
122 }
123
124 static void
125 gst_auto_video_src_clear_kid (GstAutoVideoSrc * src)
126 {
127   if (src->kid) {
128     gst_element_set_state (src->kid, GST_STATE_NULL);
129     gst_bin_remove (GST_BIN (src), src->kid);
130     src->kid = NULL;
131     /* Don't loose SOURCE flag */
132     GST_OBJECT_FLAG_SET (src, GST_ELEMENT_IS_SOURCE);
133   }
134 }
135
136 /*
137  * Hack to make initial linking work; ideally, this'd work even when
138  * no target has been assigned to the ghostpad yet.
139  */
140
141 static void
142 gst_auto_video_src_reset (GstAutoVideoSrc * src)
143 {
144   GstPad *targetpad;
145
146   /* Remove any existing element */
147   gst_auto_video_src_clear_kid (src);
148
149   /* fakesrc placeholder */
150   src->kid = gst_element_factory_make ("fakesrc", "tempsrc");
151   gst_bin_add (GST_BIN (src), src->kid);
152
153   /* pad */
154   targetpad = gst_element_get_static_pad (src->kid, "src");
155   gst_ghost_pad_set_target (GST_GHOST_PAD (src->pad), targetpad);
156   gst_object_unref (targetpad);
157 }
158
159 static GstStaticCaps raw_caps = GST_STATIC_CAPS ("video/x-raw");
160
161 static void
162 gst_auto_video_src_init (GstAutoVideoSrc * src)
163 {
164   src->pad = gst_ghost_pad_new_no_target ("src", GST_PAD_SRC);
165   gst_element_add_pad (GST_ELEMENT (src), src->pad);
166
167   gst_auto_video_src_reset (src);
168
169   /* set the default raw video caps */
170   src->filter_caps = gst_static_caps_get (&raw_caps);
171
172   /* mark as source */
173   GST_OBJECT_FLAG_SET (src, GST_ELEMENT_IS_SOURCE);
174 }
175
176 static gboolean
177 gst_auto_video_src_factory_filter (GstPluginFeature * feature, gpointer data)
178 {
179   guint rank;
180   const gchar *klass;
181
182   /* we only care about element factories */
183   if (!GST_IS_ELEMENT_FACTORY (feature))
184     return FALSE;
185
186   /* video sources */
187   klass = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (feature));
188   if (!(strstr (klass, "Source") && strstr (klass, "Video")))
189     return FALSE;
190
191   /* only select elements with autoplugging rank */
192   rank = gst_plugin_feature_get_rank (feature);
193   if (rank < GST_RANK_MARGINAL)
194     return FALSE;
195
196   return TRUE;
197 }
198
199 static gint
200 gst_auto_video_src_compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2)
201 {
202   gint diff;
203
204   diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1);
205   if (diff != 0)
206     return diff;
207   return strcmp (gst_plugin_feature_get_name (f2),
208       gst_plugin_feature_get_name (f1));
209 }
210
211 static GstElement *
212 gst_auto_video_src_create_element_with_pretty_name (GstAutoVideoSrc * src,
213     GstElementFactory * factory)
214 {
215   GstElement *element;
216   gchar *name, *marker;
217
218   marker = g_strdup (GST_OBJECT_NAME (factory));
219   if (g_str_has_suffix (marker, "src"))
220     marker[strlen (marker) - 4] = '\0';
221   if (g_str_has_prefix (marker, "gst"))
222     g_memmove (marker, marker + 3, strlen (marker + 3) + 1);
223   name = g_strdup_printf ("%s-actual-src-%s", GST_OBJECT_NAME (src), marker);
224   g_free (marker);
225
226   element = gst_element_factory_create (factory, name);
227   g_free (name);
228
229   return element;
230 }
231
232 static GstElement *
233 gst_auto_video_src_find_best (GstAutoVideoSrc * src)
234 {
235   GList *list, *item;
236   GstElement *choice = NULL;
237   GstMessage *message = NULL;
238   GSList *errors = NULL;
239   GstBus *bus = gst_bus_new ();
240   GstPad *el_pad = NULL;
241   GstCaps *el_caps = NULL;
242   gboolean no_match = TRUE;
243
244   list = gst_registry_feature_filter (gst_registry_get_default (),
245       (GstPluginFeatureFilter) gst_auto_video_src_factory_filter, FALSE, src);
246   list = g_list_sort (list, (GCompareFunc) gst_auto_video_src_compare_ranks);
247
248   GST_LOG_OBJECT (src, "Trying to find usable video devices ...");
249
250   for (item = list; item != NULL; item = item->next) {
251     GstElementFactory *f = GST_ELEMENT_FACTORY (item->data);
252     GstElement *el;
253
254     if ((el = gst_auto_video_src_create_element_with_pretty_name (src, f))) {
255       GstStateChangeReturn ret;
256
257       GST_DEBUG_OBJECT (src, "Testing %s", GST_OBJECT_NAME (f));
258
259       /* If AutoVideoSrc has been provided with filter caps,
260        * accept only sources that match with the filter caps */
261       if (src->filter_caps) {
262         el_pad = gst_element_get_static_pad (GST_ELEMENT (el), "src");
263         el_caps = gst_pad_get_caps (el_pad, NULL);
264         gst_object_unref (el_pad);
265         GST_DEBUG_OBJECT (src,
266             "Checking caps: %" GST_PTR_FORMAT " vs. %" GST_PTR_FORMAT,
267             src->filter_caps, el_caps);
268         no_match = !gst_caps_can_intersect (src->filter_caps, el_caps);
269         gst_caps_unref (el_caps);
270
271         if (no_match) {
272           GST_DEBUG_OBJECT (src, "Incompatible caps");
273           gst_object_unref (el);
274           continue;
275         } else {
276           GST_DEBUG_OBJECT (src, "Found compatible caps");
277         }
278       }
279
280       gst_element_set_bus (el, bus);
281       ret = gst_element_set_state (el, GST_STATE_READY);
282       if (ret == GST_STATE_CHANGE_SUCCESS) {
283         GST_DEBUG_OBJECT (src, "This worked!");
284         choice = el;
285         break;
286       }
287
288       /* collect all error messages */
289       while ((message = gst_bus_pop_filtered (bus, GST_MESSAGE_ERROR))) {
290         GST_DEBUG_OBJECT (src, "error message %" GST_PTR_FORMAT, message);
291         errors = g_slist_append (errors, message);
292       }
293
294       gst_element_set_state (el, GST_STATE_NULL);
295       gst_object_unref (el);
296     }
297   }
298
299   GST_DEBUG_OBJECT (src, "done trying");
300   if (!choice) {
301     if (errors) {
302       /* FIXME: we forward the first error for now; but later on it might make
303        * sense to actually analyse them */
304       gst_message_ref (GST_MESSAGE (errors->data));
305       GST_DEBUG_OBJECT (src, "reposting message %p", errors->data);
306       gst_element_post_message (GST_ELEMENT (src), GST_MESSAGE (errors->data));
307     } else {
308       /* send warning message to application and use a fakesrc */
309       GST_ELEMENT_WARNING (src, RESOURCE, NOT_FOUND, (NULL),
310           ("Failed to find a usable video source"));
311       choice = gst_element_factory_make ("fakesrc", "fake-video-src");
312       if (g_object_class_find_property (G_OBJECT_GET_CLASS (choice), "sync"))
313         g_object_set (choice, "sync", TRUE, NULL);
314       gst_element_set_state (choice, GST_STATE_READY);
315     }
316   }
317   gst_object_unref (bus);
318   gst_plugin_feature_list_free (list);
319   g_slist_foreach (errors, (GFunc) gst_mini_object_unref, NULL);
320   g_slist_free (errors);
321
322   return choice;
323 }
324
325 static gboolean
326 gst_auto_video_src_detect (GstAutoVideoSrc * src)
327 {
328   GstElement *esrc;
329   GstPad *targetpad;
330
331   gst_auto_video_src_clear_kid (src);
332
333   /* find element */
334   GST_DEBUG_OBJECT (src, "Creating new kid");
335   if (!(esrc = gst_auto_video_src_find_best (src)))
336     goto no_src;
337
338   src->kid = esrc;
339   gst_bin_add (GST_BIN (src), esrc);
340
341   /* attach ghost pad */
342   GST_DEBUG_OBJECT (src, "Re-assigning ghostpad");
343   targetpad = gst_element_get_static_pad (src->kid, "src");
344   if (!gst_ghost_pad_set_target (GST_GHOST_PAD (src->pad), targetpad))
345     goto target_failed;
346
347   gst_object_unref (targetpad);
348   GST_DEBUG_OBJECT (src, "done changing auto video source");
349
350   return TRUE;
351
352   /* ERRORS */
353 no_src:
354   {
355     GST_ELEMENT_ERROR (src, LIBRARY, INIT, (NULL),
356         ("Failed to find a supported video source"));
357     return FALSE;
358   }
359 target_failed:
360   {
361     GST_ELEMENT_ERROR (src, LIBRARY, INIT, (NULL),
362         ("Failed to set target pad"));
363     gst_object_unref (targetpad);
364     return FALSE;
365   }
366 }
367
368 static GstStateChangeReturn
369 gst_auto_video_src_change_state (GstElement * element,
370     GstStateChange transition)
371 {
372   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
373   GstAutoVideoSrc *src = GST_AUTO_VIDEO_SRC (element);
374
375   switch (transition) {
376     case GST_STATE_CHANGE_NULL_TO_READY:
377       if (!gst_auto_video_src_detect (src))
378         return GST_STATE_CHANGE_FAILURE;
379       break;
380     default:
381       break;
382   }
383
384   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
385
386   switch (transition) {
387     case GST_STATE_CHANGE_READY_TO_NULL:
388       gst_auto_video_src_reset (src);
389       break;
390     default:
391       break;
392   }
393
394   return ret;
395 }
396
397 static void
398 gst_auto_video_src_set_property (GObject * object, guint prop_id,
399     const GValue * value, GParamSpec * pspec)
400 {
401   GstAutoVideoSrc *src = GST_AUTO_VIDEO_SRC (object);
402
403   switch (prop_id) {
404     case PROP_CAPS:
405       if (src->filter_caps)
406         gst_caps_unref (src->filter_caps);
407       src->filter_caps = gst_caps_copy (gst_value_get_caps (value));
408       break;
409     default:
410       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
411       break;
412   }
413 }
414
415 static void
416 gst_auto_video_src_get_property (GObject * object, guint prop_id,
417     GValue * value, GParamSpec * pspec)
418 {
419   GstAutoVideoSrc *src = GST_AUTO_VIDEO_SRC (object);
420
421   switch (prop_id) {
422     case PROP_CAPS:{
423       gst_value_set_caps (value, src->filter_caps);
424       break;
425     }
426     default:
427       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
428       break;
429   }
430 }