Tizen 2.0 Release
[framework/multimedia/gst-plugins-good0.10.git] / ext / hal / hal.c
1 /* GStreamer
2  * Copyright (C) <2002> Thomas Vander Stichele <thomas@apestaart.org>
3  * Copyright (C) <2006> Jürg Billeter <j@bitron.ch>
4  * Copyright (C) <2007> Sebastian Dröge <slomo@circular-chaos.org>
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  * this library handles interaction with Hal
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include <string.h>
31 #include <glib.h>
32 #include "hal.h"
33
34 GST_DEBUG_CATEGORY_EXTERN (hal_debug);
35
36 #define GST_CAT_DEFAULT hal_debug
37
38 /* compat for older libhal */
39 #ifndef LIBHAL_FREE_DBUS_ERROR
40 #define LIBHAL_FREE_DBUS_ERROR(e) dbus_error_free (e)
41 #endif
42
43 /*
44  * gst_hal_get_alsa_element:
45  * @ctx: a #LibHalContext which should be used for querying HAL.
46  * @udi: a #gchar corresponding to the UDI you want to get.
47  * @device_type: a #GstHalDeviceType specifying the wanted device type.
48  *
49  * Get Hal UDI @udi's string value.
50  *
51  * Returns: a newly allocated #gchar string containing the appropriate pipeline
52  * for UDI @udi, or NULL in the case of an error..
53  */
54 static gchar *
55 gst_hal_get_alsa_element (LibHalContext * ctx, const gchar * udi,
56     GstHalDeviceType device_type)
57 {
58   char *type, *string = NULL;
59   const char *element = NULL;
60   DBusError error;
61
62   dbus_error_init (&error);
63
64   if (!libhal_device_query_capability (ctx, udi, "alsa", &error)) {
65     if (dbus_error_is_set (&error)) {
66       GST_DEBUG ("Failed querying %s for alsa capability: %s: %s",
67           udi, error.name, error.message);
68       LIBHAL_FREE_DBUS_ERROR (&error);
69     } else {
70       GST_DEBUG ("UDI %s has no alsa capability", udi);
71     }
72     return NULL;
73   }
74
75   type = libhal_device_get_property_string (ctx, udi, "alsa.type", &error);
76
77   if (dbus_error_is_set (&error)) {
78     GST_DEBUG ("UDI %s has alsa capabilities but no alsa.type property: %s, %s",
79         udi, error.name, error.message);
80     LIBHAL_FREE_DBUS_ERROR (&error);
81     return NULL;
82   } else if (!type) {
83     GST_DEBUG ("UDI %s has empty alsa.type property", udi);
84     return NULL;
85   }
86
87   if (strcmp (type, "playback") == 0 && device_type == GST_HAL_AUDIOSINK)
88     element = "alsasink";
89   else if (strcmp (type, "capture") == 0 && device_type == GST_HAL_AUDIOSRC)
90     element = "alsasrc";
91
92   libhal_free_string (type);
93
94   if (element) {
95     int card, device;
96
97     card = libhal_device_get_property_int (ctx, udi, "alsa.card", &error);
98     if (dbus_error_is_set (&error)) {
99       GST_DEBUG ("UDI %s has no alsa.card property: %s: %s", udi, error.name,
100           error.message);
101       LIBHAL_FREE_DBUS_ERROR (&error);
102       return NULL;
103     } else if (card == -1) {
104       GST_DEBUG ("UDI %s has no alsa.card property", udi);
105       return NULL;
106     }
107
108     device = libhal_device_get_property_int (ctx, udi, "alsa.device", &error);
109     if (dbus_error_is_set (&error)) {
110       GST_DEBUG ("UDI %s has no alsa.device property: %s: %s", udi, error.name,
111           error.message);
112       LIBHAL_FREE_DBUS_ERROR (&error);
113       return NULL;
114     } else if (device == -1) {
115       GST_DEBUG ("UDI %s has no alsa.device property", udi);
116       return NULL;
117     }
118
119     /* This is a bit dodgy, since it makes lots of assumptions about the way
120      * alsa is set up. In any case, only munge the device string for playback */
121     if (strcmp (element, "alsasink") == 0 && device == 0) {
122       /* handle default device specially to use
123        * dmix, dsnoop, and softvol if appropriate */
124       string = g_strdup_printf ("%s device=default:%d", element, card);
125     } else {
126       string =
127           g_strdup_printf ("%s device=plughw:%d,%d", element, card, device);
128     }
129   }
130
131   return string;
132 }
133
134 /*
135  * gst_hal_get_oss_element:
136  * @ctx: a #LibHalContext which should be used for querying HAL.
137  * @udi: a #gchar corresponding to the UDI you want to get.
138  * @device_type: a #GstHalDeviceType specifying the wanted device type.
139  *
140  * Get Hal UDI @udi's string value.
141  *
142  * Returns: a newly allocated #gchar string containing the appropriate pipeline
143  * for UDI @udi, or NULL in the case of an error..
144  */
145 static gchar *
146 gst_hal_get_oss_element (LibHalContext * ctx, const gchar * udi,
147     GstHalDeviceType device_type)
148 {
149   char *type, *string = NULL;
150   const char *element = NULL;
151   DBusError error;
152
153   dbus_error_init (&error);
154
155   if (!libhal_device_query_capability (ctx, udi, "oss", &error)) {
156     if (dbus_error_is_set (&error)) {
157       GST_DEBUG ("Failed querying %s for oss capability: %s: %s", udi,
158           error.name, error.message);
159       LIBHAL_FREE_DBUS_ERROR (&error);
160     } else {
161       GST_DEBUG ("UDI %s has no oss capability", udi);
162     }
163     return NULL;
164   }
165
166   type = libhal_device_get_property_string (ctx, udi, "oss.type", &error);
167   if (dbus_error_is_set (&error)) {
168     GST_DEBUG ("UDI %s has oss capabilities but no oss.type property: %s, %s",
169         udi, error.name, error.message);
170     LIBHAL_FREE_DBUS_ERROR (&error);
171     return NULL;
172   } else if (!type) {
173     GST_DEBUG ("UDI %s has empty oss.type property", udi);
174     return NULL;
175   }
176
177   if (strcmp (type, "pcm") == 0) {
178     if (device_type == GST_HAL_AUDIOSINK)
179       element = "osssink";
180     else if (device_type == GST_HAL_AUDIOSRC)
181       element = "osssrc";
182   }
183   libhal_free_string (type);
184
185   if (element) {
186     char *device = NULL;
187
188     device =
189         libhal_device_get_property_string (ctx, udi, "oss.device_file", &error);
190     if (dbus_error_is_set (&error)) {
191       GST_DEBUG
192           ("UDI %s has oss capabilities but no oss.device_file property: %s, %s",
193           udi, error.name, error.message);
194       LIBHAL_FREE_DBUS_ERROR (&error);
195       return NULL;
196     } else if (!device) {
197       GST_DEBUG ("UDI %s has empty oss.device_file property", udi);
198       return NULL;
199     }
200
201     string = g_strdup_printf ("%s device=%s", element, device);
202     libhal_free_string (device);
203   }
204
205   return string;
206 }
207
208 /*
209  * gst_hal_get_string:
210  * @udi: a #gchar corresponding to the UDI you want to get.
211  * @device_type: a #GstHalDeviceType specifying the wanted device type.
212  *
213  * Get Hal UDI @udi's string value.
214  *
215  * Returns: a newly allocated #gchar string containing the appropriate pipeline
216  * for UDI @udi, or NULL in the case of an error..
217  */
218 static gchar *
219 gst_hal_get_string (const gchar * udi, GstHalDeviceType device_type)
220 {
221   DBusError error;
222   LibHalContext *ctx;
223   char *string = NULL;
224
225   /* Don't query HAL for NULL UDIs. Passing NULL as UDI to HAL gives
226    * an assertion failure in D-Bus when running with
227    * DBUS_FATAL_WARNINGS=1. */
228   if (!udi)
229     return NULL;
230
231   dbus_error_init (&error);
232
233   ctx = libhal_ctx_new ();
234   /* Should only happen on OOM */
235   g_return_val_if_fail (ctx != NULL, NULL);
236
237   if (!libhal_ctx_set_dbus_connection (ctx, dbus_bus_get (DBUS_BUS_SYSTEM,
238               &error))) {
239     GST_DEBUG ("Unable to set DBus connection: %s: %s", error.name,
240         error.message);
241     LIBHAL_FREE_DBUS_ERROR (&error);
242     goto ctx_free;
243   }
244
245   if (!libhal_ctx_init (ctx, &error)) {
246     GST_DEBUG ("Unable to set init HAL context: %s: %s", error.name,
247         error.message);
248     LIBHAL_FREE_DBUS_ERROR (&error);
249     goto ctx_free;
250   }
251
252   /* Now first check if UDI is an alsa device, then oss and then
253    * check the childs of the given device. If there are alsa and oss
254    * children the first alsa one is used. */
255
256   string = gst_hal_get_alsa_element (ctx, udi, device_type);
257
258   if (!string)
259     string = gst_hal_get_oss_element (ctx, udi, device_type);
260
261   if (!string) {
262     int num_childs;
263     char **childs = NULL;
264
265     /* now try if one of the direct subdevices supports ALSA or OSS */
266     childs =
267         libhal_manager_find_device_string_match (ctx, "info.parent", udi,
268         &num_childs, &error);
269     if (dbus_error_is_set (&error)) {
270       GST_DEBUG ("Unable to retrieve childs of %s: %s: %s", udi, error.name,
271           error.message);
272       LIBHAL_FREE_DBUS_ERROR (&error);
273       goto ctx_shutdown;
274     }
275
276     if (childs && num_childs > 0) {
277       int i;
278       char *alsa_string = NULL, *oss_string = NULL;
279
280       for (i = 0; i < num_childs && !alsa_string; i++) {
281         alsa_string = gst_hal_get_alsa_element (ctx, childs[i], device_type);
282
283         if (!oss_string)
284           oss_string = gst_hal_get_oss_element (ctx, childs[i], device_type);
285       }
286
287       if (alsa_string) {
288         string = alsa_string;
289         g_free (oss_string);
290       } else if (oss_string) {
291         string = oss_string;
292       }
293     }
294     libhal_free_string_array (childs);
295   }
296
297 ctx_shutdown:
298   if (!libhal_ctx_shutdown (ctx, &error)) {
299     GST_DEBUG ("Closing connection to HAL failed: %s: %s", error.name,
300         error.message);
301     LIBHAL_FREE_DBUS_ERROR (&error);
302   }
303
304 ctx_free:
305   libhal_ctx_free (ctx);
306
307   if (string == NULL) {
308     GST_WARNING ("Problem finding a HAL audio device for udi %s", udi);
309   } else {
310     GST_INFO ("Using %s", string);
311   }
312
313   return string;
314 }
315
316 /* external functions */
317
318 /**
319  * gst_hal_render_bin_from_udi:
320  * @udi: a #gchar string corresponding to a Hal UDI.
321  *
322  * Render bin from Hal UDI @udi.
323  *
324  * Returns: a #GstElement containing the rendered bin.
325  */
326 GstElement *
327 gst_hal_render_bin_from_udi (const gchar * udi, GstHalDeviceType type)
328 {
329   GstElement *bin = NULL;
330   gchar *value;
331
332   value = gst_hal_get_string (udi, type);
333   if (value)
334     bin = gst_parse_bin_from_description (value, TRUE, NULL);
335   g_free (value);
336   return bin;
337 }
338
339 /**
340  * gst_hal_get_audio_sink:
341  * @udi: a #gchar string corresponding to a Hal UDI.
342  *
343  * Render audio output bin from GStreamer Hal UDI.
344  * If no device with the specified UDI exists or @udi is NULL,
345  * the default audio sink for the  platform is used
346  * (typically alsasink, osssink or sunaudiosink).
347  *
348  * Returns: a #GstElement containing the audio output bin, or NULL if
349  * everything failed.
350  */
351 GstElement *
352 gst_hal_get_audio_sink (const gchar * udi)
353 {
354   GstElement *ret = NULL;
355
356   if (udi)
357     ret = gst_hal_render_bin_from_udi (udi, GST_HAL_AUDIOSINK);
358
359   if (!ret) {
360     ret = gst_element_factory_make (DEFAULT_AUDIOSINK, NULL);
361
362     if (!ret)
363       GST_ERROR ("Hal audio sink and %s don't work", DEFAULT_AUDIOSINK);
364   }
365
366   return ret;
367 }
368
369 /**
370  * gst_hal_get_audio_src:
371  * @udi: a #gchar string corresponding to a Hal UDI.
372  *
373  * Render audio acquisition bin from GStreamer Hal UDI.
374  * If no device with the specified UDI exists or @udi is NULL,
375  * the default audio source for the  plaform is used
376  * (typically alsasrc, osssrc or sunaudiosrc).
377  *
378  * Returns: a #GstElement containing the audio source bin, or NULL if
379  * everything failed.
380  */
381 GstElement *
382 gst_hal_get_audio_src (const gchar * udi)
383 {
384   GstElement *ret = NULL;
385
386   if (udi)
387     ret = gst_hal_render_bin_from_udi (udi, GST_HAL_AUDIOSRC);
388
389   if (!ret) {
390     ret = gst_element_factory_make (DEFAULT_AUDIOSRC, NULL);
391
392     if (!ret)
393       GST_ERROR ("Hal audio src and %s don't work", DEFAULT_AUDIOSRC);
394   }
395
396   return ret;
397 }