pulse.c 21.5 KB
Newer Older
1
2
3
4
/*****************************************************************************
 * pulse.c : Pulseaudio output plugin for vlc
 *****************************************************************************
 * Copyright (C) 2008 the VideoLAN team
5
 * Copyright (C) 2009-2011 Rémi Denis-Courmont
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 *
 * Authors: Martin Hamrle <hamrle @ post . cz>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

28
#include <vlc_common.h>
29
#include <vlc_plugin.h>
30
#include <vlc_aout.h>
31
#include <vlc_aout_intf.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
32
#include <vlc_cpu.h>
33
34
35
36
37
38

#include <pulse/pulseaudio.h>

static int  Open        ( vlc_object_t * );
static void Close       ( vlc_object_t * );

39
vlc_module_begin ()
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
40
    set_shortname( "PulseAudio" )
41
    set_description( N_("Pulseaudio audio output") )
42
    set_capability( "audio output", 160 )
43
44
    set_category( CAT_AUDIO )
    set_subcategory( SUBCAT_AUDIO_AOUT )
45
    add_shortcut( "pulseaudio", "pa" )
46
47
    set_callbacks( Open, Close )
vlc_module_end ()
48

49
50
/* TODO: single static mainloop */

51
52
53
/* NOTE:
 * Be careful what you do when the PulseAudio mainloop is held, which is to say
 * within PulseAudio callbacks, or after pa_threaded_mainloop_lock().
54
55
 * In particular, a VLC variable callback cannot be triggered nor deleted with
 * the PulseAudio mainloop lock held, if the callback acquires the lock. */
56

57
58
59
60
61
struct aout_sys_t
{
    pa_stream *stream; /**< PulseAudio playback stream object */
    pa_context *context; /**< PulseAudio connection context */
    pa_threaded_mainloop *mainloop; /**< PulseAudio event loop */
62
63
    pa_volume_t base_volume; /**< 0dB reference volume */
    pa_cvolume cvolume; /**< actual sink input volume */
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
    //uint32_t byterate; /**< bytes per second */
};

/* Context helpers */
static void context_state_cb(pa_context *c, void *userdata)
{
    pa_threaded_mainloop *mainloop = userdata;

    switch (pa_context_get_state(c)) {
        case PA_CONTEXT_READY:
        case PA_CONTEXT_FAILED:
        case PA_CONTEXT_TERMINATED:
            pa_threaded_mainloop_signal(mainloop, 0);
        default:
            break;
    }
}

static bool context_wait(pa_threaded_mainloop *mainloop, pa_context *context)
{
    pa_context_state_t state;

    while ((state = pa_context_get_state(context)) != PA_CONTEXT_READY) {
        if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
            return -1;
        pa_threaded_mainloop_wait(mainloop);
    }
    return 0;
}

static void error(aout_instance_t *aout, const char *msg, pa_context *context)
{
    msg_Err(aout, "%s: %s", msg, pa_strerror(pa_context_errno(context)));
}

99
/* Sink */
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
static void sink_list_cb(pa_context *c, const pa_sink_info *i, int eol,
                         void *userdata)
{
    aout_instance_t *aout = userdata;
    vlc_value_t val, text;

    if (eol)
        return;
    (void) c;

    msg_Dbg(aout, "listing sink %s (%"PRIu32"): %s", i->name, i->index,
            i->description);
    val.i_int = i->index;
    text.psz_string = (char *)i->description;
    var_Change(aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text);
}

117
118
119
120
121
122
123
124
125
126
static void sink_info_cb(pa_context *c, const pa_sink_info *i, int eol,
                         void *userdata)
{
    aout_instance_t *aout = userdata;
    aout_sys_t *sys = aout->output.p_sys;

    if (eol)
        return;
    (void) c;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
127
128
129
130
131
    /* PulseAudio flat volume NORM / 100% / 0dB corresponds to no software
     * amplification and maximum hardware amplification.
     * VLC maps DEFAULT / 100% to no gain at all (software/hardware).
     * Thus we need to use the sink base_volume as a multiplier,
     * if and only if flat volume is active for our current sink. */
132
133
134
135
136
137
138
    if (i->flags & PA_SINK_FLAT_VOLUME)
        sys->base_volume = i->base_volume;
    else
        sys->base_volume = PA_VOLUME_NORM;
    msg_Dbg(aout, "base volume: %f", pa_sw_volume_to_linear(sys->base_volume));
}

139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/* Stream helpers */
static void stream_state_cb(pa_stream *s, void *userdata)
{
    pa_threaded_mainloop *mainloop = userdata;

    switch (pa_stream_get_state(s)) {
        case PA_STREAM_READY:
        case PA_STREAM_FAILED:
        case PA_STREAM_TERMINATED:
            pa_threaded_mainloop_signal(mainloop, 0);
        default:
            break;
    }
}

static void stream_moved_cb(pa_stream *s, void *userdata)
{
156
157
158
159
    aout_instance_t *aout = userdata;
    aout_sys_t *sys = aout->output.p_sys;
    pa_operation *op;
    uint32_t idx = pa_stream_get_device_index(s);
160

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
161
162
163
164
    msg_Dbg(aout, "connected to sink %"PRIu32": %s", idx,
                  pa_stream_get_device_name(s));
    op = pa_context_get_sink_info_by_index(sys->context, idx,
                                           sink_info_cb, aout);
165
166
    if (likely(op != NULL))
        pa_operation_unref(op);
167
168
169
170

    /* Update the variable if someone else moved our stream */
    var_Change(aout, "audio-device", VLC_VAR_SETVALUE,
               &(vlc_value_t){ .i_int = idx }, NULL);
171
172
}

173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
static void stream_overflow_cb(pa_stream *s, void *userdata)
{
    aout_instance_t *aout = userdata;

    msg_Err(aout, "overflow");
    (void) s;
}

static void stream_started_cb(pa_stream *s, void *userdata)
{
    aout_instance_t *aout = userdata;

    msg_Dbg(aout, "started");
    (void) s;
}

static void stream_suspended_cb(pa_stream *s, void *userdata)
{
    aout_instance_t *aout = userdata;

    msg_Dbg(aout, "suspended");
    (void) s;
}

static void stream_underflow_cb(pa_stream *s, void *userdata)
{
    aout_instance_t *aout = userdata;

    msg_Dbg(aout, "underflow");
    (void) s;
}

205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
static int stream_wait(pa_threaded_mainloop *mainloop, pa_stream *stream)
{
    pa_stream_state_t state;

    while ((state = pa_stream_get_state(stream)) != PA_STREAM_READY) {
        if (state == PA_STREAM_FAILED || state == PA_STREAM_TERMINATED)
            return -1;
        pa_threaded_mainloop_wait(mainloop);
    }
    return 0;
}

/* Memory free callback. The block_t address is in front of the data. */
static void data_free(void *data)
{
    block_t **pp = data, *block;

    memcpy(&block, pp - 1, sizeof (block));
    block_Release(block);
}

static void *data_convert(block_t **pp)
{
    block_t *block = *pp;
    /* In most cases, there is enough head room, and this is really cheap: */
    block = block_Realloc(block, sizeof (block), block->i_buffer);
    *pp = block;
    if (unlikely(block == NULL))
        return NULL;

    memcpy(block->p_buffer, &block, sizeof (block));
    block->p_buffer += sizeof (block);
    block->i_buffer -= sizeof (block);
    return block->p_buffer;
}

/*****************************************************************************
 * Play: play a sound samples buffer
 *****************************************************************************/
static void Play(aout_instance_t *aout)
{
    aout_sys_t *sys = aout->output.p_sys;
    pa_stream *s = sys->stream;

    /* Note: The core already holds the output FIFO lock at this point.
     * Therefore we must not under any circumstances (try to) acquire the
     * output FIFO lock while the PulseAudio threaded main loop lock is held
     * (including from PulseAudio stream callbacks). Otherwise lock inversion
     * will take place, and sooner or later a deadlock. */
    pa_threaded_mainloop_lock(sys->mainloop);

256
    if (pa_stream_is_corked(s) > 0) {
257
258
259
260
261
262
        pa_operation *op = pa_stream_cork(s, 0, NULL, NULL);
        if (op != NULL)
            pa_operation_unref(op);
        msg_Dbg(aout, "uncorking");
    }

263
#if 0
264
265
266
    /* This function should be called by the LibVLC core a header of time,
     * but not more than AOUT_MAX_PREPARE. The PulseAudio latency should be
     * shorter than that (though it might not be the case with some evil piece
267
268
269
     * of audio output hardware). So we may need to trigger playback early,
     * (that is to say, short cut the PulseAudio prebuffering). Otherwise,
     * audio and video may be out of synchronization. */
270
271
272
273
274
275
    pa_usec_t latency;
    int negative;
    if (pa_stream_get_latency(s, &latency, &negative) < 0) {
        /* Especially at start of stream, latency may not be known (yet). */
        if (pa_context_errno(sys->context) != PA_ERR_NODATA)
            error(aout, "cannot determine latency", sys->context);
276
    } else {
277
        mtime_t gap = aout_FifoFirstDate(&aout->output.fifo) - mdate()
278
                - latency;
279
280
281
282
283
284

        if (gap > AOUT_PTS_TOLERANCE)
            msg_Dbg(aout, "buffer too early (%"PRId64" us)", gap);
        else if (gap < -AOUT_PTS_TOLERANCE)
            msg_Err(aout, "buffer too late (%"PRId64" us)", -gap);
    }
285
286
287
288
289
290
291
292
293
294
295
296
297
298
#endif
#if 0 /* Fault injector to test underrun recovery */
    static unsigned u = 0;
    if ((++u % 500) == 0) {
        msg_Err(aout, "fault injection");
        msleep(CLOCK_FREQ*2);
    }
#endif

    /* This function is called exactly once per block in the output FIFO, so
     * this for-loop is not necessary.
     * If this function is changed to not always dequeue blocks, be sure to
     * limit the queue size to a reasonable limit to avoid huge leaks. */
    for (;;) {
299
        block_t *block = aout_FifoPop(&aout->output.fifo);
300
301
302
303
304
305
306
307
308
309
310
311
        if (block == NULL)
            break;

        const void *ptr = data_convert(&block);
        if (unlikely(ptr == NULL))
            break;

        size_t len = block->i_buffer;
        //mtime_t pts = block->i_pts, duration = block->i_length;

        if (pa_stream_write(s, ptr, len, data_free, 0, PA_SEEK_RELATIVE) < 0)
        {
312
            error(aout, "cannot write", sys->context);
313
314
315
316
317
318
319
            block_Release(block);
        }
    }

    pa_threaded_mainloop_unlock(sys->mainloop);
}

320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
static void Pause(aout_instance_t *aout, bool b_paused, mtime_t i_date)
{
    aout_sys_t *sys = aout->output.p_sys;
    pa_stream *s = sys->stream;

    /* Note: The core already holds the output FIFO lock at this point.
     * Therefore we must not under any circumstances (try to) acquire the
     * output FIFO lock while the PulseAudio threaded main loop lock is held
     * (including from PulseAudio stream callbacks). Otherwise lock inversion
     * will take place, and sooner or later a deadlock. */
    pa_threaded_mainloop_lock(sys->mainloop);

    pa_operation *op = pa_stream_cork(s, b_paused ? 1 : 0, NULL, NULL);
    if (op != NULL)
        pa_operation_unref(op);

    pa_threaded_mainloop_unlock(sys->mainloop);
}

339
static int VolumeSet(aout_instance_t *aout, audio_volume_t vol, bool mute)
340
341
342
343
344
345
346
{
    aout_sys_t *sys = aout->output.p_sys;
    pa_threaded_mainloop *mainloop = sys->mainloop;
    pa_operation *op;

    uint32_t idx = pa_stream_get_index(sys->stream);
    pa_volume_t volume = pa_sw_volume_from_linear(vol / (float)AOUT_VOLUME_DEFAULT);
347
    pa_cvolume cvolume;
348

349
350
    /* TODO: do not ruin the channel balance (if set outside VLC) */
    /* TODO: notify UI about volume changes by other PulseAudio clients */
351
    pa_cvolume_set(&sys->cvolume, sys->cvolume.channels, volume);
352
353
    pa_sw_cvolume_multiply_scalar(&cvolume, &sys->cvolume, sys->base_volume);
    assert(pa_cvolume_valid(&cvolume));
354
355

    pa_threaded_mainloop_lock(mainloop);
356
    op = pa_context_set_sink_input_volume(sys->context, idx, &cvolume, NULL, NULL);
357
358
    if (likely(op != NULL))
        pa_operation_unref(op);
359
    op = pa_context_set_sink_input_mute(sys->context, idx, mute, NULL, NULL);
360
361
    if (likely(op != NULL))
        pa_operation_unref(op);
362
363
364
365
    pa_threaded_mainloop_unlock(mainloop);

    return 0;
}
366

367
368
369
370
371
372
373
374
375
376
static int StreamMove(vlc_object_t *obj, const char *varname, vlc_value_t old,
                      vlc_value_t val, void *userdata)
{
    aout_instance_t *aout = (aout_instance_t *)obj;
    aout_sys_t *sys = aout->output.p_sys;
    pa_stream *s = userdata;
    pa_operation *op;
    uint32_t idx = pa_stream_get_index(s);
    uint32_t sink_idx = val.i_int;

377
378
379
    (void) varname; (void) old;

    pa_threaded_mainloop_lock(sys->mainloop);
380
381
    op = pa_context_move_sink_input_by_index(sys->context, idx, sink_idx,
                                             NULL, NULL);
382
383
384
385
    if (likely(op != NULL)) {
        pa_operation_unref(op);
        msg_Dbg(aout, "moving to sink %"PRIu32, sink_idx);
    } else
386
        error(aout, "cannot move sink", sys->context);
387
388
389
    pa_threaded_mainloop_unlock(sys->mainloop);

    return (op != NULL) ? VLC_SUCCESS : VLC_EGENERIC;
390
391
392
}


393
394
395
/*****************************************************************************
 * Open: open the audio device
 *****************************************************************************/
396
static int Open(vlc_object_t *obj)
397
{
398
    aout_instance_t *aout = (aout_instance_t *)obj;
399
    pa_operation *op;
400

401
402
    /* Sample format specification */
    struct pa_sample_spec ss;
Danny Wood's avatar
Danny Wood committed
403

404
    switch(aout->output.output.i_format)
Danny Wood's avatar
Danny Wood committed
405
    {
406
        case VLC_CODEC_F64B:
407
            aout->output.output.i_format = VLC_CODEC_F32B;
408
409
        case VLC_CODEC_F32B:
            ss.format = PA_SAMPLE_FLOAT32BE;
410
            break;
411
        case VLC_CODEC_F64L:
412
            aout->output.output.i_format = VLC_CODEC_F32L;
413
414
        case VLC_CODEC_F32L:
            ss.format = PA_SAMPLE_FLOAT32LE;
415
            break;
416
        case VLC_CODEC_FI32:
417
            aout->output.output.i_format = VLC_CODEC_FL32;
418
            ss.format = PA_SAMPLE_FLOAT32NE;
419
            break;
420
421
        case VLC_CODEC_S32B:
            ss.format = PA_SAMPLE_S32BE;
422
            break;
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
        case VLC_CODEC_S32L:
            ss.format = PA_SAMPLE_S32LE;
            break;
        case VLC_CODEC_S24B:
            ss.format = PA_SAMPLE_S24BE;
            break;
        case VLC_CODEC_S24L:
            ss.format = PA_SAMPLE_S24LE;
            break;
        case VLC_CODEC_S16B:
            ss.format = PA_SAMPLE_S16BE;
            break;
        case VLC_CODEC_S16L:
            ss.format = PA_SAMPLE_S16LE;
            break;
        case VLC_CODEC_S8:
439
            aout->output.output.i_format = VLC_CODEC_U8;
440
441
        case VLC_CODEC_U8:
            ss.format = PA_SAMPLE_U8;
442
443
            break;
        default:
444
445
            if (HAVE_FPU)
            {
446
                aout->output.output.i_format = VLC_CODEC_FL32;
447
448
449
450
                ss.format = PA_SAMPLE_FLOAT32NE;
            }
            else
            {
451
                aout->output.output.i_format = VLC_CODEC_S16N;
452
453
454
                ss.format = PA_SAMPLE_S16NE;
            }
            break;
Danny Wood's avatar
Danny Wood committed
455
456
    }

457
458
    ss.rate = aout->output.output.i_rate;
    ss.channels = aout_FormatNbChannels(&aout->output.output);
459
    if (!pa_sample_spec_valid(&ss)) {
460
        msg_Err(aout, "unsupported sample specification");
461
        return VLC_EGENERIC;
462
    }
463

464
    /* Channel mapping (order defined in vlc_aout.h) */
465
466
467
    struct pa_channel_map map;
    map.channels = 0;

468
    if (aout->output.output.i_physical_channels & AOUT_CHAN_LEFT)
469
        map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_LEFT;
470
    if (aout->output.output.i_physical_channels & AOUT_CHAN_RIGHT)
471
        map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_RIGHT;
472
    if (aout->output.output.i_physical_channels & AOUT_CHAN_MIDDLELEFT)
473
        map.map[map.channels++] = PA_CHANNEL_POSITION_SIDE_LEFT;
474
    if (aout->output.output.i_physical_channels & AOUT_CHAN_MIDDLERIGHT)
475
        map.map[map.channels++] = PA_CHANNEL_POSITION_SIDE_RIGHT;
476
477
478
479
480
481
482
483
484
485
486
487
488
    if (aout->output.output.i_physical_channels & AOUT_CHAN_REARLEFT)
        map.map[map.channels++] = PA_CHANNEL_POSITION_REAR_LEFT;
    if (aout->output.output.i_physical_channels & AOUT_CHAN_REARRIGHT)
        map.map[map.channels++] = PA_CHANNEL_POSITION_REAR_RIGHT;
    if (aout->output.output.i_physical_channels & AOUT_CHAN_REARCENTER)
        map.map[map.channels++] = PA_CHANNEL_POSITION_REAR_CENTER;
    if (aout->output.output.i_physical_channels & AOUT_CHAN_CENTER)
    {
        if (ss.channels == 1)
            map.map[map.channels++] = PA_CHANNEL_POSITION_MONO;
        else
            map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_CENTER;
    }
489
    if (aout->output.output.i_physical_channels & AOUT_CHAN_LFE)
490
491
492
493
        map.map[map.channels++] = PA_CHANNEL_POSITION_LFE;

    for (unsigned i = 0; map.channels < ss.channels; i++) {
        map.map[map.channels++] = PA_CHANNEL_POSITION_AUX0 + i;
494
        msg_Warn(aout, "mapping channel %"PRIu8" to AUX%u", map.channels, i);
495
    }
496

497
    if (!pa_channel_map_valid(&map)) {
498
        msg_Err(aout, "unsupported channel map");
499
500
501
        return VLC_EGENERIC;
    } else {
        const char *name = pa_channel_map_to_pretty_name(&map);
502
        msg_Dbg(aout, "using %s channel map", (name != NULL) ? name : "?");
503
    }
504

505
    /* Stream parameters */
506
507
    const pa_stream_flags_t flags = PA_STREAM_INTERPOLATE_TIMING
                                  | PA_STREAM_AUTO_TIMING_UPDATE
508
                                  | PA_STREAM_START_CORKED;
509

510
    const uint32_t byterate = pa_bytes_per_second(&ss);
511
    struct pa_buffer_attr attr;
512
    attr.maxlength = -1;
513
514
    attr.tlength = byterate * AOUT_MAX_PREPARE_TIME / CLOCK_FREQ;
    attr.prebuf = -1;
515
516
    attr.minreq = -1;
    attr.fragsize = 0; /* not used for output */
517

518
    /* Allocate structures */
519
    aout_sys_t *sys = malloc(sizeof(*sys));
520
521
    if (unlikely(sys == NULL))
        return VLC_ENOMEM;
522
    aout->output.p_sys = sys;
523
    sys->context = NULL;
524
    sys->stream = NULL;
525
    //sys->byterate = byterate;
526

527
    /* Channel volume */
528
    sys->base_volume = PA_VOLUME_NORM;
529
530
    pa_cvolume_set(&sys->cvolume, ss.channels, PA_VOLUME_NORM);

531
532
533
    /* Allocate threaded main loop */
    pa_threaded_mainloop *mainloop = pa_threaded_mainloop_new();
    if (unlikely(mainloop == NULL)) {
534
535
        free(sys);
        return VLC_ENOMEM;
536
    }
537
    sys->mainloop = mainloop;
538

539
540
    if (pa_threaded_mainloop_start(mainloop) < 0) {
        pa_threaded_mainloop_free(mainloop);
541
542
543
        free(sys);
        return VLC_ENOMEM;
    }
544
545
546
547
548
549
550
551
    pa_threaded_mainloop_lock(mainloop);

    /* Connect to PulseAudio server */
    char *user_agent = var_InheritString(aout, "user-agent");
    pa_context *ctx = pa_context_new(pa_threaded_mainloop_get_api(mainloop),
                                     user_agent);
    free(user_agent);
    if (unlikely(ctx == NULL))
552
        goto fail;
553
    sys->context = ctx;
554

555
556
557
558
    pa_context_set_state_callback(ctx, context_state_cb, mainloop);
    if (pa_context_connect(ctx, NULL, 0, NULL) < 0
     || context_wait(mainloop, ctx)) {
        error(aout, "cannot connect to server", ctx);
559
        goto fail;
560
561
    }

562
563
564
565
566
    /* Create a playback stream */
    pa_stream *s = pa_stream_new(ctx, "audio stream", &ss, &map);
    if (s == NULL) {
        error(aout, "cannot create stream", ctx);
        goto fail;
567
    }
568
569
570
    sys->stream = s;
    pa_stream_set_state_callback(s, stream_state_cb, mainloop);
    pa_stream_set_moved_callback(s, stream_moved_cb, aout);
571
572
573
574
    pa_stream_set_overflow_callback(s, stream_overflow_cb, aout);
    pa_stream_set_started_callback(s, stream_started_cb, aout);
    pa_stream_set_suspended_callback(s, stream_suspended_cb, aout);
    pa_stream_set_underflow_callback(s, stream_underflow_cb, aout);
575

576
    if (pa_stream_connect_playback(s, NULL, &attr, flags, NULL, NULL) < 0
577
578
579
     || stream_wait(mainloop, s)) {
        error(aout, "cannot connect stream", ctx);
        goto fail;
580
581
    }

582
583
    const struct pa_buffer_attr *pba = pa_stream_get_buffer_attr(s);
    msg_Dbg(aout, "using buffer metrics: maxlength=%u, tlength=%u, "
584
585
            "prebuf=%u, minreq=%u",
            pba->maxlength, pba->tlength, pba->prebuf, pba->minreq);
586

587
    aout->output.i_nb_samples = pba->minreq / pa_frame_size(&ss);
588
589
590
591
592
593
594
595
596
597

    var_Create(aout, "audio-device", VLC_VAR_INTEGER|VLC_VAR_HASCHOICE);
    var_Change(aout, "audio-device", VLC_VAR_SETTEXT,
               &(vlc_value_t){ .psz_string = (char *)_("Audio device") },
               NULL);
    var_AddCallback (aout, "audio-device", StreamMove, s);
    op = pa_context_get_sink_info_list(ctx, sink_list_cb, aout);
    /* We may need to wait for completion... once LibVLC supports this */
    if (op != NULL)
        pa_operation_unref(op);
598
    stream_moved_cb(s, aout);
599
600
601
    pa_threaded_mainloop_unlock(mainloop);

    aout->output.pf_play = Play;
602
    aout->output.pf_pause = Pause;
603
    aout->output.pf_volume_set = VolumeSet;
604
605
606
    return VLC_SUCCESS;

fail:
607
608
    pa_threaded_mainloop_unlock(mainloop);
    Close(obj);
609
610
611
612
613
614
    return VLC_EGENERIC;
}

/*****************************************************************************
 * Close: close the audio device
 *****************************************************************************/
615
static void Close (vlc_object_t *obj)
616
{
617
    aout_instance_t *aout = (aout_instance_t *)obj;
618
619
620
621
622
    aout_sys_t *sys = aout->output.p_sys;
    pa_threaded_mainloop *mainloop = sys->mainloop;
    pa_context *ctx = sys->context;
    pa_stream *s = sys->stream;

623
624
625
626
627
628
    if (s != NULL) {
        /* The callback takes mainloop lock, so it CANNOT be held here! */
        var_DelCallback (aout, "audio-device", StreamMove, s);
        var_Destroy (aout, "audio-device");
    }

629
630
631
632
633
634
635
636
637
638
639
640
    pa_threaded_mainloop_lock(mainloop);
    if (s != NULL) {
        pa_operation *op;

        op = pa_stream_flush(s, NULL, NULL);
        if (op != NULL)
            pa_operation_unref(op);
        op = pa_stream_drain(s, NULL, NULL);
        if (op != NULL)
            pa_operation_unref(op);
        pa_stream_disconnect(s);
        pa_stream_unref(s);
641
    }
642
643
644
645
    if (ctx != NULL)
        pa_context_unref(ctx);
    pa_threaded_mainloop_unlock(mainloop);
    pa_threaded_mainloop_free(mainloop);
646
    free(sys);
647
}