1 /*****************************************************************************
2 * pulse.c : Pulseaudio output plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2008 the VideoLAN team
6 * Authors: Martin Hamrle <hamrle @ post . cz>
8 * This program 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.
13 * This program 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.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
23 /*****************************************************************************
25 *****************************************************************************/
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
36 #include <pulse/pulseaudio.h>
37 #ifdef X_DISPLAY_MISSING
38 # error Xlib required due to PulseAudio bug 799!
44 /*****************************************************************************
45 * aout_sys_t: Pulseaudio output method descriptor
46 *****************************************************************************
47 * This structure is part of the audio output thread descriptor.
48 * It describes the specific properties of an audio device.
49 *****************************************************************************/
52 /** PulseAudio playback stream object */
53 struct pa_stream
*stream
;
55 /** PulseAudio connection context */
56 struct pa_context
*context
;
58 /** Main event loop object */
59 struct pa_threaded_mainloop
*mainloop
;
67 #define PULSE_DEBUG( ...) \
68 msg_Dbg( p_aout, __VA_ARGS__ )
70 #define PULSE_DEBUG( ...) \
75 #define CHECK_DEAD_GOTO(label) do { \
76 if (!p_sys->context || pa_context_get_state(p_sys->context) != PA_CONTEXT_READY || \
77 !p_sys->stream || pa_stream_get_state(p_sys->stream) != PA_STREAM_READY) { \
78 msg_Err(p_aout, "Connection died: %s", p_sys->context ? pa_strerror(pa_context_errno(p_sys->context)) : "NULL"); \
83 /*****************************************************************************
85 *****************************************************************************/
86 static int Open ( vlc_object_t
* );
87 static void Close ( vlc_object_t
* );
88 static void Play ( aout_instance_t
* );
90 static void context_state_cb(pa_context
*c
, void *userdata
);
91 static void stream_state_cb(pa_stream
*s
, void * userdata
);
92 static void stream_request_cb(pa_stream
*s
, size_t length
, void *userdata
);
93 static void stream_latency_update_cb(pa_stream
*s
, void *userdata
);
94 static void success_cb(pa_stream
*s
, int sucess
, void *userdata
);
95 static void uninit(aout_instance_t
*p_aout
);
97 /*****************************************************************************
99 *****************************************************************************/
101 set_shortname( "PulseAudio" )
102 set_description( N_("Pulseaudio audio output") )
103 set_capability( "audio output", 160 )
104 set_category( CAT_AUDIO
)
105 set_subcategory( SUBCAT_AUDIO_AOUT
)
106 add_shortcut( "pulseaudio", "pa" )
107 set_callbacks( Open
, Close
)
110 /*****************************************************************************
111 * Open: open the audio device
112 *****************************************************************************/
113 static int Open ( vlc_object_t
*p_this
)
115 aout_instance_t
*p_aout
= (aout_instance_t
*)p_this
;
116 struct aout_sys_t
* p_sys
;
117 struct pa_sample_spec ss
;
118 const struct pa_buffer_attr
*buffer_attr
;
119 struct pa_buffer_attr a
;
120 struct pa_channel_map map
;
121 char * p_client_name
;
123 if( !vlc_xlib_init( p_this
) )
126 /* Allocate structures */
127 p_aout
->output
.p_sys
= p_sys
= calloc( 1, sizeof( aout_sys_t
) );
131 PULSE_DEBUG( "Pulse start initialization");
133 ss
.channels
= aout_FormatNbChannels( &p_aout
->output
.output
); /* Get the input stream channel count */
135 /* Setup the pulse audio stream based on the input stream count */
139 p_aout
->output
.output
.i_physical_channels
140 = AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
| AOUT_CHAN_CENTER
141 | AOUT_CHAN_MIDDLELEFT
| AOUT_CHAN_MIDDLERIGHT
142 | AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT
146 p_aout
->output
.output
.i_physical_channels
147 = AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
| AOUT_CHAN_CENTER
148 | AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT
153 p_aout
->output
.output
.i_physical_channels
154 = AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
155 | AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT
;
159 p_aout
->output
.output
.i_physical_channels
160 = AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
;
164 p_aout
->output
.output
.i_physical_channels
= AOUT_CHAN_CENTER
;
168 msg_Err(p_aout
,"Invalid number of channels");
172 /* Add a quick command line info message */
173 msg_Dbg(p_aout
, "%d audio channels", ss
.channels
);
175 ss
.rate
= p_aout
->output
.output
.i_rate
;
178 ss
.format
= PA_SAMPLE_FLOAT32NE
;
179 p_aout
->output
.output
.i_format
= VLC_CODEC_FL32
;
183 ss
.format
= PA_SAMPLE_S16NE
;
184 p_aout
->output
.output
.i_format
= VLC_CODEC_S16N
;
187 if (!pa_sample_spec_valid(&ss
)) {
188 msg_Err(p_aout
,"Invalid sample spec");
192 /* Reduce overall latency to 200mS to reduce audible clicks
193 * Also pulse minreq and internal buffers are now 20mS which reduces resampling
195 a
.tlength
= pa_bytes_per_second(&ss
)/5;
196 a
.maxlength
= a
.tlength
* 2;
197 a
.prebuf
= a
.tlength
/ 2;
198 a
.minreq
= a
.tlength
/ 10;
200 /* Buffer size is 20mS */
201 p_sys
->buffer_size
= a
.minreq
;
203 /* Initialise the speaker map setup above */
204 pa_channel_map_init_auto(&map
, ss
.channels
, PA_CHANNEL_MAP_ALSA
);
206 if (!(p_sys
->mainloop
= pa_threaded_mainloop_new())) {
207 msg_Err(p_aout
, "Failed to allocate main loop");
211 if ((p_client_name
= var_InheritString(p_aout
, "user-agent")) == NULL
) {
212 msg_Err(p_aout
, "No user-agent string available.");
216 p_sys
->context
= pa_context_new(pa_threaded_mainloop_get_api(p_sys
->mainloop
), p_client_name
);
220 msg_Err(p_aout
, "Failed to allocate context");
224 pa_context_set_state_callback(p_sys
->context
, context_state_cb
, p_aout
);
226 PULSE_DEBUG( "Pulse before context connect");
228 if (pa_context_connect(p_sys
->context
, NULL
, 0, NULL
) < 0) {
229 msg_Err(p_aout
, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys
->context
)));
233 PULSE_DEBUG( "Pulse after context connect");
235 pa_threaded_mainloop_lock(p_sys
->mainloop
);
237 if (pa_threaded_mainloop_start(p_sys
->mainloop
) < 0) {
238 msg_Err(p_aout
, "Failed to start main loop");
239 goto unlock_and_fail
;
242 msg_Dbg(p_aout
, "Pulse mainloop started");
244 /* Wait until the context is ready */
245 pa_threaded_mainloop_wait(p_sys
->mainloop
);
247 if (pa_context_get_state(p_sys
->context
) != PA_CONTEXT_READY
) {
248 msg_Dbg(p_aout
, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys
->context
)));
249 goto unlock_and_fail
;
252 if (!(p_sys
->stream
= pa_stream_new(p_sys
->context
, "audio stream", &ss
, &map
))) {
253 msg_Err(p_aout
, "Failed to create stream: %s", pa_strerror(pa_context_errno(p_sys
->context
)));
254 goto unlock_and_fail
;
257 PULSE_DEBUG( "Pulse after new stream");
259 pa_stream_set_state_callback(p_sys
->stream
, stream_state_cb
, p_aout
);
260 pa_stream_set_write_callback(p_sys
->stream
, stream_request_cb
, p_aout
);
261 pa_stream_set_latency_update_callback(p_sys
->stream
, stream_latency_update_cb
, p_aout
);
263 if (pa_stream_connect_playback(p_sys
->stream
, NULL
, &a
, PA_STREAM_INTERPOLATE_TIMING
|PA_STREAM_AUTO_TIMING_UPDATE
|PA_STREAM_ADJUST_LATENCY
, NULL
, NULL
) < 0) {
264 msg_Err(p_aout
, "Failed to connect stream: %s", pa_strerror(pa_context_errno(p_sys
->context
)));
265 goto unlock_and_fail
;
268 PULSE_DEBUG("Pulse stream connect");
270 /* Wait until the stream is ready */
271 pa_threaded_mainloop_wait(p_sys
->mainloop
);
273 msg_Dbg(p_aout
,"Pulse stream connected");
275 if (pa_stream_get_state(p_sys
->stream
) != PA_STREAM_READY
) {
276 msg_Err(p_aout
, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys
->context
)));
277 goto unlock_and_fail
;
281 PULSE_DEBUG("Pulse after stream get status");
283 pa_threaded_mainloop_unlock(p_sys
->mainloop
);
285 buffer_attr
= pa_stream_get_buffer_attr(p_sys
->stream
);
286 p_aout
->output
.i_nb_samples
= buffer_attr
->minreq
/ pa_frame_size(&ss
);
287 p_aout
->output
.pf_play
= Play
;
288 aout_VolumeSoftInit(p_aout
);
289 msg_Dbg(p_aout
, "Pulse initialized successfully");
291 char cmt
[PA_CHANNEL_MAP_SNPRINT_MAX
], sst
[PA_SAMPLE_SPEC_SNPRINT_MAX
];
293 msg_Dbg(p_aout
, "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u", buffer_attr
->maxlength
, buffer_attr
->tlength
, buffer_attr
->prebuf
, buffer_attr
->minreq
);
294 msg_Dbg(p_aout
, "Using sample spec '%s', channel map '%s'.",
295 pa_sample_spec_snprint(sst
, sizeof(sst
), pa_stream_get_sample_spec(p_sys
->stream
)),
296 pa_channel_map_snprint(cmt
, sizeof(cmt
), pa_stream_get_channel_map(p_sys
->stream
)));
298 msg_Dbg(p_aout
, "Connected to device %s (%u, %ssuspended).",
299 pa_stream_get_device_name(p_sys
->stream
),
300 pa_stream_get_device_index(p_sys
->stream
),
301 pa_stream_is_suspended(p_sys
->stream
) ? "" : "not ");
307 msg_Dbg(p_aout
, "Pulse initialization unlock and fail");
310 pa_threaded_mainloop_unlock(p_sys
->mainloop
);
312 msg_Dbg(p_aout
, "Pulse initialization failed");
317 /*****************************************************************************
318 * Play: play a sound samples buffer
319 *****************************************************************************/
320 static void Play( aout_instance_t
* p_aout
)
322 struct aout_sys_t
* p_sys
= (struct aout_sys_t
*) p_aout
->output
.p_sys
;
325 msg_Dbg(p_aout
, "Pulse stream started");
326 pa_threaded_mainloop_lock(p_sys
->mainloop
);
328 aout_FifoFirstDate( p_aout
, &p_aout
->output
.fifo
);
331 pa_threaded_mainloop_signal(p_sys
->mainloop
, 0);
332 pa_threaded_mainloop_unlock(p_sys
->mainloop
);
336 /*****************************************************************************
337 * Close: close the audio device
338 *****************************************************************************/
339 static void Close ( vlc_object_t
*p_this
)
341 aout_instance_t
*p_aout
= (aout_instance_t
*)p_this
;
342 struct aout_sys_t
* p_sys
= p_aout
->output
.p_sys
;
344 msg_Dbg(p_aout
, "Pulse Close");
347 pa_threaded_mainloop_lock(p_sys
->mainloop
);
348 pa_stream_set_write_callback(p_sys
->stream
, NULL
, NULL
);
352 o
= pa_stream_flush(p_sys
->stream
, success_cb
, p_aout
);
353 while( pa_operation_get_state(o
) == PA_OPERATION_RUNNING
)
354 pa_threaded_mainloop_wait(p_sys
->mainloop
);
355 pa_operation_unref(o
);
357 o
= pa_stream_drain(p_sys
->stream
, success_cb
, p_aout
);
358 while( pa_operation_get_state(o
) == PA_OPERATION_RUNNING
)
359 pa_threaded_mainloop_wait(p_sys
->mainloop
);
360 pa_operation_unref(o
);
362 pa_threaded_mainloop_unlock(p_sys
->mainloop
);
367 static void uninit(aout_instance_t
*p_aout
){
368 struct aout_sys_t
* p_sys
= p_aout
->output
.p_sys
;
371 pa_threaded_mainloop_stop(p_sys
->mainloop
);
374 pa_stream_disconnect(p_sys
->stream
);
375 pa_stream_unref(p_sys
->stream
);
376 p_sys
->stream
= NULL
;
379 if (p_sys
->context
) {
380 pa_context_disconnect(p_sys
->context
);
381 pa_context_unref(p_sys
->context
);
382 p_sys
->context
= NULL
;
385 if (p_sys
->mainloop
) {
386 pa_threaded_mainloop_free(p_sys
->mainloop
);
387 p_sys
->mainloop
= NULL
;
391 p_aout
->output
.p_sys
= NULL
;
394 static void context_state_cb(pa_context
*c
, void *userdata
) {
395 aout_instance_t
*p_aout
= (aout_instance_t
*)userdata
;
396 struct aout_sys_t
* p_sys
= (struct aout_sys_t
*) p_aout
->output
.p_sys
;
400 PULSE_DEBUG( "Pulse context state changed");
402 switch (pa_context_get_state(c
)) {
403 case PA_CONTEXT_READY
:
404 case PA_CONTEXT_TERMINATED
:
405 case PA_CONTEXT_FAILED
:
406 PULSE_DEBUG( "Pulse context state changed signal");
407 pa_threaded_mainloop_signal(p_sys
->mainloop
, 0);
410 case PA_CONTEXT_UNCONNECTED
:
411 case PA_CONTEXT_CONNECTING
:
412 case PA_CONTEXT_AUTHORIZING
:
413 case PA_CONTEXT_SETTING_NAME
:
414 PULSE_DEBUG( "Pulse context state changed no signal");
419 static void stream_state_cb(pa_stream
*s
, void * userdata
) {
420 aout_instance_t
*p_aout
= (aout_instance_t
*)userdata
;
421 struct aout_sys_t
* p_sys
= (struct aout_sys_t
*) p_aout
->output
.p_sys
;
425 PULSE_DEBUG( "Pulse stream state changed");
427 switch (pa_stream_get_state(s
)) {
429 case PA_STREAM_READY
:
430 case PA_STREAM_FAILED
:
431 case PA_STREAM_TERMINATED
:
432 pa_threaded_mainloop_signal(p_sys
->mainloop
, 0);
435 case PA_STREAM_UNCONNECTED
:
436 case PA_STREAM_CREATING
:
441 static void stream_request_cb(pa_stream
*s
, size_t length
, void *userdata
) {
443 aout_instance_t
*p_aout
= (aout_instance_t
*)userdata
;
444 struct aout_sys_t
* p_sys
= (struct aout_sys_t
*) p_aout
->output
.p_sys
;
450 size_t buffer_size
= p_sys
->buffer_size
;
452 PULSE_DEBUG( "Pulse stream request %d", length
);
455 aout_buffer_t
* p_buffer
= NULL
;
459 if(pa_stream_get_latency(p_sys
->stream
, &latency
, &negative
)<0){
460 if (pa_context_errno(p_sys
->context
) != PA_ERR_NODATA
) {
461 msg_Err(p_aout
, "pa_stream_get_latency() failed: %s", pa_strerror(pa_context_errno(p_sys
->context
)));
467 PULSE_DEBUG( "Pulse stream request latency=%"PRId64
"", latency
);
468 next_date
= mdate() + latency
;
470 if(p_sys
->start_date
< next_date
+ AOUT_PTS_TOLERANCE
){
471 p_buffer
= aout_OutputNextBuffer( p_aout
, next_date
, 0);
475 if ( p_buffer
!= NULL
)
477 PULSE_DEBUG( "Pulse stream request write buffer %d", p_buffer
->i_buffer
);
478 pa_stream_write(p_sys
->stream
, p_buffer
->p_buffer
, p_buffer
->i_buffer
, NULL
, 0, PA_SEEK_RELATIVE
);
479 length
-= p_buffer
->i_buffer
;
480 aout_BufferFree( p_buffer
);
484 PULSE_DEBUG( "Pulse stream request write zeroes");
485 void *data
= pa_xmalloc(buffer_size
);
486 bzero(data
, buffer_size
);
487 pa_stream_write(p_sys
->stream
, data
, buffer_size
, pa_xfree
, 0, PA_SEEK_RELATIVE
);
488 length
-= buffer_size
;
490 }while(length
> buffer_size
);
492 pa_threaded_mainloop_signal(p_sys
->mainloop
, 0);
495 static void stream_latency_update_cb(pa_stream
*s
, void *userdata
) {
497 aout_instance_t
*p_aout
= (aout_instance_t
*)userdata
;
498 struct aout_sys_t
* p_sys
= (struct aout_sys_t
*) p_aout
->output
.p_sys
;
502 PULSE_DEBUG( "Pulse stream latency update");
504 pa_threaded_mainloop_signal(p_sys
->mainloop
, 0);
507 static void success_cb(pa_stream
*s
, int sucess
, void *userdata
)
510 aout_instance_t
*p_aout
= (aout_instance_t
*)userdata
;
511 struct aout_sys_t
* p_sys
= (struct aout_sys_t
*) p_aout
->output
.p_sys
;
517 pa_threaded_mainloop_signal(p_sys
->mainloop
, 0);