4 This file is part of gst-pulse.
6 gst-pulse is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 gst-pulse is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with gst-pulse; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 #include "pulseprobe.h"
27 #include "pulseutil.h"
29 GST_DEBUG_CATEGORY_EXTERN(pulse_debug
);
30 #define GST_CAT_DEFAULT pulse_debug
32 static void gst_pulseprobe_context_state_cb(pa_context
*context
, void *userdata
) {
33 GstPulseProbe
*c
= (GstPulseProbe
*) userdata
;
35 /* Called from the background thread! */
37 switch (pa_context_get_state(context
)) {
38 case PA_CONTEXT_READY
:
39 case PA_CONTEXT_TERMINATED
:
40 case PA_CONTEXT_FAILED
:
41 pa_threaded_mainloop_signal(c
->mainloop
, 0);
44 case PA_CONTEXT_UNCONNECTED
:
45 case PA_CONTEXT_CONNECTING
:
46 case PA_CONTEXT_AUTHORIZING
:
47 case PA_CONTEXT_SETTING_NAME
:
52 static void gst_pulseprobe_sink_info_cb(pa_context
*context
, const pa_sink_info
*i
, int eol
, void *userdata
) {
53 GstPulseProbe
*c
= (GstPulseProbe
*) userdata
;
55 /* Called from the background thread! */
58 c
->operation_success
= eol
> 0;
59 pa_threaded_mainloop_signal(c
->mainloop
, 0);
63 c
->devices
= g_list_append(c
->devices
, g_strdup(i
->name
));
67 static void gst_pulseprobe_source_info_cb(pa_context
*context
, const pa_source_info
*i
, int eol
, void *userdata
) {
68 GstPulseProbe
*c
= (GstPulseProbe
*) userdata
;
70 /* Called from the background thread! */
73 c
->operation_success
= eol
> 0;
74 pa_threaded_mainloop_signal(c
->mainloop
, 0);
78 c
->devices
= g_list_append(c
->devices
, g_strdup(i
->name
));
81 static void gst_pulseprobe_invalidate(GstPulseProbe
*c
) {
82 g_list_foreach(c
->devices
, (GFunc
) g_free
, NULL
);
83 g_list_free(c
->devices
);
88 static gboolean
gst_pulseprobe_open(GstPulseProbe
*c
) {
90 gchar
*name
= gst_pulse_client_name();
94 c
->mainloop
= pa_threaded_mainloop_new();
95 g_assert(c
->mainloop
);
97 e
= pa_threaded_mainloop_start(c
->mainloop
);
100 pa_threaded_mainloop_lock(c
->mainloop
);
102 if (!(c
->context
= pa_context_new(pa_threaded_mainloop_get_api(c
->mainloop
), name
))) {
103 GST_WARNING("Failed to create context");
104 goto unlock_and_fail
;
107 pa_context_set_state_callback(c
->context
, gst_pulseprobe_context_state_cb
, c
);
109 if (pa_context_connect(c
->context
, c
->server
, 0, NULL
) < 0) {
110 GST_WARNING("Failed to connect context: %s", pa_strerror(pa_context_errno(c
->context
)));
111 goto unlock_and_fail
;
114 /* Wait until the context is ready */
115 pa_threaded_mainloop_wait(c
->mainloop
);
117 if (pa_context_get_state(c
->context
) != PA_CONTEXT_READY
) {
118 GST_WARNING("Failed to connect context: %s", pa_strerror(pa_context_errno(c
->context
)));
119 goto unlock_and_fail
;
122 pa_threaded_mainloop_unlock(c
->mainloop
);
125 gst_pulseprobe_invalidate(c
);
132 pa_threaded_mainloop_unlock(c
->mainloop
);
139 #define CHECK_DEAD_GOTO(c, label) do { \
140 if (!(c)->context || pa_context_get_state((c)->context) != PA_CONTEXT_READY) { \
141 GST_WARNING("Not connected: %s", (c)->context ? pa_strerror(pa_context_errno((c)->context)) : "NULL"); \
146 static gboolean
gst_pulseprobe_enumerate(GstPulseProbe
*c
) {
147 pa_operation
*o
= NULL
;
149 pa_threaded_mainloop_lock(c
->mainloop
);
151 if (c
->enumerate_sinks
) {
154 if (!(o
= pa_context_get_sink_info_list(c
->context
, gst_pulseprobe_sink_info_cb
, c
))) {
155 GST_WARNING("Failed to get sink info: %s", pa_strerror(pa_context_errno(c
->context
)));
156 goto unlock_and_fail
;
159 c
->operation_success
= 0;
160 while (pa_operation_get_state(o
) != PA_OPERATION_DONE
) {
161 pa_threaded_mainloop_wait(c
->mainloop
);
162 CHECK_DEAD_GOTO(c
, unlock_and_fail
);
165 if (!c
->operation_success
) {
166 GST_WARNING("Failed to get sink info: %s", pa_strerror(pa_context_errno(c
->context
)));
167 goto unlock_and_fail
;
170 pa_operation_unref(o
);
174 if (c
->enumerate_sources
) {
175 /* Get source info */
177 if (!(o
= pa_context_get_source_info_list(c
->context
, gst_pulseprobe_source_info_cb
, c
))) {
178 GST_WARNING("Failed to get source info: %s", pa_strerror(pa_context_errno(c
->context
)));
179 goto unlock_and_fail
;
182 c
->operation_success
= 0;
183 while (pa_operation_get_state(o
) != PA_OPERATION_DONE
) {
184 pa_threaded_mainloop_wait(c
->mainloop
);
185 CHECK_DEAD_GOTO(c
, unlock_and_fail
);
188 if (!c
->operation_success
) {
189 GST_WARNING("Failed to get sink info: %s", pa_strerror(pa_context_errno(c
->context
)));
190 goto unlock_and_fail
;
193 pa_operation_unref(o
);
197 c
->devices_valid
= 1;
199 pa_threaded_mainloop_unlock(c
->mainloop
);
206 pa_operation_unref(o
);
208 pa_threaded_mainloop_unlock(c
->mainloop
);
213 static void gst_pulseprobe_close(GstPulseProbe
*c
) {
217 pa_threaded_mainloop_stop(c
->mainloop
);
220 pa_context_disconnect(c
->context
);
221 pa_context_unref(c
->context
);
226 pa_threaded_mainloop_free(c
->mainloop
);
231 GstPulseProbe
* gst_pulseprobe_new(GObjectClass
*klass
, guint prop_id
, const gchar
*server
, gboolean sinks
, gboolean sources
) {
232 GstPulseProbe
*c
= NULL
;
234 c
= g_new(GstPulseProbe
, 1);
235 c
->server
= g_strdup(server
);
236 c
->enumerate_sinks
= sinks
;
237 c
->enumerate_sources
= sources
;
242 c
->prop_id
= prop_id
;
243 c
->properties
= g_list_append(NULL
, g_object_class_find_property(klass
, "device"));
245 c
->devices_valid
= 0;
250 void gst_pulseprobe_free(GstPulseProbe
* c
) {
253 gst_pulseprobe_close(c
);
255 g_list_free(c
->properties
);
258 g_list_foreach(c
->devices
, (GFunc
) g_free
, NULL
);
259 g_list_free(c
->devices
);
264 const GList
* gst_pulseprobe_get_properties(GstPulseProbe
*c
) {
265 return c
->properties
;
268 gboolean
gst_pulseprobe_needs_probe(GstPulseProbe
*c
, guint prop_id
, const GParamSpec
*pspec
) {
270 if (prop_id
== c
->prop_id
)
271 return !c
->devices_valid
;
273 G_OBJECT_WARN_INVALID_PROPERTY_ID(c
, prop_id
, pspec
);
277 void gst_pulseprobe_probe_property(GstPulseProbe
*c
, guint prop_id
, const GParamSpec
*pspec
) {
279 if (prop_id
!= c
->prop_id
) {
280 G_OBJECT_WARN_INVALID_PROPERTY_ID(c
, prop_id
, pspec
);
284 if (gst_pulseprobe_open(c
)) {
285 gst_pulseprobe_enumerate(c
);
286 gst_pulseprobe_close(c
);
290 GValueArray
*gst_pulseprobe_get_values(GstPulseProbe
*c
, guint prop_id
, const GParamSpec
*pspec
) {
292 GValue value
= { 0 };
295 if (prop_id
!= c
->prop_id
) {
296 G_OBJECT_WARN_INVALID_PROPERTY_ID(c
, prop_id
, pspec
);
300 if (!c
->devices_valid
)
303 array
= g_value_array_new(g_list_length(c
->devices
));
304 g_value_init(&value
, G_TYPE_STRING
);
305 for (item
= c
->devices
; item
!= NULL
; item
= item
->next
) {
306 GST_WARNING("device found: %s", (const gchar
*) item
->data
);
307 g_value_set_string(&value
, (const gchar
*) item
->data
);
308 g_value_array_append(array
, &value
);
310 g_value_unset (&value
);
315 void gst_pulseprobe_set_server(GstPulseProbe
*c
, const gchar
*server
) {
318 gst_pulseprobe_invalidate(c
);
321 c
->server
= g_strdup(server
);