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