1 /* Copyright (C) 2001 CodeFactory AB |
|
2 * Copyright (C) 2001 Thomas Nyberg <thomas@codefactory.se> |
|
3 * Copyright (C) 2001-2002 Andy Wingo <apwingo@eos.ncsu.edu> |
|
4 * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de> |
|
5 * Copyright (C) 2005 Tim-Philipp Müller <tim centricular net> |
|
6 * |
|
7 * This library is free software; you can redistribute it and/or |
|
8 * modify it under the terms of the GNU Library General Public |
|
9 * License as published by the Free Software Foundation; either |
|
10 * version 2 of the License, or (at your option) any later version. |
|
11 * |
|
12 * This library is distributed in the hope that it will be useful, |
|
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
15 * Library General Public License for more details. |
|
16 * |
|
17 * You should have received a copy of the GNU Library General Public |
|
18 * License along with this library; if not, write to the Free |
|
19 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
|
20 */ |
|
21 |
|
22 #ifdef HAVE_CONFIG_H |
|
23 #include "config.h" |
|
24 #endif |
|
25 |
|
26 #include "gstalsadeviceprobe.h" |
|
27 #include "gst/interfaces/propertyprobe.h" |
|
28 |
|
29 static const GList * |
|
30 gst_alsa_device_property_probe_get_properties (GstPropertyProbe * probe) |
|
31 { |
|
32 GObjectClass *klass = G_OBJECT_GET_CLASS (probe); |
|
33 static GList *list = NULL; |
|
34 |
|
35 /* well, not perfect, but better than no locking at all. |
|
36 * In the worst case we leak a list node, so who cares? */ |
|
37 GST_CLASS_LOCK (GST_OBJECT_CLASS (klass)); |
|
38 |
|
39 if (!list) { |
|
40 GParamSpec *pspec; |
|
41 |
|
42 pspec = g_object_class_find_property (klass, "device"); |
|
43 list = g_list_append (NULL, pspec); |
|
44 } |
|
45 |
|
46 GST_CLASS_UNLOCK (GST_OBJECT_CLASS (klass)); |
|
47 |
|
48 return list; |
|
49 } |
|
50 |
|
51 static GList * |
|
52 gst_alsa_get_device_list (snd_pcm_stream_t stream) |
|
53 { |
|
54 snd_ctl_t *handle; |
|
55 int card, err, dev; |
|
56 snd_ctl_card_info_t *info; |
|
57 snd_pcm_info_t *pcminfo; |
|
58 gboolean mixer = (stream == -1); |
|
59 GList *list = NULL; |
|
60 |
|
61 if (stream == -1) |
|
62 stream = 0; |
|
63 |
|
64 snd_ctl_card_info_malloc (&info); |
|
65 snd_pcm_info_malloc (&pcminfo); |
|
66 card = -1; |
|
67 |
|
68 if (snd_card_next (&card) < 0 || card < 0) { |
|
69 /* no soundcard found */ |
|
70 return NULL; |
|
71 } |
|
72 |
|
73 while (card >= 0) { |
|
74 gchar name[32]; |
|
75 |
|
76 g_snprintf (name, sizeof (name), "hw:%d", card); |
|
77 if ((err = snd_ctl_open (&handle, name, 0)) < 0) { |
|
78 goto next_card; |
|
79 } |
|
80 if ((err = snd_ctl_card_info (handle, info)) < 0) { |
|
81 snd_ctl_close (handle); |
|
82 goto next_card; |
|
83 } |
|
84 |
|
85 if (mixer) { |
|
86 list = g_list_append (list, g_strdup (name)); |
|
87 } else { |
|
88 dev = -1; |
|
89 while (1) { |
|
90 gchar *gst_device; |
|
91 |
|
92 snd_ctl_pcm_next_device (handle, &dev); |
|
93 |
|
94 if (dev < 0) |
|
95 break; |
|
96 snd_pcm_info_set_device (pcminfo, dev); |
|
97 snd_pcm_info_set_subdevice (pcminfo, 0); |
|
98 snd_pcm_info_set_stream (pcminfo, stream); |
|
99 if ((err = snd_ctl_pcm_info (handle, pcminfo)) < 0) { |
|
100 continue; |
|
101 } |
|
102 |
|
103 gst_device = g_strdup_printf ("hw:%d,%d", card, dev); |
|
104 list = g_list_append (list, gst_device); |
|
105 } |
|
106 } |
|
107 snd_ctl_close (handle); |
|
108 next_card: |
|
109 if (snd_card_next (&card) < 0) { |
|
110 break; |
|
111 } |
|
112 } |
|
113 |
|
114 snd_ctl_card_info_free (info); |
|
115 snd_pcm_info_free (pcminfo); |
|
116 |
|
117 return list; |
|
118 } |
|
119 |
|
120 static void |
|
121 gst_alsa_device_property_probe_probe_property (GstPropertyProbe * probe, |
|
122 guint prop_id, const GParamSpec * pspec) |
|
123 { |
|
124 if (!g_str_equal (pspec->name, "device")) { |
|
125 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec); |
|
126 } |
|
127 } |
|
128 |
|
129 static gboolean |
|
130 gst_alsa_device_property_probe_needs_probe (GstPropertyProbe * probe, |
|
131 guint prop_id, const GParamSpec * pspec) |
|
132 { |
|
133 /* don't cache probed data */ |
|
134 return TRUE; |
|
135 } |
|
136 |
|
137 static GValueArray * |
|
138 gst_alsa_device_property_probe_get_values (GstPropertyProbe * probe, |
|
139 guint prop_id, const GParamSpec * pspec) |
|
140 { |
|
141 GstElementClass *klass; |
|
142 const GList *templates; |
|
143 snd_pcm_stream_t mode = -1; |
|
144 GValueArray *array; |
|
145 GValue value = { 0, }; |
|
146 GList *l, *list; |
|
147 |
|
148 if (!g_str_equal (pspec->name, "device")) { |
|
149 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec); |
|
150 return NULL; |
|
151 } |
|
152 |
|
153 klass = GST_ELEMENT_GET_CLASS (GST_ELEMENT (probe)); |
|
154 |
|
155 /* I'm pretty sure ALSA has a good way to do this. However, their cool |
|
156 * auto-generated documentation is pretty much useless if you try to |
|
157 * do function-wise look-ups. */ |
|
158 /* we assume one pad template at max [zero=mixer] */ |
|
159 templates = gst_element_class_get_pad_template_list (klass); |
|
160 if (templates) { |
|
161 if (GST_PAD_TEMPLATE_DIRECTION (templates->data) == GST_PAD_SRC) |
|
162 mode = SND_PCM_STREAM_CAPTURE; |
|
163 else |
|
164 mode = SND_PCM_STREAM_PLAYBACK; |
|
165 } |
|
166 |
|
167 list = gst_alsa_get_device_list (mode); |
|
168 |
|
169 if (list == NULL) { |
|
170 GST_LOG_OBJECT (probe, "No devices found"); |
|
171 return NULL; |
|
172 } |
|
173 |
|
174 array = g_value_array_new (g_list_length (list)); |
|
175 g_value_init (&value, G_TYPE_STRING); |
|
176 for (l = list; l != NULL; l = l->next) { |
|
177 GST_LOG_OBJECT (probe, "Found device: %s", (gchar *) l->data); |
|
178 g_value_take_string (&value, (gchar *) l->data); |
|
179 l->data = NULL; |
|
180 g_value_array_append (array, &value); |
|
181 } |
|
182 g_value_unset (&value); |
|
183 g_list_free (list); |
|
184 |
|
185 return array; |
|
186 } |
|
187 |
|
188 static void |
|
189 gst_alsa_property_probe_interface_init (GstPropertyProbeInterface * iface) |
|
190 { |
|
191 iface->get_properties = gst_alsa_device_property_probe_get_properties; |
|
192 iface->probe_property = gst_alsa_device_property_probe_probe_property; |
|
193 iface->needs_probe = gst_alsa_device_property_probe_needs_probe; |
|
194 iface->get_values = gst_alsa_device_property_probe_get_values; |
|
195 } |
|
196 |
|
197 void |
|
198 gst_alsa_type_add_device_property_probe_interface (GType type) |
|
199 { |
|
200 static const GInterfaceInfo probe_iface_info = { |
|
201 (GInterfaceInitFunc) gst_alsa_property_probe_interface_init, |
|
202 NULL, |
|
203 NULL, |
|
204 }; |
|
205 |
|
206 g_type_add_interface_static (type, GST_TYPE_PROPERTY_PROBE, |
|
207 &probe_iface_info); |
|
208 } |
|