]> git.sesse.net Git - vlc/blob - modules/audio_output/pulse.c
Use gettext_noop() consistently
[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 #include <vlc_plugin.h>
32
33 #include <vlc_aout.h>
34
35 #include <pulse/pulseaudio.h>
36
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  *****************************************************************************/
43 struct aout_sys_t
44 {
45     /** PulseAudio playback stream object */
46     struct pa_stream *stream;
47
48     /** PulseAudio connection context */
49     struct pa_context *context;
50
51     /** Main event loop object */
52     struct pa_threaded_mainloop *mainloop;
53
54     int started;
55     size_t buffer_size;
56     mtime_t start_date;
57 };
58
59 #define    PULSE_CLIENT_NAME N_("VLC media player")
60
61 #if 0
62 #define PULSE_DEBUG( ...) \
63     msg_Dbg( p_aout, __VA_ARGS__ )
64 #else
65 #define PULSE_DEBUG( ...) \
66     (void) 0
67 #endif
68
69
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"); \
74         goto label; \
75     }  \
76 } while(0);
77 /*****************************************************************************
78  * Local prototypes
79  *****************************************************************************/
80 static int  Open        ( vlc_object_t * );
81 static void Close       ( vlc_object_t * );
82 static void Play        ( aout_instance_t * );
83
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 /*****************************************************************************
91  * Module descriptor
92  *****************************************************************************/
93 vlc_module_begin();
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 );
102 vlc_module_end();
103
104 /*****************************************************************************
105  * Open: open the audio device
106  *****************************************************************************/
107 static int Open ( vlc_object_t *p_this )
108 {
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;
115
116     /* Allocate structures */
117     p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) );
118     if( p_sys == NULL )
119         return VLC_ENOMEM;
120     memset( p_sys, 0, sizeof( aout_sys_t ) );
121
122     PULSE_DEBUG( "Pulse start initialization");
123
124     ss.rate = p_aout->output.output.i_rate;
125     ss.channels = 2;
126
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;
131
132     if (!pa_sample_spec_valid(&ss)) {
133         msg_Err(p_aout,"Invalid sample spec");
134         goto fail;
135     }
136     
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;
141
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);
146
147     p_sys->buffer_size = a.minreq;
148
149     pa_channel_map_init_stereo(&map);
150
151
152     if (!(p_sys->mainloop = pa_threaded_mainloop_new())) {
153         msg_Err(p_aout, "Failed to allocate main loop");
154         goto fail;
155     }
156
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");
159         goto fail;
160     }
161
162     pa_context_set_state_callback(p_sys->context, context_state_cb, p_aout);
163
164     PULSE_DEBUG( "Pulse before context connect");
165
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)));
168         goto fail;
169     }
170
171     PULSE_DEBUG( "Pulse after context connect");
172
173     pa_threaded_mainloop_lock(p_sys->mainloop);
174     
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;
178     }
179
180     msg_Dbg(p_aout, "Pulse mainloop started");
181
182     /* Wait until the context is ready */
183     pa_threaded_mainloop_wait(p_sys->mainloop);
184
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;
188     }
189
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;
193     }
194
195     PULSE_DEBUG( "Pulse after new stream");
196
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);
200
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;
204     }
205
206      PULSE_DEBUG("Pulse stream connect");
207
208     /* Wait until the stream is ready */
209     pa_threaded_mainloop_wait(p_sys->mainloop);
210
211     msg_Dbg(p_aout,"Pulse stream connected");
212
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;
216     }
217
218
219     PULSE_DEBUG("Pulse after stream get status");
220
221     pa_threaded_mainloop_unlock(p_sys->mainloop);
222
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");
228     {
229         char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
230
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)));
235
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 ");
240     }
241
242     return VLC_SUCCESS;
243
244 unlock_and_fail:
245     msg_Dbg(p_aout, "Pulse initialization unlock and fail");
246
247     if (p_sys->mainloop)
248         pa_threaded_mainloop_unlock(p_sys->mainloop);
249 fail:
250     msg_Err(p_aout, "Pulse initialization failed");
251     uninit(p_aout);
252     return VLC_EGENERIC;
253 }
254
255 /*****************************************************************************
256  * Play: play a sound samples buffer
257  *****************************************************************************/
258 static void Play( aout_instance_t * p_aout )
259 {
260     struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
261
262     pa_operation *o;
263
264     if(!p_sys->started){
265         msg_Dbg(p_aout, "Pulse stream started");
266         p_sys->start_date =
267             aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
268         p_sys->started = 1;
269
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);
273         }
274         pa_threaded_mainloop_unlock(p_sys->mainloop);
275
276         pa_threaded_mainloop_signal(p_sys->mainloop, 0);
277     }
278 }
279
280 /*****************************************************************************
281  * Close: close the audio device
282  *****************************************************************************/
283 static void Close ( vlc_object_t *p_this )
284 {
285     aout_instance_t *p_aout = (aout_instance_t *)p_this;
286     struct aout_sys_t * p_sys = p_aout->output.p_sys;
287
288     msg_Dbg(p_aout, "Pulse Close");
289
290     if(p_sys->stream){
291         pa_operation *o;
292         pa_threaded_mainloop_lock(p_sys->mainloop);
293         pa_stream_set_write_callback(p_sys->stream, NULL, NULL);
294
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);
299             }
300
301         fail:
302
303             pa_operation_unref(o);
304         }
305
306         pa_threaded_mainloop_unlock(p_sys->mainloop);
307     }
308     uninit(p_aout);
309 }
310
311 static void uninit(aout_instance_t *p_aout){
312     struct aout_sys_t * p_sys = p_aout->output.p_sys;
313
314     if (p_sys->mainloop)
315         pa_threaded_mainloop_stop(p_sys->mainloop);
316
317     if (p_sys->stream) {
318         pa_stream_disconnect(p_sys->stream);
319         pa_stream_unref(p_sys->stream);
320         p_sys->stream = NULL;
321     }
322
323     if (p_sys->context) {
324         pa_context_disconnect(p_sys->context);
325         pa_context_unref(p_sys->context);
326         p_sys->context = NULL;
327     }
328
329     if (p_sys->mainloop) {
330         pa_threaded_mainloop_free(p_sys->mainloop);
331         p_sys->mainloop = NULL;
332     }
333
334     free(p_sys);
335     p_aout->output.p_sys = NULL;
336 }
337
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;
341
342     assert(c);
343
344     PULSE_DEBUG( "Pulse context state changed");
345
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);
352             break;
353
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");
359             break;
360     }
361 }
362
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;
366
367     assert(s);
368
369     PULSE_DEBUG( "Pulse stream state changed");
370
371     switch (pa_stream_get_state(s)) {
372
373         case PA_STREAM_READY:
374         case PA_STREAM_FAILED:
375         case PA_STREAM_TERMINATED:
376             pa_threaded_mainloop_signal(p_sys->mainloop, 0);
377             break;
378
379         case PA_STREAM_UNCONNECTED:
380         case PA_STREAM_CREATING:
381             break;
382     }
383 }
384
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;
388     mtime_t next_date;
389
390     assert(s);
391     assert(p_sys);
392
393     size_t buffer_size = p_sys->buffer_size;
394
395     PULSE_DEBUG( "Pulse stream request %d", length);
396
397     do{
398         aout_buffer_t *   p_buffer = NULL;
399         if(p_sys->started){
400             pa_usec_t latency;
401             int negative;
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)));
405                 }
406                 latency = 0;
407
408             }
409             PULSE_DEBUG( "Pulse stream request latency=%"PRId64"", latency);
410             next_date = mdate() + latency;
411
412
413             if(p_sys->start_date < next_date + AOUT_PTS_TOLERANCE ){
414     /*
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 );
418     */
419                 p_buffer = aout_OutputNextBuffer( p_aout, next_date, 0);
420             }
421         }
422
423         if ( p_buffer != NULL )
424         {
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 );
429         }
430         else
431         {
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;
437         }
438     }while(length > buffer_size);
439
440     pa_threaded_mainloop_signal(p_sys->mainloop, 0);
441 }
442
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;
446
447     assert(s);
448
449     PULSE_DEBUG( "Pulse stream latency update");
450
451     pa_threaded_mainloop_signal(p_sys->mainloop, 0);
452 }
453
454 static void success_cb(pa_stream *s, int sucess, void *userdata)
455 {
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;
458
459     VLC_UNUSED(sucess);
460
461     assert(s);
462
463     pa_threaded_mainloop_signal(p_sys->mainloop, 0);
464 }
465
466 #undef PULSE_DEBUG