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