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