Line 0
Link Here
|
|
|
1 |
#include <assert.h> |
2 |
#include <string.h> |
3 |
#include <stdio.h> |
4 |
|
5 |
#include <pulse/pulseaudio.h> |
6 |
|
7 |
#include "config.h" |
8 |
#include "libaf/af_format.h" |
9 |
#include "mp_msg.h" |
10 |
#include "audio_out.h" |
11 |
#include "audio_out_internal.h" |
12 |
|
13 |
#define PULSE_CLIENT_NAME "MPlayer" |
14 |
|
15 |
/*#define PULSE_DEBUG*/ |
16 |
|
17 |
/** General driver info */ |
18 |
static ao_info_t info = { |
19 |
"PulseAudio audio output", |
20 |
"pulse", |
21 |
"Lennart Poettering", |
22 |
"" |
23 |
}; |
24 |
|
25 |
/** The sink to connect to */ |
26 |
static char *sink = NULL; |
27 |
|
28 |
/** PulseAudio playback stream object */ |
29 |
static struct pa_stream *stream = NULL; |
30 |
|
31 |
/** PulseAudio connection context */ |
32 |
static struct pa_context *context = NULL; |
33 |
|
34 |
/** Main event loop object */ |
35 |
static struct pa_threaded_mainloop *mainloop = NULL; |
36 |
|
37 |
/** A temporary variable to store the current volume */ |
38 |
static pa_cvolume volume; |
39 |
static int volume_initialized = 0; |
40 |
|
41 |
/** Some special libao macro magic */ |
42 |
LIBAO_EXTERN(pulse) |
43 |
|
44 |
#define CHECK_DEAD_GOTO(label) do { \ |
45 |
if (!context || pa_context_get_state(context) != PA_CONTEXT_READY || \ |
46 |
!stream || pa_stream_get_state(stream) != PA_STREAM_READY) { \ |
47 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Connection died: %s\n", context ? pa_strerror(pa_context_errno(context)) : "NULL"); \ |
48 |
goto label; \ |
49 |
} \ |
50 |
} while(0); |
51 |
|
52 |
static void context_state_cb(pa_context *c, void *userdata) { |
53 |
assert(c); |
54 |
|
55 |
switch (pa_context_get_state(c)) { |
56 |
case PA_CONTEXT_READY: |
57 |
case PA_CONTEXT_TERMINATED: |
58 |
case PA_CONTEXT_FAILED: |
59 |
pa_threaded_mainloop_signal(mainloop, 0); |
60 |
break; |
61 |
|
62 |
case PA_CONTEXT_UNCONNECTED: |
63 |
case PA_CONTEXT_CONNECTING: |
64 |
case PA_CONTEXT_AUTHORIZING: |
65 |
case PA_CONTEXT_SETTING_NAME: |
66 |
break; |
67 |
} |
68 |
} |
69 |
|
70 |
static void stream_state_cb(pa_stream *s, void * userdata) { |
71 |
assert(s); |
72 |
|
73 |
switch (pa_stream_get_state(s)) { |
74 |
|
75 |
case PA_STREAM_READY: |
76 |
case PA_STREAM_FAILED: |
77 |
case PA_STREAM_TERMINATED: |
78 |
pa_threaded_mainloop_signal(mainloop, 0); |
79 |
break; |
80 |
|
81 |
case PA_STREAM_UNCONNECTED: |
82 |
case PA_STREAM_CREATING: |
83 |
break; |
84 |
} |
85 |
} |
86 |
|
87 |
static void stream_request_cb(pa_stream *s, size_t length, void *userdata) { |
88 |
assert(s); |
89 |
|
90 |
pa_threaded_mainloop_signal(mainloop, 0); |
91 |
} |
92 |
|
93 |
static void stream_latency_update_cb(pa_stream *s, void *userdata) { |
94 |
assert(s); |
95 |
|
96 |
pa_threaded_mainloop_signal(mainloop, 0); |
97 |
} |
98 |
|
99 |
static void success_cb(pa_stream *s, int success, void *userdata) { |
100 |
assert(s); |
101 |
|
102 |
if (userdata) |
103 |
*(int*) userdata = success; |
104 |
pa_threaded_mainloop_signal(mainloop, 0); |
105 |
} |
106 |
|
107 |
/** libao initialization function, arguments are sampling frequency, |
108 |
* number of channels, sample type and some flags */ |
109 |
static int init(int rate_hz, int channels, int format, int flags) { |
110 |
struct pa_sample_spec ss; |
111 |
struct pa_channel_map map; |
112 |
char hn[128]; |
113 |
char *host = NULL; |
114 |
|
115 |
assert(!context); |
116 |
assert(!stream); |
117 |
assert(!mainloop); |
118 |
|
119 |
if (ao_subdevice) { |
120 |
int i = strcspn(ao_subdevice, ":"); |
121 |
if ((size_t) i >= sizeof(hn)) |
122 |
i = sizeof(hn)-1; |
123 |
|
124 |
if (i > 0) { |
125 |
strncpy(host = hn, ao_subdevice, i); |
126 |
hn[i] = 0; |
127 |
} |
128 |
|
129 |
if (ao_subdevice[i] == ':') |
130 |
sink = ao_subdevice+i+1; |
131 |
} |
132 |
|
133 |
ss.channels = channels; |
134 |
ss.rate = rate_hz; |
135 |
|
136 |
ao_data.samplerate = rate_hz; |
137 |
ao_data.format = format; |
138 |
ao_data.channels = channels; |
139 |
|
140 |
switch (format) { |
141 |
case AF_FORMAT_U8: |
142 |
ss.format = PA_SAMPLE_U8; |
143 |
break; |
144 |
case AF_FORMAT_S16_LE: |
145 |
ss.format = PA_SAMPLE_S16LE; |
146 |
break; |
147 |
case AF_FORMAT_S16_BE: |
148 |
ss.format = PA_SAMPLE_S16BE; |
149 |
break; |
150 |
case AF_FORMAT_FLOAT_LE: |
151 |
ss.format = PA_SAMPLE_FLOAT32LE; |
152 |
break; |
153 |
case AF_FORMAT_FLOAT_BE: |
154 |
ss.format = PA_SAMPLE_FLOAT32BE; |
155 |
break; |
156 |
case AF_FORMAT_MU_LAW: |
157 |
ss.format = PA_SAMPLE_ULAW; |
158 |
break; |
159 |
case AF_FORMAT_A_LAW: |
160 |
ss.format = PA_SAMPLE_ALAW; |
161 |
break; |
162 |
default: |
163 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Unsupported sample spec\n"); |
164 |
goto fail; |
165 |
} |
166 |
|
167 |
if (!pa_sample_spec_valid(&ss)) { |
168 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Invalid sample spec\n"); |
169 |
goto fail; |
170 |
} |
171 |
|
172 |
pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA); |
173 |
ao_data.bps = pa_bytes_per_second(&ss); |
174 |
|
175 |
if (!volume_initialized || ss.channels != volume.channels) { |
176 |
pa_cvolume_reset(&volume, ss.channels); |
177 |
volume_initialized = 1; |
178 |
} |
179 |
|
180 |
if (!(mainloop = pa_threaded_mainloop_new())) { |
181 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate main loop\n"); |
182 |
goto fail; |
183 |
} |
184 |
|
185 |
if (!(context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), PULSE_CLIENT_NAME))) { |
186 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate context\n"); |
187 |
goto fail; |
188 |
} |
189 |
|
190 |
pa_context_set_state_callback(context, context_state_cb, NULL); |
191 |
|
192 |
if (pa_context_connect(context, host, 0, NULL) < 0) { |
193 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to connect to server: %s\n", pa_strerror(pa_context_errno(context))); |
194 |
goto fail; |
195 |
} |
196 |
|
197 |
pa_threaded_mainloop_lock(mainloop); |
198 |
|
199 |
if (pa_threaded_mainloop_start(mainloop) < 0) { |
200 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to start main loop\n"); |
201 |
goto unlock_and_fail; |
202 |
} |
203 |
|
204 |
/* Wait until the context is ready */ |
205 |
pa_threaded_mainloop_wait(mainloop); |
206 |
|
207 |
if (pa_context_get_state(context) != PA_CONTEXT_READY) { |
208 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to connect to server: %s\n", pa_strerror(pa_context_errno(context))); |
209 |
goto unlock_and_fail; |
210 |
} |
211 |
|
212 |
if (!(stream = pa_stream_new(context, "audio stream", &ss, &map))) { |
213 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to create stream: %s\n", pa_strerror(pa_context_errno(context))); |
214 |
goto unlock_and_fail; |
215 |
} |
216 |
|
217 |
pa_stream_set_state_callback(stream, stream_state_cb, NULL); |
218 |
pa_stream_set_write_callback(stream, stream_request_cb, NULL); |
219 |
pa_stream_set_latency_update_callback(stream, stream_latency_update_cb, NULL); |
220 |
|
221 |
if (pa_stream_connect_playback(stream, sink, NULL, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, &volume, NULL) < 0) { |
222 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to connect stream: %s\n", pa_strerror(pa_context_errno(context))); |
223 |
goto unlock_and_fail; |
224 |
} |
225 |
|
226 |
/* Wait until the stream is ready */ |
227 |
pa_threaded_mainloop_wait(mainloop); |
228 |
|
229 |
if (pa_stream_get_state(stream) != PA_STREAM_READY) { |
230 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to connect to server: %s\n", pa_strerror(pa_context_errno(context))); |
231 |
goto unlock_and_fail; |
232 |
} |
233 |
|
234 |
pa_threaded_mainloop_unlock(mainloop); |
235 |
|
236 |
return 1; |
237 |
|
238 |
unlock_and_fail: |
239 |
|
240 |
if (mainloop) |
241 |
pa_threaded_mainloop_unlock(mainloop); |
242 |
|
243 |
fail: |
244 |
|
245 |
uninit(1); |
246 |
return 0; |
247 |
} |
248 |
|
249 |
/** Destroy libao driver */ |
250 |
static void uninit(int immed) { |
251 |
#ifdef PULSE_DEBUG |
252 |
fprintf(stderr, "uninit(%i) ***\n", immed); |
253 |
#endif |
254 |
|
255 |
if (stream) { |
256 |
if (!immed) { |
257 |
pa_operation *o; |
258 |
|
259 |
pa_threaded_mainloop_lock(mainloop); |
260 |
|
261 |
if ((o = pa_stream_drain(stream, success_cb, NULL))) { |
262 |
|
263 |
while (pa_operation_get_state(o) != PA_OPERATION_DONE) { |
264 |
CHECK_DEAD_GOTO(fail); |
265 |
pa_threaded_mainloop_wait(mainloop); |
266 |
} |
267 |
|
268 |
fail: |
269 |
|
270 |
pa_operation_unref(o); |
271 |
} |
272 |
|
273 |
pa_threaded_mainloop_unlock(mainloop); |
274 |
} |
275 |
} |
276 |
|
277 |
if (mainloop) |
278 |
pa_threaded_mainloop_stop(mainloop); |
279 |
|
280 |
if (stream) { |
281 |
pa_stream_disconnect(stream); |
282 |
pa_stream_unref(stream); |
283 |
stream = NULL; |
284 |
} |
285 |
|
286 |
if (context) { |
287 |
pa_context_disconnect(context); |
288 |
pa_context_unref(context); |
289 |
context = NULL; |
290 |
} |
291 |
|
292 |
if (mainloop) { |
293 |
pa_threaded_mainloop_free(mainloop); |
294 |
mainloop = NULL; |
295 |
} |
296 |
} |
297 |
|
298 |
/** Play the specified data to the pulseaudio server */ |
299 |
static int play(void* data, int len, int flags) { |
300 |
int r = -1; |
301 |
pa_operation *o = NULL; |
302 |
|
303 |
assert(stream); |
304 |
assert(context); |
305 |
|
306 |
#ifdef PULSE_DEBUG |
307 |
fprintf(stderr, "playing %lu ***\n", len); |
308 |
#endif |
309 |
|
310 |
pa_threaded_mainloop_lock(mainloop); |
311 |
|
312 |
CHECK_DEAD_GOTO(fail); |
313 |
|
314 |
if (len) { |
315 |
|
316 |
if (pa_stream_write(stream, data, len, NULL, 0, PA_SEEK_RELATIVE) < 0) { |
317 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] pa_stream_write() failed: %s\n", pa_strerror(pa_context_errno(context))); |
318 |
goto fail; |
319 |
} |
320 |
|
321 |
} else { |
322 |
|
323 |
if (!(o = pa_stream_trigger(stream, NULL, NULL))) { |
324 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] pa_stream_trigger() failed: %s\n", pa_strerror(pa_context_errno(context))); |
325 |
goto fail; |
326 |
} |
327 |
|
328 |
/* We don't wait for this operation to complete */ |
329 |
} |
330 |
|
331 |
r = len; |
332 |
|
333 |
fail: |
334 |
if (o) |
335 |
pa_operation_unref(o); |
336 |
|
337 |
pa_threaded_mainloop_unlock(mainloop); |
338 |
|
339 |
return r; |
340 |
} |
341 |
|
342 |
static void cork(int b) { |
343 |
pa_operation *o = NULL; |
344 |
int success = 0; |
345 |
|
346 |
assert(stream); |
347 |
assert(context); |
348 |
|
349 |
#ifdef PULSE_DEBUG |
350 |
fprintf(stderr, "cork(%i) ***\n", b); |
351 |
#endif |
352 |
|
353 |
pa_threaded_mainloop_lock(mainloop); |
354 |
|
355 |
CHECK_DEAD_GOTO(fail); |
356 |
|
357 |
if (!(o = pa_stream_cork(stream, b, success_cb, &success))) { |
358 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] pa_stream_cork() failed: %s\n", pa_strerror(pa_context_errno(context))); |
359 |
goto fail; |
360 |
} |
361 |
|
362 |
while (pa_operation_get_state(o) != PA_OPERATION_DONE) { |
363 |
CHECK_DEAD_GOTO(fail); |
364 |
pa_threaded_mainloop_wait(mainloop); |
365 |
} |
366 |
|
367 |
if (!success) |
368 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] pa_stream_cork() failed: %s\n", pa_strerror(pa_context_errno(context))); |
369 |
|
370 |
pa_operation_unref(o); |
371 |
|
372 |
fail: |
373 |
pa_threaded_mainloop_unlock(mainloop); |
374 |
} |
375 |
|
376 |
/** Pause the audio stream by corking it on the server */ |
377 |
static void audio_pause(void) { |
378 |
cork(1); |
379 |
} |
380 |
|
381 |
/** Resume the audio stream by uncorking it on the server */ |
382 |
static void audio_resume(void) { |
383 |
cork(0); |
384 |
} |
385 |
|
386 |
/** Reset the audio stream, i.e. flush the playback buffer on the server side */ |
387 |
static void reset(void) { |
388 |
pa_operation *o = NULL; |
389 |
int success = 0; |
390 |
|
391 |
assert(stream); |
392 |
assert(context); |
393 |
|
394 |
#ifdef PULSE_DEBUG |
395 |
fprintf(stderr, "reset() ***\n"); |
396 |
#endif |
397 |
|
398 |
pa_threaded_mainloop_lock(mainloop); |
399 |
|
400 |
CHECK_DEAD_GOTO(fail); |
401 |
|
402 |
if (!(o = pa_stream_flush(stream, success_cb, &success))) { |
403 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] pa_stream_flush() failed: %s\n", pa_strerror(pa_context_errno(context))); |
404 |
goto fail; |
405 |
} |
406 |
|
407 |
while (pa_operation_get_state(o) != PA_OPERATION_DONE) { |
408 |
CHECK_DEAD_GOTO(fail); |
409 |
pa_threaded_mainloop_wait(mainloop); |
410 |
} |
411 |
|
412 |
if (!success) |
413 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] pa_stream_flush() failed: %s\n", pa_strerror(pa_context_errno(context))); |
414 |
|
415 |
pa_operation_unref(o); |
416 |
|
417 |
fail: |
418 |
pa_threaded_mainloop_unlock(mainloop); |
419 |
} |
420 |
|
421 |
/** Return number of bytes that may be written to the server without blocking */ |
422 |
static int get_space(void) { |
423 |
size_t l = (size_t) -1; |
424 |
|
425 |
pa_threaded_mainloop_lock(mainloop); |
426 |
|
427 |
CHECK_DEAD_GOTO(fail); |
428 |
|
429 |
l = pa_stream_writable_size(stream); |
430 |
|
431 |
#ifdef PULSE_DEBUG |
432 |
fprintf(stderr, "\nspace = %lu\n", l); |
433 |
#endif |
434 |
|
435 |
fail: |
436 |
|
437 |
pa_threaded_mainloop_unlock(mainloop); |
438 |
|
439 |
return l == (size_t) -1 ? -1 : (int) l; |
440 |
} |
441 |
|
442 |
/** Return the current latency in seconds */ |
443 |
static float get_delay(void) { |
444 |
pa_usec_t latency = (pa_usec_t) -1; |
445 |
|
446 |
pa_threaded_mainloop_lock(mainloop); |
447 |
|
448 |
for (;;) { |
449 |
CHECK_DEAD_GOTO(fail); |
450 |
|
451 |
if (pa_stream_get_latency(stream, &latency, NULL) >= 0) |
452 |
break; |
453 |
|
454 |
if (pa_context_errno(context) != PA_ERR_NODATA) { |
455 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] pa_stream_get_latency() failed: %s\n", pa_strerror(pa_context_errno(context))); |
456 |
goto fail; |
457 |
} |
458 |
|
459 |
/* Wait until latency data is available again */ |
460 |
pa_threaded_mainloop_wait(mainloop); |
461 |
} |
462 |
|
463 |
#ifdef PULSE_DEBUG |
464 |
fprintf(stderr, "latency=%0.3f sec\n", (double) latency / 1000000); |
465 |
#endif |
466 |
|
467 |
fail: |
468 |
pa_threaded_mainloop_unlock(mainloop); |
469 |
|
470 |
return (latency == (pa_usec_t) -1) ? 0 : ((float) latency / 1000000.0); |
471 |
} |
472 |
|
473 |
/** A callback function that is called when the |
474 |
* pa_context_get_sink_input_info() operation completes. Saves the |
475 |
* volume field of the specified structure to the global variable volume. */ |
476 |
static void info_func(struct pa_context *c, const struct pa_sink_input_info *i, int is_last, void *userdata) { |
477 |
if (is_last < 0) { |
478 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to get sink input info: %s\n", pa_strerror(pa_context_errno(context))); |
479 |
return; |
480 |
} |
481 |
|
482 |
if (!i) |
483 |
return; |
484 |
|
485 |
volume = i->volume; |
486 |
volume_initialized = 1; |
487 |
|
488 |
pa_threaded_mainloop_signal(mainloop, 0); |
489 |
} |
490 |
|
491 |
/** Issue special libao controls on the device */ |
492 |
static int control(int cmd, void *arg) { |
493 |
|
494 |
if (!context || !stream) |
495 |
return CONTROL_ERROR; |
496 |
|
497 |
switch (cmd) { |
498 |
|
499 |
case AOCONTROL_GET_VOLUME: { |
500 |
/* Return the current volume of the playback stream */ |
501 |
ao_control_vol_t *vol = (ao_control_vol_t*) arg; |
502 |
pa_operation *o; |
503 |
|
504 |
if (!(o = pa_context_get_sink_input_info(context, pa_stream_get_index(stream), info_func, NULL))) { |
505 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] pa_stream_get_sink_input_info() failed: %s\n", pa_strerror(pa_context_errno(context))); |
506 |
return CONTROL_ERROR; |
507 |
} |
508 |
|
509 |
while (pa_operation_get_state(o) != PA_OPERATION_DONE) { |
510 |
CHECK_DEAD_GOTO(fail); |
511 |
pa_threaded_mainloop_wait(mainloop); |
512 |
} |
513 |
|
514 |
fail: |
515 |
pa_operation_unref(o); |
516 |
|
517 |
if (!volume_initialized) { |
518 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] pa_stream_get_sink_input_info() failed: %s\n", pa_strerror(pa_context_errno(context))); |
519 |
return CONTROL_ERROR; |
520 |
} |
521 |
|
522 |
if (volume.channels != 2) |
523 |
vol->left = vol->right = (int) ((pa_cvolume_avg(&volume)*100)/PA_VOLUME_NORM); |
524 |
else { |
525 |
vol->left = (int) (volume.values[0]*100)/PA_VOLUME_NORM; |
526 |
vol->right = (int) (volume.values[1]*100)/PA_VOLUME_NORM; |
527 |
} |
528 |
|
529 |
return CONTROL_OK; |
530 |
} |
531 |
|
532 |
case AOCONTROL_SET_VOLUME: { |
533 |
/* Set the playback volume of the stream */ |
534 |
const ao_control_vol_t *vol = (ao_control_vol_t*) arg; |
535 |
pa_operation *o; |
536 |
|
537 |
if (!volume_initialized) { |
538 |
pa_cvolume_reset(&volume, 2); |
539 |
volume_initialized = 1; |
540 |
} |
541 |
|
542 |
if (volume.channels != 2) |
543 |
pa_cvolume_set(&volume, volume.channels, ((pa_volume_t) vol->left*PA_VOLUME_NORM)/100); |
544 |
else { |
545 |
volume.values[0] = ((pa_volume_t) vol->left*PA_VOLUME_NORM)/100; |
546 |
volume.values[1] = ((pa_volume_t) vol->right*PA_VOLUME_NORM)/100; |
547 |
} |
548 |
|
549 |
if (!(o = pa_context_set_sink_input_volume(context, pa_stream_get_index(stream), &volume, NULL, NULL))) { |
550 |
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] pa_context_set_sink_input_volume() failed: %s\n", pa_strerror(pa_context_errno(context))); |
551 |
return CONTROL_ERROR; |
552 |
} |
553 |
|
554 |
pa_operation_unref(o); |
555 |
|
556 |
/* We don't wait for completion here */ |
557 |
|
558 |
return CONTROL_OK; |
559 |
} |
560 |
|
561 |
default: |
562 |
/* Unknown CONTROL command */ |
563 |
return CONTROL_UNKNOWN; |
564 |
} |
565 |
} |