• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * (c) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include <stdio.h>
25 #include <string.h>
26 
27 #include <gst/gst.h>
28 
29 #include "gstautodetect.h"
30 
31 #define DEFAULT_SYNC                TRUE
32 
33 /* Properties */
34 enum
35 {
36   PROP_0,
37   PROP_CAPS,
38   PROP_SYNC,
39 };
40 
41 static GstStateChangeReturn gst_auto_detect_change_state (GstElement * element,
42     GstStateChange transition);
43 static void gst_auto_detect_constructed (GObject * object);
44 static void gst_auto_detect_dispose (GObject * self);
45 static void gst_auto_detect_clear_kid (GstAutoDetect * self);
46 static void gst_auto_detect_set_property (GObject * object, guint prop_id,
47     const GValue * value, GParamSpec * pspec);
48 static void gst_auto_detect_get_property (GObject * object, guint prop_id,
49     GValue * value, GParamSpec * pspec);
50 
51 #define gst_auto_detect_parent_class parent_class
52 G_DEFINE_ABSTRACT_TYPE (GstAutoDetect, gst_auto_detect, GST_TYPE_BIN);
53 
54 static void
gst_auto_detect_class_init(GstAutoDetectClass * klass)55 gst_auto_detect_class_init (GstAutoDetectClass * klass)
56 {
57   GObjectClass *gobject_class;
58   GstElementClass *eklass;
59 
60   gobject_class = G_OBJECT_CLASS (klass);
61   eklass = GST_ELEMENT_CLASS (klass);
62 
63   gobject_class->constructed = gst_auto_detect_constructed;
64   gobject_class->dispose = gst_auto_detect_dispose;
65   gobject_class->set_property = gst_auto_detect_set_property;
66   gobject_class->get_property = gst_auto_detect_get_property;
67 
68   eklass->change_state = GST_DEBUG_FUNCPTR (gst_auto_detect_change_state);
69 
70   /**
71    * GstAutoDetect:filter-caps:
72    *
73    * This property will filter out candidate sinks that can handle the specified
74    * caps. By default only elements that support uncompressed data are selected.
75    *
76    * This property can only be set before the element goes to the READY state.
77    */
78   g_object_class_install_property (gobject_class, PROP_CAPS,
79       g_param_spec_boxed ("filter-caps", "Filter caps",
80           "Filter sink candidates using these caps.", GST_TYPE_CAPS,
81           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
82           GST_PARAM_DOC_SHOW_DEFAULT));
83 
84   g_object_class_install_property (gobject_class, PROP_SYNC,
85       g_param_spec_boolean ("sync", "Sync",
86           "Sync on the clock", DEFAULT_SYNC,
87           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
88 
89   gst_type_mark_as_plugin_api (GST_TYPE_AUTO_DETECT, 0);
90 }
91 
92 static void
gst_auto_detect_dispose(GObject * object)93 gst_auto_detect_dispose (GObject * object)
94 {
95   GstAutoDetect *self = GST_AUTO_DETECT (object);
96 
97   gst_auto_detect_clear_kid (self);
98 
99   if (self->filter_caps)
100     gst_caps_unref (self->filter_caps);
101   self->filter_caps = NULL;
102 
103   G_OBJECT_CLASS (parent_class)->dispose ((GObject *) self);
104 }
105 
106 static void
gst_auto_detect_clear_kid(GstAutoDetect * self)107 gst_auto_detect_clear_kid (GstAutoDetect * self)
108 {
109   if (self->kid) {
110     gst_element_set_state (self->kid, GST_STATE_NULL);
111     gst_bin_remove (GST_BIN (self), self->kid);
112     self->kid = NULL;
113   }
114 }
115 
116 static GstElement *
gst_auto_detect_create_fake_element_default(GstAutoDetect * self)117 gst_auto_detect_create_fake_element_default (GstAutoDetect * self)
118 {
119   GstElement *fake;
120   gchar dummy_factory[10], dummy_name[20];
121 
122   sprintf (dummy_factory, "fake%s", self->type_klass_lc);
123   sprintf (dummy_name, "fake-%s-%s", self->media_klass_lc, self->type_klass_lc);
124   fake = gst_element_factory_make (dummy_factory, dummy_name);
125   g_object_set (fake, "sync", self->sync, NULL);
126 
127   return fake;
128 }
129 
130 static GstElement *
gst_auto_detect_create_fake_element(GstAutoDetect * self)131 gst_auto_detect_create_fake_element (GstAutoDetect * self)
132 {
133   GstAutoDetectClass *klass = GST_AUTO_DETECT_GET_CLASS (self);
134   GstElement *fake;
135 
136   if (klass->create_fake_element)
137     fake = klass->create_fake_element (self);
138   else
139     fake = gst_auto_detect_create_fake_element_default (self);
140 
141   return fake;
142 }
143 
144 static gboolean
gst_auto_detect_attach_ghost_pad(GstAutoDetect * self)145 gst_auto_detect_attach_ghost_pad (GstAutoDetect * self)
146 {
147   GstPad *target = gst_element_get_static_pad (self->kid, self->type_klass_lc);
148   gboolean res = gst_ghost_pad_set_target (GST_GHOST_PAD (self->pad), target);
149   gst_object_unref (target);
150 
151   return res;
152 }
153 
154 /* Hack to make initial linking work; ideally, this would work even when
155  * no target has been assigned to the ghostpad yet. */
156 static void
gst_auto_detect_reset(GstAutoDetect * self)157 gst_auto_detect_reset (GstAutoDetect * self)
158 {
159   gst_auto_detect_clear_kid (self);
160 
161   /* placeholder element */
162   self->kid = gst_auto_detect_create_fake_element (self);
163   gst_bin_add (GST_BIN (self), self->kid);
164 
165   gst_auto_detect_attach_ghost_pad (self);
166 }
167 
168 static GstStaticCaps raw_audio_caps = GST_STATIC_CAPS ("audio/x-raw");
169 static GstStaticCaps raw_video_caps = GST_STATIC_CAPS ("video/x-raw");
170 
171 static void
gst_auto_detect_init(GstAutoDetect * self)172 gst_auto_detect_init (GstAutoDetect * self)
173 {
174   self->sync = DEFAULT_SYNC;
175 }
176 
177 static void
gst_auto_detect_constructed(GObject * object)178 gst_auto_detect_constructed (GObject * object)
179 {
180   GstAutoDetect *self = GST_AUTO_DETECT (object);
181   gboolean is_audio;
182 
183   if (G_OBJECT_CLASS (parent_class)->constructed)
184     G_OBJECT_CLASS (parent_class)->constructed (object);
185 
186   is_audio = !g_strcmp0 (self->media_klass, "Audio");
187   self->type_klass = (self->flag == GST_ELEMENT_FLAG_SINK) ? "Sink" : "Source";
188   self->type_klass_lc = (self->flag == GST_ELEMENT_FLAG_SINK) ? "sink" : "src";
189   self->media_klass_lc = is_audio ? "audio" : "video";
190   /* set the default raw caps */
191   self->filter_caps = gst_static_caps_get (is_audio ? &raw_audio_caps :
192       &raw_video_caps);
193 
194   self->pad = gst_ghost_pad_new_no_target (self->type_klass_lc,
195       (self->flag == GST_ELEMENT_FLAG_SINK) ? GST_PAD_SINK : GST_PAD_SRC);
196   gst_element_add_pad (GST_ELEMENT (self), self->pad);
197 
198   gst_auto_detect_reset (self);
199 
200   /* mark element type */
201   GST_OBJECT_FLAG_SET (self, self->flag);
202   gst_bin_set_suppressed_flags (GST_BIN (self),
203       GST_ELEMENT_FLAG_SOURCE | GST_ELEMENT_FLAG_SINK);
204 }
205 
206 static gboolean
gst_auto_detect_factory_filter(GstPluginFeature * feature,gpointer data)207 gst_auto_detect_factory_filter (GstPluginFeature * feature, gpointer data)
208 {
209   GstAutoDetect *self = (GstAutoDetect *) data;
210   guint rank;
211   const gchar *klass;
212 
213   /* we only care about element factories */
214   if (!GST_IS_ELEMENT_FACTORY (feature))
215     return FALSE;
216 
217   /* audio sinks */
218   klass = gst_element_factory_get_metadata (GST_ELEMENT_FACTORY (feature),
219       GST_ELEMENT_METADATA_KLASS);
220   if (!(strstr (klass, self->type_klass) && strstr (klass, self->media_klass)))
221     return FALSE;
222 
223   /* only select elements with autoplugging rank */
224   rank = gst_plugin_feature_get_rank (feature);
225   if (rank < GST_RANK_MARGINAL)
226     return FALSE;
227 
228   return TRUE;
229 }
230 
231 static GstElement *
create_element_with_pretty_name(GstAutoDetect * self,GstElementFactory * factory)232 create_element_with_pretty_name (GstAutoDetect * self,
233     GstElementFactory * factory)
234 {
235   GstElement *element;
236   gchar *name, *marker;
237 
238   marker = g_strdup (GST_OBJECT_NAME (factory));
239   if (g_str_has_suffix (marker, self->type_klass_lc))
240     marker[strlen (marker) - 4] = '\0';
241   if (g_str_has_prefix (marker, "gst"))
242     memmove (marker, marker + 3, strlen (marker + 3) + 1);
243   name = g_strdup_printf ("%s-actual-%s-%s", GST_OBJECT_NAME (self),
244       self->type_klass_lc, marker);
245   g_free (marker);
246 
247   element = gst_element_factory_create (factory, name);
248   g_free (name);
249 
250   return element;
251 }
252 
253 static GstElement *
gst_auto_detect_find_best(GstAutoDetect * self)254 gst_auto_detect_find_best (GstAutoDetect * self)
255 {
256   GList *list, *item;
257   GstElement *choice = NULL;
258   GstMessage *message = NULL;
259   GSList *errors = NULL;
260   GstBus *bus = gst_bus_new ();
261   GstPad *el_pad = NULL;
262   GstCaps *el_caps = NULL;
263   gboolean no_match = TRUE;
264 
265   /* We don't treat sound server sinks special. Our policy is that sound
266    * server sinks that have a rank must not auto-spawn a daemon under any
267    * circumstances, so there's nothing for us to worry about here */
268   list = gst_registry_feature_filter (gst_registry_get (),
269       (GstPluginFeatureFilter) gst_auto_detect_factory_filter, FALSE, self);
270   list =
271       g_list_sort (list, (GCompareFunc) gst_plugin_feature_rank_compare_func);
272 
273   GST_LOG_OBJECT (self, "Trying to find usable %s elements ...",
274       self->media_klass_lc);
275 
276   for (item = list; item != NULL; item = item->next) {
277     GstElementFactory *f = GST_ELEMENT_FACTORY (item->data);
278     GstElement *el;
279 
280     if ((el = create_element_with_pretty_name (self, f))) {
281       GstStateChangeReturn ret;
282 
283       GST_DEBUG_OBJECT (self, "Testing %s", GST_OBJECT_NAME (f));
284 
285       /* If autodetect has been provided with filter caps,
286        * accept only elements that match with the filter caps */
287       if (self->filter_caps) {
288         el_pad = gst_element_get_static_pad (el, self->type_klass_lc);
289         el_caps = gst_pad_query_caps (el_pad, NULL);
290         gst_object_unref (el_pad);
291         GST_DEBUG_OBJECT (self,
292             "Checking caps: %" GST_PTR_FORMAT " vs. %" GST_PTR_FORMAT,
293             self->filter_caps, el_caps);
294         no_match = !gst_caps_can_intersect (self->filter_caps, el_caps);
295         gst_caps_unref (el_caps);
296 
297         if (no_match) {
298           GST_DEBUG_OBJECT (self, "Incompatible caps");
299           gst_object_unref (el);
300           continue;
301         } else {
302           GST_DEBUG_OBJECT (self, "Found compatible caps");
303         }
304       }
305 
306       gst_element_set_bus (el, bus);
307       ret = gst_element_set_state (el, GST_STATE_READY);
308       if (ret == GST_STATE_CHANGE_SUCCESS) {
309         GST_DEBUG_OBJECT (self, "This worked!");
310         gst_element_set_state (el, GST_STATE_NULL);
311         choice = el;
312         break;
313       }
314 
315       /* collect all error messages */
316       while ((message = gst_bus_pop_filtered (bus, GST_MESSAGE_ERROR))) {
317         GST_DEBUG_OBJECT (self, "error message %" GST_PTR_FORMAT, message);
318         errors = g_slist_append (errors, message);
319       }
320 
321       gst_element_set_state (el, GST_STATE_NULL);
322       gst_object_unref (el);
323     }
324   }
325 
326   GST_DEBUG_OBJECT (self, "done trying");
327   if (!choice) {
328     /* We post a warning and plug a fake-element. This is convenient for running
329      * tests without requiring hardware src/sinks. */
330     if (errors) {
331       GError *err = NULL;
332       gchar *dbg = NULL;
333 
334       /* FIXME: we forward the first message for now; but later on it might make
335        * sense to forward all so that apps can actually analyse them. */
336       gst_message_parse_error (GST_MESSAGE (errors->data), &err, &dbg);
337       gst_element_post_message (GST_ELEMENT_CAST (self),
338           gst_message_new_warning (GST_OBJECT_CAST (self), err, dbg));
339       g_error_free (err);
340       g_free (dbg);
341     } else {
342       /* send warning message to application and use a fakesrc */
343       GST_ELEMENT_WARNING (self, RESOURCE, NOT_FOUND, (NULL),
344           ("Failed to find a usable %s %s", self->media_klass_lc,
345               self->type_klass_lc));
346     }
347     choice = gst_auto_detect_create_fake_element (self);
348     gst_element_set_state (choice, GST_STATE_READY);
349   }
350   gst_object_unref (bus);
351   gst_plugin_feature_list_free (list);
352   g_slist_foreach (errors, (GFunc) gst_mini_object_unref, NULL);
353   g_slist_free (errors);
354 
355   return choice;
356 }
357 
358 static gboolean
gst_auto_detect_detect(GstAutoDetect * self)359 gst_auto_detect_detect (GstAutoDetect * self)
360 {
361   GstElement *kid;
362   GstAutoDetectClass *klass = GST_AUTO_DETECT_GET_CLASS (self);
363 
364   gst_auto_detect_clear_kid (self);
365 
366   /* find element */
367   GST_DEBUG_OBJECT (self, "Creating new kid");
368   if (!(kid = gst_auto_detect_find_best (self)))
369     goto no_sink;
370 
371   self->has_sync =
372       g_object_class_find_property (G_OBJECT_GET_CLASS (kid), "sync") != NULL;
373   if (self->has_sync)
374     g_object_set (G_OBJECT (kid), "sync", self->sync, NULL);
375   if (klass->configure) {
376     klass->configure (self, kid);
377   }
378 
379   self->kid = kid;
380 
381   gst_bin_add (GST_BIN (self), kid);
382 
383   /* Ensure the child is brought up to the right state to match the parent. */
384   if (GST_STATE (self->kid) < GST_STATE (self))
385     gst_element_set_state (self->kid, GST_STATE (self));
386 
387   /* attach ghost pad */
388   GST_DEBUG_OBJECT (self, "Re-assigning ghostpad");
389   if (!gst_auto_detect_attach_ghost_pad (self))
390     goto target_failed;
391 
392   GST_DEBUG_OBJECT (self, "done changing auto %s %s", self->media_klass_lc,
393       self->type_klass_lc);
394 
395   return TRUE;
396 
397   /* ERRORS */
398 no_sink:
399   {
400     GST_ELEMENT_ERROR (self, LIBRARY, INIT, (NULL),
401         ("Failed to find a supported audio sink"));
402     return FALSE;
403   }
404 target_failed:
405   {
406     GST_ELEMENT_ERROR (self, LIBRARY, INIT, (NULL),
407         ("Failed to set target pad"));
408     return FALSE;
409   }
410 }
411 
412 static GstStateChangeReturn
gst_auto_detect_change_state(GstElement * element,GstStateChange transition)413 gst_auto_detect_change_state (GstElement * element, GstStateChange transition)
414 {
415   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
416   GstAutoDetect *sink = GST_AUTO_DETECT (element);
417 
418   switch (transition) {
419     case GST_STATE_CHANGE_NULL_TO_READY:
420       if (!gst_auto_detect_detect (sink))
421         return GST_STATE_CHANGE_FAILURE;
422       break;
423     default:
424       break;
425   }
426 
427   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
428   if (ret == GST_STATE_CHANGE_FAILURE)
429     return ret;
430 
431   switch (transition) {
432     case GST_STATE_CHANGE_READY_TO_NULL:
433       gst_auto_detect_reset (sink);
434       break;
435     default:
436       break;
437   }
438 
439   return ret;
440 }
441 
442 static void
gst_auto_detect_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)443 gst_auto_detect_set_property (GObject * object, guint prop_id,
444     const GValue * value, GParamSpec * pspec)
445 {
446   GstAutoDetect *self = GST_AUTO_DETECT (object);
447 
448   switch (prop_id) {
449     case PROP_CAPS:
450       if (self->filter_caps)
451         gst_caps_unref (self->filter_caps);
452       self->filter_caps = gst_caps_copy (gst_value_get_caps (value));
453       break;
454     case PROP_SYNC:
455       self->sync = g_value_get_boolean (value);
456       if (self->kid && self->has_sync)
457         g_object_set_property (G_OBJECT (self->kid), pspec->name, value);
458       break;
459     default:
460       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
461       break;
462   }
463 }
464 
465 static void
gst_auto_detect_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)466 gst_auto_detect_get_property (GObject * object, guint prop_id,
467     GValue * value, GParamSpec * pspec)
468 {
469   GstAutoDetect *self = GST_AUTO_DETECT (object);
470 
471   switch (prop_id) {
472     case PROP_CAPS:
473       gst_value_set_caps (value, self->filter_caps);
474       break;
475     case PROP_SYNC:
476       g_value_set_boolean (value, self->sync);
477       break;
478     default:
479       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
480       break;
481   }
482 }
483