]> git.sesse.net Git - vlc/blob - modules/audio_output/pulse.c
pulseaudio: disable pa_stream_drain on close
[vlc] / modules / audio_output / pulse.c
1 /*****************************************************************************
2  * pulse.c : Pulseaudio output plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2008 the VideoLAN team
5  *
6  * Authors: Martin Hamrle <hamrle @ post . cz>
7  *
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.
12  *
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.
17  *
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  *****************************************************************************/
22
23 /*****************************************************************************
24  * Preamble
25  *****************************************************************************/
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
32
33 #include <vlc_aout.h>
34 #include <vlc_cpu.h>
35
36 #include <pulse/pulseaudio.h>
37
38 #include <assert.h>
39
40 /*****************************************************************************
41  * aout_sys_t: Pulseaudio output method descriptor
42  *****************************************************************************
43  * This structure is part of the audio output thread descriptor.
44  * It describes the specific properties of an audio device.
45  *****************************************************************************/
46 struct aout_sys_t
47 {
48     /** PulseAudio playback stream object */
49     struct pa_stream *stream;
50
51     /** PulseAudio connection context */
52     struct pa_context *context;
53
54     /** Main event loop object */
55     struct pa_threaded_mainloop *mainloop;
56
57     int started;
58     size_t buffer_size;
59     mtime_t start_date;
60 };
61
62 #define    PULSE_CLIENT_NAME N_("VLC media player")
63
64 #if 0
65 #define PULSE_DEBUG( ...) \
66     msg_Dbg( p_aout, __VA_ARGS__ )
67 #else
68 #define PULSE_DEBUG( ...) \
69     (void) 0
70 #endif
71
72
73 #define CHECK_DEAD_GOTO(label) do { \
74 if (!p_sys->context || pa_context_get_state(p_sys->context) != PA_CONTEXT_READY || \
75     !p_sys->stream || pa_stream_get_state(p_sys->stream) != PA_STREAM_READY) { \
76         msg_Err(p_aout, "Connection died: %s", p_sys->context ? pa_strerror(pa_context_errno(p_sys->context)) : "NULL"); \
77         goto label; \
78     }  \
79 } while(0);
80
81 /*****************************************************************************
82  * Local prototypes
83  *****************************************************************************/
84 static int  Open        ( vlc_object_t * );
85 static void Close       ( vlc_object_t * );
86 static void Play        ( aout_instance_t * );
87
88 static void context_state_cb(pa_context *c, void *userdata);
89 static void stream_state_cb(pa_stream *s, void * userdata);
90 static void stream_request_cb(pa_stream *s, size_t length, void *userdata);
91 static void stream_latency_update_cb(pa_stream *s, void *userdata);
92 static void success_cb(pa_stream *s, int sucess, void *userdata);
93 static void uninit(aout_instance_t *p_aout);
94
95 /*****************************************************************************
96  * Module descriptor
97  *****************************************************************************/
98 vlc_module_begin ()
99     set_shortname( "PulseAudio" )
100     set_description( N_("Pulseaudio audio output") )
101     set_capability( "audio output", 160 )
102     set_category( CAT_AUDIO )
103     set_subcategory( SUBCAT_AUDIO_AOUT )
104     add_shortcut( "pulseaudio" )
105     add_shortcut( "pa" )
106     set_callbacks( Open, Close )
107     linked_with_a_crap_library_which_uses_atexit()
108 vlc_module_end ()
109
110 /*****************************************************************************
111  * Open: open the audio device
112  *****************************************************************************/
113 static int Open ( vlc_object_t *p_this )
114 {
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
122     /* Allocate structures */
123     p_aout->output.p_sys = p_sys = calloc( 1, sizeof( aout_sys_t ) );
124     if( p_sys == NULL )
125         return VLC_ENOMEM;
126
127     PULSE_DEBUG( "Pulse start initialization");
128
129     ss.channels = aout_FormatNbChannels( &p_aout->output.output ); /* Get the input stream channel count */
130
131     /* Setup the pulse audio stream based on the input stream count */
132     switch(ss.channels)
133     {
134         case 8:
135             p_aout->output.output.i_physical_channels
136                 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
137                 | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT
138                 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
139                 | AOUT_CHAN_LFE;
140             break;
141         case 6:
142             p_aout->output.output.i_physical_channels
143                 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
144                 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
145                 | AOUT_CHAN_LFE;
146             break;
147
148         case 4:
149             p_aout->output.output.i_physical_channels
150                 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
151                 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
152             break;
153
154         case 2:
155             p_aout->output.output.i_physical_channels
156                 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
157             break;
158
159         case 1:
160             p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
161             break;
162
163         default:
164             msg_Err(p_aout,"Invalid number of channels");
165         goto fail;
166     }
167
168     /* Add a quick command line info message */
169     msg_Dbg(p_aout, "%d audio channels", ss.channels);
170
171     ss.rate = p_aout->output.output.i_rate;
172     if (HAVE_FPU)
173     {
174         ss.format = PA_SAMPLE_FLOAT32NE;
175         p_aout->output.output.i_format = VLC_CODEC_FL32;
176     }
177     else
178     {
179         ss.format = PA_SAMPLE_S16NE;
180         p_aout->output.output.i_format = VLC_CODEC_S16N;
181     }
182
183     if (!pa_sample_spec_valid(&ss)) {
184         msg_Err(p_aout,"Invalid sample spec");
185         goto fail;
186     }
187
188     /* Reduce overall latency to 200mS to reduce audible clicks
189      * Also pulse minreq and internal buffers are now 20mS which reduces resampling
190      */
191     a.tlength = pa_bytes_per_second(&ss)/5;
192     a.maxlength = a.tlength * 2;
193     a.prebuf = a.tlength / 2;
194     a.minreq = a.tlength / 10;
195
196     /* Buffer size is 20mS */
197     p_sys->buffer_size = a.minreq;
198
199     /* Initialise the speaker map setup above */
200     pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA);
201
202     if (!(p_sys->mainloop = pa_threaded_mainloop_new())) {
203         msg_Err(p_aout, "Failed to allocate main loop");
204         goto fail;
205     }
206
207     if (!(p_sys->context = pa_context_new(pa_threaded_mainloop_get_api(p_sys->mainloop), _( PULSE_CLIENT_NAME )))) {
208         msg_Err(p_aout, "Failed to allocate context");
209         goto fail;
210     }
211
212     pa_context_set_state_callback(p_sys->context, context_state_cb, p_aout);
213
214     PULSE_DEBUG( "Pulse before context connect");
215
216     if (pa_context_connect(p_sys->context, NULL, 0, NULL) < 0) {
217         msg_Err(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context)));
218         goto fail;
219     }
220
221     PULSE_DEBUG( "Pulse after context connect");
222
223     pa_threaded_mainloop_lock(p_sys->mainloop);
224
225     if (pa_threaded_mainloop_start(p_sys->mainloop) < 0) {
226         msg_Err(p_aout, "Failed to start main loop");
227         goto unlock_and_fail;
228     }
229
230     msg_Dbg(p_aout, "Pulse mainloop started");
231
232     /* Wait until the context is ready */
233     pa_threaded_mainloop_wait(p_sys->mainloop);
234
235     if (pa_context_get_state(p_sys->context) != PA_CONTEXT_READY) {
236         msg_Dbg(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context)));
237         goto unlock_and_fail;
238     }
239
240     if (!(p_sys->stream = pa_stream_new(p_sys->context, "audio stream", &ss, &map))) {
241         msg_Err(p_aout, "Failed to create stream: %s", pa_strerror(pa_context_errno(p_sys->context)));
242         goto unlock_and_fail;
243     }
244
245     PULSE_DEBUG( "Pulse after new stream");
246
247     pa_stream_set_state_callback(p_sys->stream, stream_state_cb, p_aout);
248     pa_stream_set_write_callback(p_sys->stream, stream_request_cb, p_aout);
249     pa_stream_set_latency_update_callback(p_sys->stream, stream_latency_update_cb, p_aout);
250
251     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) {
252         msg_Err(p_aout, "Failed to connect stream: %s", pa_strerror(pa_context_errno(p_sys->context)));
253         goto unlock_and_fail;
254     }
255
256      PULSE_DEBUG("Pulse stream connect");
257
258     /* Wait until the stream is ready */
259     pa_threaded_mainloop_wait(p_sys->mainloop);
260
261     msg_Dbg(p_aout,"Pulse stream connected");
262
263     if (pa_stream_get_state(p_sys->stream) != PA_STREAM_READY) {
264         msg_Err(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context)));
265         goto unlock_and_fail;
266     }
267
268
269     PULSE_DEBUG("Pulse after stream get status");
270
271     pa_threaded_mainloop_unlock(p_sys->mainloop);
272
273     buffer_attr = pa_stream_get_buffer_attr(p_sys->stream);
274     p_aout->output.i_nb_samples = buffer_attr->minreq / pa_frame_size(&ss);
275     p_aout->output.pf_play = Play;
276     aout_VolumeSoftInit(p_aout);
277     msg_Dbg(p_aout, "Pulse initialized successfully");
278     {
279         char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
280
281         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);
282         msg_Dbg(p_aout, "Using sample spec '%s', channel map '%s'.",
283                 pa_sample_spec_snprint(sst, sizeof(sst), pa_stream_get_sample_spec(p_sys->stream)),
284                 pa_channel_map_snprint(cmt, sizeof(cmt), pa_stream_get_channel_map(p_sys->stream)));
285
286             msg_Dbg(p_aout, "Connected to device %s (%u, %ssuspended).",
287                         pa_stream_get_device_name(p_sys->stream),
288                         pa_stream_get_device_index(p_sys->stream),
289                         pa_stream_is_suspended(p_sys->stream) ? "" : "not ");
290     }
291
292     return VLC_SUCCESS;
293
294 unlock_and_fail:
295     msg_Dbg(p_aout, "Pulse initialization unlock and fail");
296
297     if (p_sys->mainloop)
298         pa_threaded_mainloop_unlock(p_sys->mainloop);
299 fail:
300     msg_Dbg(p_aout, "Pulse initialization failed");
301     uninit(p_aout);
302     return VLC_EGENERIC;
303 }
304
305 /*****************************************************************************
306  * Play: play a sound samples buffer
307  *****************************************************************************/
308 static void Play( aout_instance_t * p_aout )
309 {
310     struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
311
312     pa_operation *o;
313
314     if(!p_sys->started){
315         msg_Dbg(p_aout, "Pulse stream started");
316         p_sys->start_date =
317             aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
318         p_sys->started = 1;
319
320         pa_threaded_mainloop_lock(p_sys->mainloop);
321         if((o = pa_stream_flush(p_sys->stream, success_cb, p_aout))){
322             pa_operation_unref(o);
323         }
324         pa_threaded_mainloop_unlock(p_sys->mainloop);
325
326         pa_threaded_mainloop_signal(p_sys->mainloop, 0);
327     }
328 }
329
330 /*****************************************************************************
331  * Close: close the audio device
332  *****************************************************************************/
333 static void Close ( vlc_object_t *p_this )
334 {
335     aout_instance_t *p_aout = (aout_instance_t *)p_this;
336     struct aout_sys_t * p_sys = p_aout->output.p_sys;
337
338     msg_Dbg(p_aout, "Pulse Close");
339
340     if(p_sys->stream){
341         pa_operation *o;
342         pa_threaded_mainloop_lock(p_sys->mainloop);
343         pa_stream_set_write_callback(p_sys->stream, NULL, NULL);
344
345 /* I didn't find any explanation why we need to do pa_stream_drain on close
346  * as we don't really care if we lose 20ms buffer in this point anyway?
347  * And disabling this speeds up closing pulseaudio quite a lot (atleast for me).
348  */
349 #if 0
350         if((o = pa_stream_drain(p_sys->stream, success_cb, p_aout))){
351             while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
352                 CHECK_DEAD_GOTO(fail);
353                 pa_threaded_mainloop_wait(p_sys->mainloop);
354             }
355
356         fail:
357
358             pa_operation_unref(o);
359         }
360 #endif
361         pa_threaded_mainloop_unlock(p_sys->mainloop);
362     }
363     uninit(p_aout);
364 }
365
366 static void uninit(aout_instance_t *p_aout){
367     struct aout_sys_t * p_sys = p_aout->output.p_sys;
368
369     if (p_sys->mainloop)
370         pa_threaded_mainloop_stop(p_sys->mainloop);
371
372     if (p_sys->stream) {
373         pa_stream_disconnect(p_sys->stream);
374         pa_stream_unref(p_sys->stream);
375         p_sys->stream = NULL;
376     }
377
378     if (p_sys->context) {
379         pa_context_disconnect(p_sys->context);
380         pa_context_unref(p_sys->context);
381         p_sys->context = NULL;
382     }
383
384     if (p_sys->mainloop) {
385         pa_threaded_mainloop_free(p_sys->mainloop);
386         p_sys->mainloop = NULL;
387     }
388
389     free(p_sys);
390     p_aout->output.p_sys = NULL;
391 }
392
393 static void context_state_cb(pa_context *c, void *userdata) {
394     aout_instance_t *p_aout = (aout_instance_t *)userdata;
395     struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
396
397     assert(c);
398
399     PULSE_DEBUG( "Pulse context state changed");
400
401     switch (pa_context_get_state(c)) {
402         case PA_CONTEXT_READY:
403         case PA_CONTEXT_TERMINATED:
404         case PA_CONTEXT_FAILED:
405         PULSE_DEBUG( "Pulse context state changed signal");
406             pa_threaded_mainloop_signal(p_sys->mainloop, 0);
407             break;
408
409         case PA_CONTEXT_UNCONNECTED:
410         case PA_CONTEXT_CONNECTING:
411         case PA_CONTEXT_AUTHORIZING:
412         case PA_CONTEXT_SETTING_NAME:
413         PULSE_DEBUG( "Pulse context state changed no signal");
414             break;
415     }
416 }
417
418 static void stream_state_cb(pa_stream *s, void * userdata) {
419     aout_instance_t *p_aout = (aout_instance_t *)userdata;
420     struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
421
422     assert(s);
423
424     PULSE_DEBUG( "Pulse stream state changed");
425
426     switch (pa_stream_get_state(s)) {
427
428         case PA_STREAM_READY:
429         case PA_STREAM_FAILED:
430         case PA_STREAM_TERMINATED:
431             pa_threaded_mainloop_signal(p_sys->mainloop, 0);
432             break;
433
434         case PA_STREAM_UNCONNECTED:
435         case PA_STREAM_CREATING:
436             break;
437     }
438 }
439
440 static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
441     VLC_UNUSED( s );
442     aout_instance_t *p_aout = (aout_instance_t *)userdata;
443     struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
444     mtime_t next_date;
445
446     assert(s);
447     assert(p_sys);
448
449     size_t buffer_size = p_sys->buffer_size;
450
451     PULSE_DEBUG( "Pulse stream request %d", length);
452
453     do{
454         aout_buffer_t *   p_buffer = NULL;
455         if(p_sys->started){
456             pa_usec_t latency;
457             int negative;
458             if(pa_stream_get_latency(p_sys->stream, &latency, &negative)<0){
459                 if (pa_context_errno(p_sys->context) != PA_ERR_NODATA) {
460                     msg_Err(p_aout, "pa_stream_get_latency() failed: %s", pa_strerror(pa_context_errno(p_sys->context)));
461                 }
462                 latency = 0;
463
464             }
465
466             PULSE_DEBUG( "Pulse stream request latency=%"PRId64"", latency);
467             next_date = mdate() + latency;
468
469             if(p_sys->start_date < next_date + AOUT_PTS_TOLERANCE ){
470                 p_buffer = aout_OutputNextBuffer( p_aout, next_date, 0);
471             }
472         }
473
474         if ( p_buffer != NULL )
475         {
476             PULSE_DEBUG( "Pulse stream request write buffer %d", p_buffer->i_buffer);
477             pa_stream_write(p_sys->stream, p_buffer->p_buffer, p_buffer->i_buffer, NULL, 0, PA_SEEK_RELATIVE);
478             length -= p_buffer->i_buffer;
479             aout_BufferFree( p_buffer );
480         }
481         else
482         {
483             PULSE_DEBUG( "Pulse stream request write zeroes");
484             void *data = pa_xmalloc(buffer_size);
485             bzero(data, buffer_size);
486             pa_stream_write(p_sys->stream, data, buffer_size, pa_xfree, 0, PA_SEEK_RELATIVE);
487             length -= buffer_size;
488         }
489     }while(length > buffer_size);
490
491     pa_threaded_mainloop_signal(p_sys->mainloop, 0);
492 }
493
494 static void stream_latency_update_cb(pa_stream *s, void *userdata) {
495     VLC_UNUSED( s );
496     aout_instance_t *p_aout = (aout_instance_t *)userdata;
497     struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
498
499     assert(s);
500
501     PULSE_DEBUG( "Pulse stream latency update");
502
503     pa_threaded_mainloop_signal(p_sys->mainloop, 0);
504 }
505
506 static void success_cb(pa_stream *s, int sucess, void *userdata)
507 {
508     VLC_UNUSED( s );
509     aout_instance_t *p_aout = (aout_instance_t *)userdata;
510     struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
511
512     VLC_UNUSED(sucess);
513
514     assert(s);
515
516     pa_threaded_mainloop_signal(p_sys->mainloop, 0);
517 }
518
519 #undef PULSE_DEBUG