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 *****************************************************************************/
31 #include <vlc_plugin.h>
35 #include <pulse/pulseaudio.h>
37 /*****************************************************************************
38 * aout_sys_t: Pulseaudio output method descriptor
39 *****************************************************************************
40 * This structure is part of the audio output thread descriptor.
41 * It describes the specific properties of an audio device.
42 *****************************************************************************/
45 /** PulseAudio playback stream object */
46 struct pa_stream *stream;
48 /** PulseAudio connection context */
49 struct pa_context *context;
51 /** Main event loop object */
52 struct pa_threaded_mainloop *mainloop;
59 #define PULSE_CLIENT_NAME N_("VLC media player")
62 #define PULSE_DEBUG( ...) \
63 msg_Dbg( p_aout, __VA_ARGS__ )
65 #define PULSE_DEBUG( ...) \
70 #define CHECK_DEAD_GOTO(label) do { \
71 if (!p_sys->context || pa_context_get_state(p_sys->context) != PA_CONTEXT_READY || \
72 !p_sys->stream || pa_stream_get_state(p_sys->stream) != PA_STREAM_READY) { \
73 msg_Err(p_aout, "Connection died: %s", p_sys->context ? pa_strerror(pa_context_errno(p_sys->context)) : "NULL"); \
77 /*****************************************************************************
79 *****************************************************************************/
80 static int Open ( vlc_object_t * );
81 static void Close ( vlc_object_t * );
82 static void Play ( aout_instance_t * );
84 static void context_state_cb(pa_context *c, void *userdata);
85 static void stream_state_cb(pa_stream *s, void * userdata);
86 static void stream_request_cb(pa_stream *s, size_t length, void *userdata);
87 static void stream_latency_update_cb(pa_stream *s, void *userdata);
88 static void success_cb(pa_stream *s, int sucess, void *userdata);
89 static void uninit(aout_instance_t *p_aout);
90 /*****************************************************************************
92 *****************************************************************************/
94 set_shortname( "Pulse Audio" );
95 set_description( N_("Pulseaudio audio output") );
96 set_capability( "audio output", 40 );
97 set_category( CAT_AUDIO );
98 set_subcategory( SUBCAT_AUDIO_AOUT );
99 add_shortcut( "pulseaudio" );
100 add_shortcut( "pa" );
101 set_callbacks( Open, Close );
104 /*****************************************************************************
105 * Open: open the audio device
106 *****************************************************************************/
107 static int Open ( vlc_object_t *p_this )
109 aout_instance_t *p_aout = (aout_instance_t *)p_this;
110 struct aout_sys_t * p_sys;
111 struct pa_sample_spec ss;
112 const struct pa_buffer_attr *buffer_attr;
113 struct pa_buffer_attr a;
114 struct pa_channel_map map;
116 /* Allocate structures */
117 p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) );
120 memset( p_sys, 0, sizeof( aout_sys_t ) );
122 PULSE_DEBUG( "Pulse start initialization");
124 ss.rate = p_aout->output.output.i_rate;
127 ss.format = PA_SAMPLE_S16LE;
128 p_aout->output.output.i_physical_channels =
129 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
130 p_aout->output.output.i_format = AOUT_FMT_S16_NE;
132 if (!pa_sample_spec_valid(&ss)) {
133 msg_Err(p_aout,"Invalid sample spec");
137 a.maxlength = pa_bytes_per_second(&ss)/4/pa_frame_size(&ss);
138 a.tlength = a.maxlength*9/10;
139 a.prebuf = a.tlength/2;
140 a.minreq = a.tlength/10;
142 a.maxlength *= pa_frame_size(&ss);
143 a.tlength *= pa_frame_size(&ss);
144 a.prebuf *= pa_frame_size(&ss);
145 a.minreq *= pa_frame_size(&ss);
147 p_sys->buffer_size = a.minreq;
149 pa_channel_map_init_stereo(&map);
152 if (!(p_sys->mainloop = pa_threaded_mainloop_new())) {
153 msg_Err(p_aout, "Failed to allocate main loop");
157 if (!(p_sys->context = pa_context_new(pa_threaded_mainloop_get_api(p_sys->mainloop), _( PULSE_CLIENT_NAME )))) {
158 msg_Err(p_aout, "Failed to allocate context");
162 pa_context_set_state_callback(p_sys->context, context_state_cb, p_aout);
164 PULSE_DEBUG( "Pulse before context connect");
166 if (pa_context_connect(p_sys->context, NULL, 0, NULL) < 0) {
167 msg_Err(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context)));
171 PULSE_DEBUG( "Pulse after context connect");
173 pa_threaded_mainloop_lock(p_sys->mainloop);
175 if (pa_threaded_mainloop_start(p_sys->mainloop) < 0) {
176 msg_Err(p_aout, "Failed to start main loop");
177 goto unlock_and_fail;
180 msg_Dbg(p_aout, "Pulse mainloop started");
182 /* Wait until the context is ready */
183 pa_threaded_mainloop_wait(p_sys->mainloop);
185 if (pa_context_get_state(p_sys->context) != PA_CONTEXT_READY) {
186 msg_Err(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context)));
187 goto unlock_and_fail;
190 if (!(p_sys->stream = pa_stream_new(p_sys->context, "audio stream", &ss, &map))) {
191 msg_Err(p_aout, "Failed to create stream: %s", pa_strerror(pa_context_errno(p_sys->context)));
192 goto unlock_and_fail;
195 PULSE_DEBUG( "Pulse after new stream");
197 pa_stream_set_state_callback(p_sys->stream, stream_state_cb, p_aout);
198 pa_stream_set_write_callback(p_sys->stream, stream_request_cb, p_aout);
199 pa_stream_set_latency_update_callback(p_sys->stream, stream_latency_update_cb, p_aout);
201 if (pa_stream_connect_playback(p_sys->stream, NULL, &a, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL) < 0) {
202 msg_Err(p_aout, "Failed to connect stream: %s", pa_strerror(pa_context_errno(p_sys->context)));
203 goto unlock_and_fail;
206 PULSE_DEBUG("Pulse stream connect");
208 /* Wait until the stream is ready */
209 pa_threaded_mainloop_wait(p_sys->mainloop);
211 msg_Dbg(p_aout,"Pulse stream connected");
213 if (pa_stream_get_state(p_sys->stream) != PA_STREAM_READY) {
214 msg_Err(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context)));
215 goto unlock_and_fail;
219 PULSE_DEBUG("Pulse after stream get status");
221 pa_threaded_mainloop_unlock(p_sys->mainloop);
223 buffer_attr = pa_stream_get_buffer_attr(p_sys->stream);
224 p_aout->output.i_nb_samples = buffer_attr->minreq / pa_frame_size(&ss);
225 p_aout->output.pf_play = Play;
226 aout_VolumeSoftInit(p_aout);
227 msg_Dbg(p_aout, "Pulse initialized successfully");
229 char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
231 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);
232 msg_Dbg(p_aout, "Using sample spec '%s', channel map '%s'.",
233 pa_sample_spec_snprint(sst, sizeof(sst), pa_stream_get_sample_spec(p_sys->stream)),
234 pa_channel_map_snprint(cmt, sizeof(cmt), pa_stream_get_channel_map(p_sys->stream)));
236 msg_Dbg(p_aout, "Connected to device %s (%u, %ssuspended).",
237 pa_stream_get_device_name(p_sys->stream),
238 pa_stream_get_device_index(p_sys->stream),
239 pa_stream_is_suspended(p_sys->stream) ? "" : "not ");
245 msg_Dbg(p_aout, "Pulse initialization unlock and fail");
248 pa_threaded_mainloop_unlock(p_sys->mainloop);
250 msg_Err(p_aout, "Pulse initialization failed");
255 /*****************************************************************************
256 * Play: play a sound samples buffer
257 *****************************************************************************/
258 static void Play( aout_instance_t * p_aout )
260 struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
265 msg_Dbg(p_aout, "Pulse stream started");
267 aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
270 pa_threaded_mainloop_lock(p_sys->mainloop);
271 if((o = pa_stream_flush(p_sys->stream, success_cb, p_aout))){
272 pa_operation_unref(o);
274 pa_threaded_mainloop_unlock(p_sys->mainloop);
276 pa_threaded_mainloop_signal(p_sys->mainloop, 0);
280 /*****************************************************************************
281 * Close: close the audio device
282 *****************************************************************************/
283 static void Close ( vlc_object_t *p_this )
285 aout_instance_t *p_aout = (aout_instance_t *)p_this;
286 struct aout_sys_t * p_sys = p_aout->output.p_sys;
288 msg_Dbg(p_aout, "Pulse Close");
292 pa_threaded_mainloop_lock(p_sys->mainloop);
293 pa_stream_set_write_callback(p_sys->stream, NULL, NULL);
295 if((o = pa_stream_drain(p_sys->stream, success_cb, p_aout))){
296 while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
297 CHECK_DEAD_GOTO(fail);
298 pa_threaded_mainloop_wait(p_sys->mainloop);
303 pa_operation_unref(o);
306 pa_threaded_mainloop_unlock(p_sys->mainloop);
311 static void uninit(aout_instance_t *p_aout){
312 struct aout_sys_t * p_sys = p_aout->output.p_sys;
315 pa_threaded_mainloop_stop(p_sys->mainloop);
318 pa_stream_disconnect(p_sys->stream);
319 pa_stream_unref(p_sys->stream);
320 p_sys->stream = NULL;
323 if (p_sys->context) {
324 pa_context_disconnect(p_sys->context);
325 pa_context_unref(p_sys->context);
326 p_sys->context = NULL;
329 if (p_sys->mainloop) {
330 pa_threaded_mainloop_free(p_sys->mainloop);
331 p_sys->mainloop = NULL;
335 p_aout->output.p_sys = NULL;
338 static void context_state_cb(pa_context *c, void *userdata) {
339 aout_instance_t *p_aout = (aout_instance_t *)userdata;
340 struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
344 PULSE_DEBUG( "Pulse context state changed");
346 switch (pa_context_get_state(c)) {
347 case PA_CONTEXT_READY:
348 case PA_CONTEXT_TERMINATED:
349 case PA_CONTEXT_FAILED:
350 PULSE_DEBUG( "Pulse context state changed signal");
351 pa_threaded_mainloop_signal(p_sys->mainloop, 0);
354 case PA_CONTEXT_UNCONNECTED:
355 case PA_CONTEXT_CONNECTING:
356 case PA_CONTEXT_AUTHORIZING:
357 case PA_CONTEXT_SETTING_NAME:
358 PULSE_DEBUG( "Pulse context state changed no signal");
363 static void stream_state_cb(pa_stream *s, void * userdata) {
364 aout_instance_t *p_aout = (aout_instance_t *)userdata;
365 struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
369 PULSE_DEBUG( "Pulse stream state changed");
371 switch (pa_stream_get_state(s)) {
373 case PA_STREAM_READY:
374 case PA_STREAM_FAILED:
375 case PA_STREAM_TERMINATED:
376 pa_threaded_mainloop_signal(p_sys->mainloop, 0);
379 case PA_STREAM_UNCONNECTED:
380 case PA_STREAM_CREATING:
385 static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
386 aout_instance_t *p_aout = (aout_instance_t *)userdata;
387 struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
393 size_t buffer_size = p_sys->buffer_size;
395 PULSE_DEBUG( "Pulse stream request %d", length);
398 aout_buffer_t * p_buffer = NULL;
402 if(pa_stream_get_latency(p_sys->stream, &latency, &negative)<0){
403 if (pa_context_errno(p_sys->context) != PA_ERR_NODATA) {
404 msg_Err(p_aout, "pa_stream_get_latency() failed: %s", pa_strerror(pa_context_errno(p_sys->context)));
409 PULSE_DEBUG( "Pulse stream request latency=%"PRId64"", latency);
410 next_date = mdate() + latency;
413 if(p_sys->start_date < next_date + AOUT_PTS_TOLERANCE ){
415 vlc_mutex_lock( &p_aout->output_fifo_lock );
416 p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo );
417 vlc_mutex_unlock( &p_aout->output_fifo_lock );
419 p_buffer = aout_OutputNextBuffer( p_aout, next_date, 0);
423 if ( p_buffer != NULL )
425 PULSE_DEBUG( "Pulse stream request write buffer %d", p_buffer->i_nb_bytes);
426 pa_stream_write(p_sys->stream, p_buffer->p_buffer, p_buffer->i_nb_bytes, NULL, 0, PA_SEEK_RELATIVE);
427 length -= p_buffer->i_nb_bytes;
428 aout_BufferFree( p_buffer );
432 PULSE_DEBUG( "Pulse stream request write zeroes");
433 void *data = pa_xmalloc(buffer_size);
434 bzero(data, buffer_size);
435 pa_stream_write(p_sys->stream, data, buffer_size, pa_xfree, 0, PA_SEEK_RELATIVE);
436 length -= buffer_size;
438 }while(length > buffer_size);
440 pa_threaded_mainloop_signal(p_sys->mainloop, 0);
443 static void stream_latency_update_cb(pa_stream *s, void *userdata) {
444 aout_instance_t *p_aout = (aout_instance_t *)userdata;
445 struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
449 PULSE_DEBUG( "Pulse stream latency update");
451 pa_threaded_mainloop_signal(p_sys->mainloop, 0);
454 static void success_cb(pa_stream *s, int sucess, void *userdata)
456 aout_instance_t *p_aout = (aout_instance_t *)userdata;
457 struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
463 pa_threaded_mainloop_signal(p_sys->mainloop, 0);