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