Mercurial > mplayer.hg
annotate libao2/ao_pulse.c @ 33259:04dc3e55cd90
Increase the maximum value of the DVB timeout to 240 seconds.
Some devices may need more time for the initial tune (e.g. firmware loading).
Let the user specify higher timeout value if there is need to.
The default remains 30 seconds.
author | iive |
---|---|
date | Sun, 01 May 2011 18:07:59 +0000 |
parents | d652e17184f9 |
children | 96019b1174b8 |
rev | line source |
---|---|
24782 | 1 /* |
2 * PulseAudio audio output driver. | |
3 * Copyright (C) 2006 Lennart Poettering | |
4 * Copyright (C) 2007 Reimar Doeffinger | |
5 * | |
6 * This file is part of MPlayer. | |
7 * | |
8 * MPlayer is free software; you can redistribute it and/or modify | |
9 * it under the terms of the GNU General Public License as published by | |
10 * the Free Software Foundation; either version 2 of the License, or | |
11 * (at your option) any later version. | |
12 * | |
13 * MPlayer is distributed in the hope that it will be useful, | |
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 * GNU General Public License for more details. | |
17 * | |
26743
0f42fb42843c
Use standard license headers with standard formatting.
diego
parents:
26649
diff
changeset
|
18 * You should have received a copy of the GNU General Public License along |
0f42fb42843c
Use standard license headers with standard formatting.
diego
parents:
26649
diff
changeset
|
19 * with MPlayer; if not, write to the Free Software Foundation, Inc., |
0f42fb42843c
Use standard license headers with standard formatting.
diego
parents:
26649
diff
changeset
|
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
24782 | 21 */ |
26743
0f42fb42843c
Use standard license headers with standard formatting.
diego
parents:
26649
diff
changeset
|
22 |
24782 | 23 #include <string.h> |
24 | |
25 #include <pulse/pulseaudio.h> | |
26 | |
27 #include "config.h" | |
28 #include "libaf/af_format.h" | |
29 #include "mp_msg.h" | |
30 #include "audio_out.h" | |
31 #include "audio_out_internal.h" | |
32 | |
33 #define PULSE_CLIENT_NAME "MPlayer" | |
34 | |
35 /** General driver info */ | |
28823 | 36 static const ao_info_t info = { |
24782 | 37 "PulseAudio audio output", |
38 "pulse", | |
39 "Lennart Poettering", | |
40 "" | |
41 }; | |
42 | |
43 /** PulseAudio playback stream object */ | |
44 static struct pa_stream *stream; | |
45 | |
46 /** PulseAudio connection context */ | |
47 static struct pa_context *context; | |
48 | |
49 /** Main event loop object */ | |
50 static struct pa_threaded_mainloop *mainloop; | |
51 | |
28633
28fe0787a67c
Work around a PulseAudio bug that causes MPlayer to hang after unpausing.
reimar
parents:
26743
diff
changeset
|
52 static int broken_pause; |
28fe0787a67c
Work around a PulseAudio bug that causes MPlayer to hang after unpausing.
reimar
parents:
26743
diff
changeset
|
53 |
24782 | 54 LIBAO_EXTERN(pulse) |
55 | |
56 #define GENERIC_ERR_MSG(ctx, str) \ | |
57 mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] "str": %s\n", \ | |
58 pa_strerror(pa_context_errno(ctx))) | |
59 | |
60 static void context_state_cb(pa_context *c, void *userdata) { | |
61 switch (pa_context_get_state(c)) { | |
62 case PA_CONTEXT_READY: | |
63 case PA_CONTEXT_TERMINATED: | |
64 case PA_CONTEXT_FAILED: | |
65 pa_threaded_mainloop_signal(mainloop, 0); | |
66 break; | |
67 } | |
68 } | |
69 | |
70 static void stream_state_cb(pa_stream *s, void *userdata) { | |
71 switch (pa_stream_get_state(s)) { | |
72 case PA_STREAM_READY: | |
73 case PA_STREAM_FAILED: | |
74 case PA_STREAM_TERMINATED: | |
75 pa_threaded_mainloop_signal(mainloop, 0); | |
76 break; | |
77 } | |
78 } | |
79 | |
80 static void stream_request_cb(pa_stream *s, size_t length, void *userdata) { | |
81 pa_threaded_mainloop_signal(mainloop, 0); | |
82 } | |
83 | |
84 static void stream_latency_update_cb(pa_stream *s, void *userdata) { | |
85 pa_threaded_mainloop_signal(mainloop, 0); | |
86 } | |
87 | |
88 static void success_cb(pa_stream *s, int success, void *userdata) { | |
89 if (userdata) | |
90 *(int *)userdata = success; | |
91 pa_threaded_mainloop_signal(mainloop, 0); | |
92 } | |
93 | |
25383 | 94 /** |
95 * \brief waits for a pulseaudio operation to finish, frees it and | |
96 * unlocks the mainloop | |
97 * \param op operation to wait for | |
98 * \return 1 if operation has finished normally (DONE state), 0 otherwise | |
99 */ | |
24782 | 100 static int waitop(pa_operation *op) { |
101 pa_operation_state_t state; | |
29117
010781e92294
Make sure waitop always unlocks the mainloop even if the operation could not
reimar
parents:
28823
diff
changeset
|
102 if (!op) { |
010781e92294
Make sure waitop always unlocks the mainloop even if the operation could not
reimar
parents:
28823
diff
changeset
|
103 pa_threaded_mainloop_unlock(mainloop); |
010781e92294
Make sure waitop always unlocks the mainloop even if the operation could not
reimar
parents:
28823
diff
changeset
|
104 return 0; |
010781e92294
Make sure waitop always unlocks the mainloop even if the operation could not
reimar
parents:
28823
diff
changeset
|
105 } |
24782 | 106 state = pa_operation_get_state(op); |
107 while (state == PA_OPERATION_RUNNING) { | |
108 pa_threaded_mainloop_wait(mainloop); | |
109 state = pa_operation_get_state(op); | |
110 } | |
111 pa_operation_unref(op); | |
112 pa_threaded_mainloop_unlock(mainloop); | |
113 return state == PA_OPERATION_DONE; | |
114 } | |
115 | |
116 static const struct format_map_s { | |
117 int mp_format; | |
118 pa_sample_format_t pa_format; | |
119 } format_maps[] = { | |
120 {AF_FORMAT_S16_LE, PA_SAMPLE_S16LE}, | |
121 {AF_FORMAT_S16_BE, PA_SAMPLE_S16BE}, | |
26649 | 122 #ifdef PA_SAMPLE_S32NE |
26603 | 123 {AF_FORMAT_S32_LE, PA_SAMPLE_S32LE}, |
124 {AF_FORMAT_S32_BE, PA_SAMPLE_S32BE}, | |
26649 | 125 #endif |
126 #ifdef PA_SAMPLE_FLOAT32NE | |
24782 | 127 {AF_FORMAT_FLOAT_LE, PA_SAMPLE_FLOAT32LE}, |
128 {AF_FORMAT_FLOAT_BE, PA_SAMPLE_FLOAT32BE}, | |
26649 | 129 #endif |
26602
d48e2d7191df
Make ao_pulse fall back to s16le format instead of just failing.
reimar
parents:
25386
diff
changeset
|
130 {AF_FORMAT_U8, PA_SAMPLE_U8}, |
24782 | 131 {AF_FORMAT_MU_LAW, PA_SAMPLE_ULAW}, |
132 {AF_FORMAT_A_LAW, PA_SAMPLE_ALAW}, | |
133 {AF_FORMAT_UNKNOWN, 0} | |
134 }; | |
135 | |
136 static int init(int rate_hz, int channels, int format, int flags) { | |
137 struct pa_sample_spec ss; | |
138 struct pa_channel_map map; | |
139 const struct format_map_s *fmt_map; | |
24921
148ca265fcb6
Change parsing to allow host == NULL and sink != NULL
reimar
parents:
24920
diff
changeset
|
140 char *devarg = NULL; |
24782 | 141 char *host = NULL; |
24919
51372a34bc7d
Make sink variable local, it is only used in one place
reimar
parents:
24782
diff
changeset
|
142 char *sink = NULL; |
30020 | 143 const char *version = pa_get_library_version(); |
24782 | 144 |
145 if (ao_subdevice) { | |
24921
148ca265fcb6
Change parsing to allow host == NULL and sink != NULL
reimar
parents:
24920
diff
changeset
|
146 devarg = strdup(ao_subdevice); |
148ca265fcb6
Change parsing to allow host == NULL and sink != NULL
reimar
parents:
24920
diff
changeset
|
147 sink = strchr(devarg, ':'); |
24920 | 148 if (sink) *sink++ = 0; |
24921
148ca265fcb6
Change parsing to allow host == NULL and sink != NULL
reimar
parents:
24920
diff
changeset
|
149 if (devarg[0]) host = devarg; |
24782 | 150 } |
151 | |
28633
28fe0787a67c
Work around a PulseAudio bug that causes MPlayer to hang after unpausing.
reimar
parents:
26743
diff
changeset
|
152 broken_pause = 0; |
29120
aa06c29db609
Disable pause-hack from PulseAudio 0.9.15 on, it should be fixed.
reimar
parents:
29119
diff
changeset
|
153 // not sure which versions are affected, assume 0.9.11* to 0.9.14* |
28633
28fe0787a67c
Work around a PulseAudio bug that causes MPlayer to hang after unpausing.
reimar
parents:
26743
diff
changeset
|
154 // known bad: 0.9.14, 0.9.13 |
29120
aa06c29db609
Disable pause-hack from PulseAudio 0.9.15 on, it should be fixed.
reimar
parents:
29119
diff
changeset
|
155 // known good: 0.9.9, 0.9.10, 0.9.15 |
28633
28fe0787a67c
Work around a PulseAudio bug that causes MPlayer to hang after unpausing.
reimar
parents:
26743
diff
changeset
|
156 // to test: pause, wait ca. 5 seconds framestep and see if MPlayer hangs somewhen |
29120
aa06c29db609
Disable pause-hack from PulseAudio 0.9.15 on, it should be fixed.
reimar
parents:
29119
diff
changeset
|
157 if (strncmp(version, "0.9.1", 5) == 0 && version[5] >= '1' && version[5] <= '4') { |
28633
28fe0787a67c
Work around a PulseAudio bug that causes MPlayer to hang after unpausing.
reimar
parents:
26743
diff
changeset
|
158 mp_msg(MSGT_AO, MSGL_WARN, "[pulse] working around probably broken pause functionality,\n" |
28fe0787a67c
Work around a PulseAudio bug that causes MPlayer to hang after unpausing.
reimar
parents:
26743
diff
changeset
|
159 " see http://www.pulseaudio.org/ticket/440\n"); |
28fe0787a67c
Work around a PulseAudio bug that causes MPlayer to hang after unpausing.
reimar
parents:
26743
diff
changeset
|
160 broken_pause = 1; |
28fe0787a67c
Work around a PulseAudio bug that causes MPlayer to hang after unpausing.
reimar
parents:
26743
diff
changeset
|
161 } |
28fe0787a67c
Work around a PulseAudio bug that causes MPlayer to hang after unpausing.
reimar
parents:
26743
diff
changeset
|
162 |
24782 | 163 ss.channels = channels; |
164 ss.rate = rate_hz; | |
165 | |
166 ao_data.samplerate = rate_hz; | |
167 ao_data.channels = channels; | |
168 | |
169 fmt_map = format_maps; | |
170 while (fmt_map->mp_format != format) { | |
171 if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) { | |
26602
d48e2d7191df
Make ao_pulse fall back to s16le format instead of just failing.
reimar
parents:
25386
diff
changeset
|
172 mp_msg(MSGT_AO, MSGL_V, "AO: [pulse] Unsupported format, using default\n"); |
d48e2d7191df
Make ao_pulse fall back to s16le format instead of just failing.
reimar
parents:
25386
diff
changeset
|
173 fmt_map = format_maps; |
d48e2d7191df
Make ao_pulse fall back to s16le format instead of just failing.
reimar
parents:
25386
diff
changeset
|
174 break; |
24782 | 175 } |
176 fmt_map++; | |
177 } | |
26602
d48e2d7191df
Make ao_pulse fall back to s16le format instead of just failing.
reimar
parents:
25386
diff
changeset
|
178 ao_data.format = fmt_map->mp_format; |
24782 | 179 ss.format = fmt_map->pa_format; |
180 | |
181 if (!pa_sample_spec_valid(&ss)) { | |
182 mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Invalid sample spec\n"); | |
183 goto fail; | |
184 } | |
185 | |
186 pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA); | |
187 ao_data.bps = pa_bytes_per_second(&ss); | |
188 | |
189 if (!(mainloop = pa_threaded_mainloop_new())) { | |
190 mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate main loop\n"); | |
191 goto fail; | |
192 } | |
193 | |
194 if (!(context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), PULSE_CLIENT_NAME))) { | |
195 mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate context\n"); | |
196 goto fail; | |
197 } | |
198 | |
199 pa_context_set_state_callback(context, context_state_cb, NULL); | |
200 | |
201 if (pa_context_connect(context, host, 0, NULL) < 0) | |
202 goto fail; | |
203 | |
204 pa_threaded_mainloop_lock(mainloop); | |
205 | |
206 if (pa_threaded_mainloop_start(mainloop) < 0) | |
207 goto unlock_and_fail; | |
208 | |
209 /* Wait until the context is ready */ | |
210 pa_threaded_mainloop_wait(mainloop); | |
211 | |
212 if (pa_context_get_state(context) != PA_CONTEXT_READY) | |
213 goto unlock_and_fail; | |
214 | |
215 if (!(stream = pa_stream_new(context, "audio stream", &ss, &map))) | |
216 goto unlock_and_fail; | |
217 | |
218 pa_stream_set_state_callback(stream, stream_state_cb, NULL); | |
219 pa_stream_set_write_callback(stream, stream_request_cb, NULL); | |
220 pa_stream_set_latency_update_callback(stream, stream_latency_update_cb, NULL); | |
221 | |
30019
2d62e9614c8d
Allow pulseaudio to restore the previous volume on init instead of forcing to
reimar
parents:
30018
diff
changeset
|
222 if (pa_stream_connect_playback(stream, sink, NULL, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL) < 0) |
24782 | 223 goto unlock_and_fail; |
224 | |
225 /* Wait until the stream is ready */ | |
226 pa_threaded_mainloop_wait(mainloop); | |
227 | |
228 if (pa_stream_get_state(stream) != PA_STREAM_READY) | |
229 goto unlock_and_fail; | |
230 | |
231 pa_threaded_mainloop_unlock(mainloop); | |
232 | |
24921
148ca265fcb6
Change parsing to allow host == NULL and sink != NULL
reimar
parents:
24920
diff
changeset
|
233 free(devarg); |
24782 | 234 return 1; |
235 | |
236 unlock_and_fail: | |
237 | |
238 if (mainloop) | |
239 pa_threaded_mainloop_unlock(mainloop); | |
240 | |
241 fail: | |
242 if (context) | |
243 GENERIC_ERR_MSG(context, "Init failed"); | |
24921
148ca265fcb6
Change parsing to allow host == NULL and sink != NULL
reimar
parents:
24920
diff
changeset
|
244 free(devarg); |
24782 | 245 uninit(1); |
246 return 0; | |
247 } | |
248 | |
249 /** Destroy libao driver */ | |
250 static void uninit(int immed) { | |
251 if (stream && !immed) { | |
252 pa_threaded_mainloop_lock(mainloop); | |
253 waitop(pa_stream_drain(stream, success_cb, NULL)); | |
254 } | |
255 | |
256 if (mainloop) | |
257 pa_threaded_mainloop_stop(mainloop); | |
258 | |
259 if (stream) { | |
260 pa_stream_disconnect(stream); | |
261 pa_stream_unref(stream); | |
262 stream = NULL; | |
263 } | |
264 | |
265 if (context) { | |
266 pa_context_disconnect(context); | |
267 pa_context_unref(context); | |
268 context = NULL; | |
269 } | |
270 | |
271 if (mainloop) { | |
272 pa_threaded_mainloop_free(mainloop); | |
273 mainloop = NULL; | |
274 } | |
275 } | |
276 | |
277 /** Play the specified data to the pulseaudio server */ | |
278 static int play(void* data, int len, int flags) { | |
25386
bd46b759fd35
pa_stream_write reportedly needs locking of the main loop
reimar
parents:
25385
diff
changeset
|
279 pa_threaded_mainloop_lock(mainloop); |
25385 | 280 if (pa_stream_write(stream, data, len, NULL, 0, PA_SEEK_RELATIVE) < 0) { |
281 GENERIC_ERR_MSG(context, "pa_stream_write() failed"); | |
25386
bd46b759fd35
pa_stream_write reportedly needs locking of the main loop
reimar
parents:
25385
diff
changeset
|
282 len = -1; |
25385 | 283 } |
25386
bd46b759fd35
pa_stream_write reportedly needs locking of the main loop
reimar
parents:
25385
diff
changeset
|
284 pa_threaded_mainloop_unlock(mainloop); |
24782 | 285 return len; |
286 } | |
287 | |
288 static void cork(int b) { | |
289 int success = 0; | |
290 pa_threaded_mainloop_lock(mainloop); | |
291 if (!waitop(pa_stream_cork(stream, b, success_cb, &success)) || | |
292 !success) | |
293 GENERIC_ERR_MSG(context, "pa_stream_cork() failed"); | |
294 } | |
295 | |
296 /** Pause the audio stream by corking it on the server */ | |
297 static void audio_pause(void) { | |
298 cork(1); | |
299 } | |
300 | |
301 /** Resume the audio stream by uncorking it on the server */ | |
302 static void audio_resume(void) { | |
28633
28fe0787a67c
Work around a PulseAudio bug that causes MPlayer to hang after unpausing.
reimar
parents:
26743
diff
changeset
|
303 // without this, certain versions will cause an infinite hang because |
28fe0787a67c
Work around a PulseAudio bug that causes MPlayer to hang after unpausing.
reimar
parents:
26743
diff
changeset
|
304 // pa_stream_writable_size returns 0 always. |
28fe0787a67c
Work around a PulseAudio bug that causes MPlayer to hang after unpausing.
reimar
parents:
26743
diff
changeset
|
305 // Note that this workaround causes A-V desync after pause |
28fe0787a67c
Work around a PulseAudio bug that causes MPlayer to hang after unpausing.
reimar
parents:
26743
diff
changeset
|
306 if (broken_pause) reset(); |
24782 | 307 cork(0); |
308 } | |
309 | |
310 /** Reset the audio stream, i.e. flush the playback buffer on the server side */ | |
311 static void reset(void) { | |
312 int success = 0; | |
313 pa_threaded_mainloop_lock(mainloop); | |
314 if (!waitop(pa_stream_flush(stream, success_cb, &success)) || | |
315 !success) | |
316 GENERIC_ERR_MSG(context, "pa_stream_flush() failed"); | |
317 } | |
318 | |
319 /** Return number of bytes that may be written to the server without blocking */ | |
320 static int get_space(void) { | |
321 size_t l; | |
322 pa_threaded_mainloop_lock(mainloop); | |
323 l = pa_stream_writable_size(stream); | |
324 pa_threaded_mainloop_unlock(mainloop); | |
24923 | 325 return l; |
24782 | 326 } |
327 | |
328 /** Return the current latency in seconds */ | |
329 static float get_delay(void) { | |
330 pa_usec_t latency = (pa_usec_t) -1; | |
331 pa_threaded_mainloop_lock(mainloop); | |
332 while (pa_stream_get_latency(stream, &latency, NULL) < 0) { | |
333 if (pa_context_errno(context) != PA_ERR_NODATA) { | |
334 GENERIC_ERR_MSG(context, "pa_stream_get_latency() failed"); | |
335 break; | |
336 } | |
337 /* Wait until latency data is available again */ | |
338 pa_threaded_mainloop_wait(mainloop); | |
339 } | |
340 pa_threaded_mainloop_unlock(mainloop); | |
341 return latency == (pa_usec_t) -1 ? 0 : latency / 1000000.0; | |
342 } | |
343 | |
344 /** A callback function that is called when the | |
345 * pa_context_get_sink_input_info() operation completes. Saves the | |
346 * volume field of the specified structure to the global variable volume. */ | |
347 static void info_func(struct pa_context *c, const struct pa_sink_input_info *i, int is_last, void *userdata) { | |
30018
ae5d67d8ee95
Get rid of global volume variable, it is only used for temporary values.
reimar
parents:
29120
diff
changeset
|
348 struct pa_cvolume *volume = userdata; |
24782 | 349 if (is_last < 0) { |
350 GENERIC_ERR_MSG(context, "Failed to get sink input info"); | |
351 return; | |
352 } | |
353 if (!i) | |
354 return; | |
30018
ae5d67d8ee95
Get rid of global volume variable, it is only used for temporary values.
reimar
parents:
29120
diff
changeset
|
355 *volume = i->volume; |
24782 | 356 pa_threaded_mainloop_signal(mainloop, 0); |
357 } | |
358 | |
359 static int control(int cmd, void *arg) { | |
360 switch (cmd) { | |
361 case AOCONTROL_GET_VOLUME: { | |
362 ao_control_vol_t *vol = arg; | |
363 uint32_t devidx = pa_stream_get_index(stream); | |
30018
ae5d67d8ee95
Get rid of global volume variable, it is only used for temporary values.
reimar
parents:
29120
diff
changeset
|
364 struct pa_cvolume volume; |
24782 | 365 pa_threaded_mainloop_lock(mainloop); |
30018
ae5d67d8ee95
Get rid of global volume variable, it is only used for temporary values.
reimar
parents:
29120
diff
changeset
|
366 if (!waitop(pa_context_get_sink_input_info(context, devidx, info_func, &volume))) { |
24782 | 367 GENERIC_ERR_MSG(context, "pa_stream_get_sink_input_info() failed"); |
368 return CONTROL_ERROR; | |
369 } | |
370 | |
371 if (volume.channels != 2) | |
372 vol->left = vol->right = pa_cvolume_avg(&volume)*100/PA_VOLUME_NORM; | |
373 else { | |
374 vol->left = volume.values[0]*100/PA_VOLUME_NORM; | |
375 vol->right = volume.values[1]*100/PA_VOLUME_NORM; | |
376 } | |
377 | |
378 return CONTROL_OK; | |
379 } | |
380 | |
381 case AOCONTROL_SET_VOLUME: { | |
382 const ao_control_vol_t *vol = arg; | |
383 pa_operation *o; | |
30018
ae5d67d8ee95
Get rid of global volume variable, it is only used for temporary values.
reimar
parents:
29120
diff
changeset
|
384 struct pa_cvolume volume; |
24782 | 385 |
30018
ae5d67d8ee95
Get rid of global volume variable, it is only used for temporary values.
reimar
parents:
29120
diff
changeset
|
386 pa_cvolume_reset(&volume, ao_data.channels); |
24782 | 387 if (volume.channels != 2) |
388 pa_cvolume_set(&volume, volume.channels, (pa_volume_t)vol->left*PA_VOLUME_NORM/100); | |
389 else { | |
390 volume.values[0] = (pa_volume_t)vol->left*PA_VOLUME_NORM/100; | |
391 volume.values[1] = (pa_volume_t)vol->right*PA_VOLUME_NORM/100; | |
392 } | |
393 | |
29118
17c6a6c192a7
Also lock the mainloop when doing adjusting the volume for PulseAudio.
reimar
parents:
29117
diff
changeset
|
394 pa_threaded_mainloop_lock(mainloop); |
29119
52afd0fe451b
Split oversized of "argument" onto a separate line.
reimar
parents:
29118
diff
changeset
|
395 o = pa_context_set_sink_input_volume(context, pa_stream_get_index(stream), &volume, NULL, NULL); |
52afd0fe451b
Split oversized of "argument" onto a separate line.
reimar
parents:
29118
diff
changeset
|
396 if (!o) { |
29118
17c6a6c192a7
Also lock the mainloop when doing adjusting the volume for PulseAudio.
reimar
parents:
29117
diff
changeset
|
397 pa_threaded_mainloop_unlock(mainloop); |
24782 | 398 GENERIC_ERR_MSG(context, "pa_context_set_sink_input_volume() failed"); |
399 return CONTROL_ERROR; | |
400 } | |
401 /* We don't wait for completion here */ | |
402 pa_operation_unref(o); | |
29118
17c6a6c192a7
Also lock the mainloop when doing adjusting the volume for PulseAudio.
reimar
parents:
29117
diff
changeset
|
403 pa_threaded_mainloop_unlock(mainloop); |
24782 | 404 return CONTROL_OK; |
405 } | |
406 | |
407 default: | |
408 return CONTROL_UNKNOWN; | |
409 } | |
410 } |