Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
View | Details | Raw Unified | Return to bug 215342 | Differences between
and this patch

Collapse All | Expand All

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

Return to bug 215342