]> git.sesse.net Git - vlc/blob - modules/audio_output/pulse.c
1222211cf0b6e1b7c4aadb233d62ab09a4d15a03
[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/vlc.h>
31
32 #include <vlc_aout.h>
33
34 #include <pulse/pulseaudio.h>
35
36 /*****************************************************************************
37  * aout_sys_t: Pulseaudio output method descriptor
38  *****************************************************************************
39  * This structure is part of the audio output thread descriptor.
40  * It describes the specific properties of an audio device.
41  *****************************************************************************/
42 struct aout_sys_t
43 {
44     /** PulseAudio playback stream object */
45     struct pa_stream *stream;
46
47     /** PulseAudio connection context */
48     struct pa_context *context;
49
50     /** Main event loop object */
51     struct pa_threaded_mainloop *mainloop;
52
53     int started;
54     size_t buffer_size;
55     mtime_t start_date;
56 };
57
58 #define    PULSE_CLIENT_NAME N_("VLC media player")
59
60 #if 0
61 #define PULSE_DEBUG( ...) \
62     msg_Dbg( p_aout, __VA_ARGS__ )
63 #else
64 #define PULSE_DEBUG( ...) \
65     (void) 0
66 #endif
67
68
69 #define CHECK_DEAD_GOTO(label) do { \
70 if (!p_sys->context || pa_context_get_state(p_sys->context) != PA_CONTEXT_READY || \
71     !p_sys->stream || pa_stream_get_state(p_sys->stream) != PA_STREAM_READY) { \
72         msg_Err(p_aout, "Connection died: %s", p_sys->context ? pa_strerror(pa_context_errno(p_sys->context)) : "NULL"); \
73         goto label; \
74     }  \
75 } while(0);
76 /*****************************************************************************
77  * Local prototypes
78  *****************************************************************************/
79 static int  Open        ( vlc_object_t * );
80 static void Close       ( vlc_object_t * );
81 static void Play        ( aout_instance_t * );
82
83 static void context_state_cb(pa_context *c, void *userdata);
84 static void stream_state_cb(pa_stream *s, void * userdata);
85 static void stream_request_cb(pa_stream *s, size_t length, void *userdata);
86 static void stream_latency_update_cb(pa_stream *s, void *userdata);
87 static void success_cb(pa_stream *s, int sucess, void *userdata);
88 static void uninit(aout_instance_t *p_aout);
89 /*****************************************************************************
90  * Module descriptor
91  *****************************************************************************/
92 vlc_module_begin();
93     set_shortname( "Pulse Audio" );
94     set_description( _("Pulseaudio audio output") );
95     set_capability( "audio output", 40 );
96     set_category( CAT_AUDIO );
97     set_subcategory( SUBCAT_AUDIO_AOUT );
98     add_shortcut( "pulseaudio" );
99     add_shortcut( "pa" );
100     set_callbacks( Open, Close );
101 vlc_module_end();
102
103 /*****************************************************************************
104  * Open: open the audio device
105  *****************************************************************************/
106 static int Open ( vlc_object_t *p_this )
107 {
108     aout_instance_t *p_aout = (aout_instance_t *)p_this;
109     struct aout_sys_t * p_sys;
110     struct pa_sample_spec ss;
111     const struct pa_buffer_attr *buffer_attr;
112     struct pa_buffer_attr a;
113     struct pa_channel_map map;
114
115     /* Allocate structures */
116     p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) );
117     if( p_sys == NULL )
118         return VLC_ENOMEM;
119     memset( p_sys, 0, sizeof( aout_sys_t ) );
120
121     PULSE_DEBUG( "Pulse start initialization");
122
123     ss.rate = p_aout->output.output.i_rate;
124     ss.channels = 2;
125
126     ss.format = PA_SAMPLE_S16LE;
127     p_aout->output.output.i_physical_channels =
128             AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
129     p_aout->output.output.i_format = AOUT_FMT_S16_NE;
130
131     if (!pa_sample_spec_valid(&ss)) {
132         msg_Err(p_aout,"Invalid sample spec");
133         goto fail;
134     }
135     
136     a.maxlength = pa_bytes_per_second(&ss)/4/pa_frame_size(&ss);
137     a.tlength = a.maxlength*9/10;
138     a.prebuf = a.tlength/2;
139     a.minreq = a.tlength/10;
140
141     a.maxlength *= pa_frame_size(&ss);
142     a.tlength *= pa_frame_size(&ss);
143     a.prebuf *= pa_frame_size(&ss);
144     a.minreq *= pa_frame_size(&ss);
145
146     p_sys->buffer_size = a.minreq;
147
148     pa_channel_map_init_stereo(&map);
149
150
151     if (!(p_sys->mainloop = pa_threaded_mainloop_new())) {
152         msg_Err(p_aout, "Failed to allocate main loop");
153         goto fail;
154     }
155
156     if (!(p_sys->context = pa_context_new(pa_threaded_mainloop_get_api(p_sys->mainloop), _( PULSE_CLIENT_NAME )))) {
157         msg_Err(p_aout, "Failed to allocate context");
158         goto fail;
159     }
160
161     pa_context_set_state_callback(p_sys->context, context_state_cb, p_aout);
162
163     PULSE_DEBUG( "Pulse before context connect");
164
165     if (pa_context_connect(p_sys->context, NULL, 0, NULL) < 0) {
166         msg_Err(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context)));
167         goto fail;
168     }
169
170     PULSE_DEBUG( "Pulse after context connect");
171
172     pa_threaded_mainloop_lock(p_sys->mainloop);
173     
174     if (pa_threaded_mainloop_start(p_sys->mainloop) < 0) {
175         msg_Err(p_aout, "Failed to start main loop");
176         goto unlock_and_fail;
177     }
178
179     msg_Dbg(p_aout, "Pulse mainloop started");
180
181     /* Wait until the context is ready */
182     pa_threaded_mainloop_wait(p_sys->mainloop);
183
184     if (pa_context_get_state(p_sys->context) != PA_CONTEXT_READY) {
185         msg_Err(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context)));
186         goto unlock_and_fail;
187     }
188
189     if (!(p_sys->stream = pa_stream_new(p_sys->context, "audio stream", &ss, &map))) {
190         msg_Err(p_aout, "Failed to create stream: %s", pa_strerror(pa_context_errno(p_sys->context)));
191         goto unlock_and_fail;
192     }
193
194     PULSE_DEBUG( "Pulse after new stream");
195
196     pa_stream_set_state_callback(p_sys->stream, stream_state_cb, p_aout);
197     pa_stream_set_write_callback(p_sys->stream, stream_request_cb, p_aout);
198     pa_stream_set_latency_update_callback(p_sys->stream, stream_latency_update_cb, p_aout);
199
200     if (pa_stream_connect_playback(p_sys->stream, NULL, &a, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL) < 0) {
201         msg_Err(p_aout, "Failed to connect stream: %s", pa_strerror(pa_context_errno(p_sys->context)));
202         goto unlock_and_fail;
203     }
204
205      PULSE_DEBUG("Pulse stream connect");
206
207     /* Wait until the stream is ready */
208     pa_threaded_mainloop_wait(p_sys->mainloop);
209
210     msg_Dbg(p_aout,"Pulse stream connected");
211
212     if (pa_stream_get_state(p_sys->stream) != PA_STREAM_READY) {
213         msg_Err(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context)));
214         goto unlock_and_fail;
215     }
216
217
218     PULSE_DEBUG("Pulse after stream get status");
219
220     pa_threaded_mainloop_unlock(p_sys->mainloop);
221
222     buffer_attr = pa_stream_get_buffer_attr(p_sys->stream);
223     p_aout->output.i_nb_samples = buffer_attr->minreq / pa_frame_size(&ss);
224     p_aout->output.pf_play = Play;
225     aout_VolumeSoftInit(p_aout);
226     msg_Dbg(p_aout, "Pulse initialized successfully");
227     {
228         char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
229
230         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);
231         msg_Dbg(p_aout, "Using sample spec '%s', channel map '%s'.",
232                 pa_sample_spec_snprint(sst, sizeof(sst), pa_stream_get_sample_spec(p_sys->stream)),
233                 pa_channel_map_snprint(cmt, sizeof(cmt), pa_stream_get_channel_map(p_sys->stream)));
234
235             msg_Dbg(p_aout, "Connected to device %s (%u, %ssuspended).",
236                         pa_stream_get_device_name(p_sys->stream),
237                         pa_stream_get_device_index(p_sys->stream),
238                         pa_stream_is_suspended(p_sys->stream) ? "" : "not ");
239     }
240
241     return VLC_SUCCESS;
242
243 unlock_and_fail:
244     msg_Dbg(p_aout, "Pulse initialization unlock and fail");
245
246     if (p_sys->mainloop)
247         pa_threaded_mainloop_unlock(p_sys->mainloop);
248 fail:
249     msg_Err(p_aout, "Pulse initialization failed");
250     uninit(p_aout);
251     return VLC_EGENERIC;
252 }
253
254 /*****************************************************************************
255  * Play: play a sound samples buffer
256  *****************************************************************************/
257 static void Play( aout_instance_t * p_aout )
258 {
259     struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
260
261     pa_operation *o;
262
263     if(!p_sys->started){
264         msg_Dbg(p_aout, "Pulse stream started");
265         p_sys->start_date =
266             aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
267         p_sys->started = 1;
268
269         pa_threaded_mainloop_lock(p_sys->mainloop);
270         if((o = pa_stream_flush(p_sys->stream, success_cb, p_aout))){
271             pa_operation_unref(o);
272         }
273         pa_threaded_mainloop_unlock(p_sys->mainloop);
274
275         pa_threaded_mainloop_signal(p_sys->mainloop, 0);
276     }
277 }
278
279 /*****************************************************************************
280  * Close: close the audio device
281  *****************************************************************************/
282 static void Close ( vlc_object_t *p_this )
283 {
284     aout_instance_t *p_aout = (aout_instance_t *)p_this;
285     struct aout_sys_t * p_sys = p_aout->output.p_sys;
286
287     msg_Dbg(p_aout, "Pulse Close");
288
289     if(p_sys->stream){
290         pa_operation *o;
291         pa_threaded_mainloop_lock(p_sys->mainloop);
292         pa_stream_set_write_callback(p_sys->stream, NULL, NULL);
293
294         if((o = pa_stream_drain(p_sys->stream, success_cb, p_aout))){
295             while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
296                 CHECK_DEAD_GOTO(fail);
297                 pa_threaded_mainloop_wait(p_sys->mainloop);
298             }
299
300         fail:
301
302             pa_operation_unref(o);
303         }
304
305         pa_threaded_mainloop_unlock(p_sys->mainloop);
306     }
307     uninit(p_aout);
308 }
309
310 static void uninit(aout_instance_t *p_aout){
311     struct aout_sys_t * p_sys = p_aout->output.p_sys;
312
313     if (p_sys->mainloop)
314         pa_threaded_mainloop_stop(p_sys->mainloop);
315
316     if (p_sys->stream) {
317         pa_stream_disconnect(p_sys->stream);
318         pa_stream_unref(p_sys->stream);
319         p_sys->stream = NULL;
320     }
321
322     if (p_sys->context) {
323         pa_context_disconnect(p_sys->context);
324         pa_context_unref(p_sys->context);
325         p_sys->context = NULL;
326     }
327
328     if (p_sys->mainloop) {
329         pa_threaded_mainloop_free(p_sys->mainloop);
330         p_sys->mainloop = NULL;
331     }
332
333     free(p_sys);
334     p_aout->output.p_sys = NULL;
335 }
336
337 static void context_state_cb(pa_context *c, void *userdata) {
338     aout_instance_t *p_aout = (aout_instance_t *)userdata;
339     struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
340
341     assert(c);
342
343     PULSE_DEBUG( "Pulse context state changed");
344
345     switch (pa_context_get_state(c)) {
346         case PA_CONTEXT_READY:
347         case PA_CONTEXT_TERMINATED:
348         case PA_CONTEXT_FAILED:
349         PULSE_DEBUG( "Pulse context state changed signal");
350             pa_threaded_mainloop_signal(p_sys->mainloop, 0);
351             break;
352
353         case PA_CONTEXT_UNCONNECTED:
354         case PA_CONTEXT_CONNECTING:
355         case PA_CONTEXT_AUTHORIZING:
356         case PA_CONTEXT_SETTING_NAME:
357         PULSE_DEBUG( "Pulse context state changed no signal");
358             break;
359     }
360 }
361
362 static void stream_state_cb(pa_stream *s, void * userdata) {
363     aout_instance_t *p_aout = (aout_instance_t *)userdata;
364     struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
365
366     assert(s);
367
368     PULSE_DEBUG( "Pulse stream state changed");
369
370     switch (pa_stream_get_state(s)) {
371
372         case PA_STREAM_READY:
373         case PA_STREAM_FAILED:
374         case PA_STREAM_TERMINATED:
375             pa_threaded_mainloop_signal(p_sys->mainloop, 0);
376             break;
377
378         case PA_STREAM_UNCONNECTED:
379         case PA_STREAM_CREATING:
380             break;
381     }
382 }
383
384 static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
385     aout_instance_t *p_aout = (aout_instance_t *)userdata;
386     struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
387     mtime_t next_date;
388
389     assert(s);
390     assert(p_sys);
391
392     size_t buffer_size = p_sys->buffer_size;
393
394     PULSE_DEBUG( "Pulse stream request %d", length);
395
396     do{
397         aout_buffer_t *   p_buffer = NULL;
398         if(p_sys->started){
399             pa_usec_t latency;
400             int negative;
401             if(pa_stream_get_latency(p_sys->stream, &latency, &negative)<0){
402                 if (pa_context_errno(p_sys->context) != PA_ERR_NODATA) {
403                     msg_Err(p_aout, "pa_stream_get_latency() failed: %s", pa_strerror(pa_context_errno(p_sys->context)));
404                 }
405                 latency = 0;
406
407             }
408             PULSE_DEBUG( "Pulse stream request latency=%"PRId64"", latency);
409             next_date = mdate() + latency;
410
411
412             if(p_sys->start_date < next_date + AOUT_PTS_TOLERANCE ){
413     /*
414                   vlc_mutex_lock( &p_aout->output_fifo_lock );
415                 p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo );
416                 vlc_mutex_unlock( &p_aout->output_fifo_lock );
417     */
418                 p_buffer = aout_OutputNextBuffer( p_aout, next_date, 0);
419             }
420         }
421
422         if ( p_buffer != NULL )
423         {
424             PULSE_DEBUG( "Pulse stream request write buffer %d", p_buffer->i_nb_bytes);
425             pa_stream_write(p_sys->stream, p_buffer->p_buffer, p_buffer->i_nb_bytes, NULL, 0, PA_SEEK_RELATIVE);
426             length -= p_buffer->i_nb_bytes;
427             aout_BufferFree( p_buffer );
428         }
429         else
430         {
431             PULSE_DEBUG( "Pulse stream request write zeroes");
432             void *data = pa_xmalloc(buffer_size);
433             bzero(data, buffer_size);
434             pa_stream_write(p_sys->stream, data, buffer_size, pa_xfree, 0, PA_SEEK_RELATIVE);
435             length -= buffer_size;
436         }
437     }while(length > buffer_size);
438
439     pa_threaded_mainloop_signal(p_sys->mainloop, 0);
440 }
441
442 static void stream_latency_update_cb(pa_stream *s, 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;
445
446     assert(s);
447
448     PULSE_DEBUG( "Pulse stream latency update");
449
450     pa_threaded_mainloop_signal(p_sys->mainloop, 0);
451 }
452
453 static void success_cb(pa_stream *s, int sucess, void *userdata)
454 {
455     aout_instance_t *p_aout = (aout_instance_t *)userdata;
456     struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
457
458     VLC_UNUSED(sucess);
459
460     assert(s);
461
462     pa_threaded_mainloop_signal(p_sys->mainloop, 0);
463 }
464
465 #undef PULSE_DEBUG