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