pulse.c 20.9 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>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
31
#include <vlc_cpu.h>
32
33
34
35
36
37

#include <pulse/pulseaudio.h>

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

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

48
49
/* TODO: single static mainloop */

50
51
52
/* 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().
53
54
 * In particular, a VLC variable callback cannot be triggered nor deleted with
 * the PulseAudio mainloop lock held, if the callback acquires the lock. */
55

56
57
58
59
60
struct aout_sys_t
{
    pa_stream *stream; /**< PulseAudio playback stream object */
    pa_context *context; /**< PulseAudio connection context */
    pa_threaded_mainloop *mainloop; /**< PulseAudio event loop */
61
62
    pa_volume_t base_volume; /**< 0dB reference volume */
    pa_cvolume cvolume; /**< actual sink input volume */
63
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
    //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)));
}

98
/* Sink */
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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);
}

116
117
118
119
120
121
122
123
124
125
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
126
127
128
129
130
    /* 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. */
131
132
133
134
135
136
137
    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));
}

138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/* 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)
{
155
156
157
158
    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);
159

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
160
161
162
163
    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);
164
165
    if (likely(op != NULL))
        pa_operation_unref(op);
166
167
168
169

    /* Update the variable if someone else moved our stream */
    var_Change(aout, "audio-device", VLC_VAR_SETVALUE,
               &(vlc_value_t){ .i_int = idx }, NULL);
170
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
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;
}

204
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
256
257
258
259
260
261
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);

    if (pa_stream_is_corked(sys->stream) > 0) {
        pa_operation *op = pa_stream_cork(s, 0, NULL, NULL);
        if (op != NULL)
            pa_operation_unref(op);
        msg_Dbg(aout, "uncorking");
    }

262
#if 0
263
264
265
    /* 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
266
267
268
     * 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. */
269
270
271
272
273
274
    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);
275
    } else {
276
        mtime_t gap = aout_FifoFirstDate(&aout->output.fifo) - mdate()
277
                - latency;
278
279
280
281
282
283

        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);
    }
284
285
286
287
288
289
290
291
292
293
294
295
296
297
#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 (;;) {
298
        block_t *block = aout_FifoPop(&aout->output.fifo);
299
300
301
302
303
304
305
306
307
308
309
310
        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)
        {
311
            error(aout, "cannot write", sys->context);
312
313
314
315
316
317
318
            block_Release(block);
        }
    }

    pa_threaded_mainloop_unlock(sys->mainloop);
}

319
static int VolumeSet(aout_instance_t *aout, audio_volume_t vol, bool mute)
320
321
322
323
324
325
326
{
    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);
327
    pa_cvolume cvolume;
328

329
330
    /* TODO: do not ruin the channel balance (if set outside VLC) */
    /* TODO: notify UI about volume changes by other PulseAudio clients */
331
    pa_cvolume_set(&sys->cvolume, sys->cvolume.channels, volume);
332
333
    pa_sw_cvolume_multiply_scalar(&cvolume, &sys->cvolume, sys->base_volume);
    assert(pa_cvolume_valid(&cvolume));
334
335

    pa_threaded_mainloop_lock(mainloop);
336
    op = pa_context_set_sink_input_volume(sys->context, idx, &cvolume, NULL, NULL);
337
338
    if (likely(op != NULL))
        pa_operation_unref(op);
339
    op = pa_context_set_sink_input_mute(sys->context, idx, mute, NULL, NULL);
340
341
    if (likely(op != NULL))
        pa_operation_unref(op);
342
343
344
345
    pa_threaded_mainloop_unlock(mainloop);

    return 0;
}
346

347
348
349
350
351
352
353
354
355
356
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;

357
358
359
    (void) varname; (void) old;

    pa_threaded_mainloop_lock(sys->mainloop);
360
361
    op = pa_context_move_sink_input_by_index(sys->context, idx, sink_idx,
                                             NULL, NULL);
362
363
364
365
    if (likely(op != NULL)) {
        pa_operation_unref(op);
        msg_Dbg(aout, "moving to sink %"PRIu32, sink_idx);
    } else
366
        error(aout, "cannot move sink", sys->context);
367
368
369
    pa_threaded_mainloop_unlock(sys->mainloop);

    return (op != NULL) ? VLC_SUCCESS : VLC_EGENERIC;
370
371
372
}


373
374
375
/*****************************************************************************
 * Open: open the audio device
 *****************************************************************************/
376
static int Open(vlc_object_t *obj)
377
{
378
    aout_instance_t *aout = (aout_instance_t *)obj;
379
    pa_operation *op;
380

381
382
    /* Sample format specification */
    struct pa_sample_spec ss;
Danny Wood's avatar
Danny Wood committed
383

384
    switch(aout->output.output.i_format)
Danny Wood's avatar
Danny Wood committed
385
    {
386
        case VLC_CODEC_F64B:
387
            aout->output.output.i_format = VLC_CODEC_F32B;
388
389
        case VLC_CODEC_F32B:
            ss.format = PA_SAMPLE_FLOAT32BE;
390
            break;
391
        case VLC_CODEC_F64L:
392
            aout->output.output.i_format = VLC_CODEC_F32L;
393
394
        case VLC_CODEC_F32L:
            ss.format = PA_SAMPLE_FLOAT32LE;
395
            break;
396
        case VLC_CODEC_FI32:
397
            aout->output.output.i_format = VLC_CODEC_FL32;
398
            ss.format = PA_SAMPLE_FLOAT32NE;
399
            break;
400
401
        case VLC_CODEC_S32B:
            ss.format = PA_SAMPLE_S32BE;
402
            break;
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
        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:
419
            aout->output.output.i_format = VLC_CODEC_U8;
420
421
        case VLC_CODEC_U8:
            ss.format = PA_SAMPLE_U8;
422
423
            break;
        default:
424
425
            if (HAVE_FPU)
            {
426
                aout->output.output.i_format = VLC_CODEC_FL32;
427
428
429
430
                ss.format = PA_SAMPLE_FLOAT32NE;
            }
            else
            {
431
                aout->output.output.i_format = VLC_CODEC_S16N;
432
433
434
                ss.format = PA_SAMPLE_S16NE;
            }
            break;
Danny Wood's avatar
Danny Wood committed
435
436
    }

437
438
    ss.rate = aout->output.output.i_rate;
    ss.channels = aout_FormatNbChannels(&aout->output.output);
439
    if (!pa_sample_spec_valid(&ss)) {
440
        msg_Err(aout, "unsupported sample specification");
441
        return VLC_EGENERIC;
442
    }
443

444
    /* Channel mapping (order defined in vlc_aout.h) */
445
446
447
    struct pa_channel_map map;
    map.channels = 0;

448
    if (aout->output.output.i_physical_channels & AOUT_CHAN_LEFT)
449
        map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_LEFT;
450
    if (aout->output.output.i_physical_channels & AOUT_CHAN_RIGHT)
451
        map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_RIGHT;
452
    if (aout->output.output.i_physical_channels & AOUT_CHAN_MIDDLELEFT)
453
        map.map[map.channels++] = PA_CHANNEL_POSITION_SIDE_LEFT;
454
    if (aout->output.output.i_physical_channels & AOUT_CHAN_MIDDLERIGHT)
455
        map.map[map.channels++] = PA_CHANNEL_POSITION_SIDE_RIGHT;
456
457
458
459
460
461
462
463
464
465
466
467
468
    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;
    }
469
    if (aout->output.output.i_physical_channels & AOUT_CHAN_LFE)
470
471
472
473
        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;
474
        msg_Warn(aout, "mapping channel %"PRIu8" to AUX%u", map.channels, i);
475
    }
476

477
    if (!pa_channel_map_valid(&map)) {
478
        msg_Err(aout, "unsupported channel map");
479
480
481
        return VLC_EGENERIC;
    } else {
        const char *name = pa_channel_map_to_pretty_name(&map);
482
        msg_Dbg(aout, "using %s channel map", (name != NULL) ? name : "?");
483
    }
484

485
    /* Stream parameters */
486
487
    const pa_stream_flags_t flags = PA_STREAM_INTERPOLATE_TIMING
                                  | PA_STREAM_AUTO_TIMING_UPDATE
488
489
                                  | PA_STREAM_ADJUST_LATENCY
                                  | PA_STREAM_START_CORKED;
490

491
    const uint32_t byterate = pa_bytes_per_second(&ss);
492
    struct pa_buffer_attr attr;
493
    /* no point in larger buffers on PA side than VLC */
494
495
    attr.maxlength = -1;
    attr.tlength = byterate * AOUT_MAX_ADVANCE_TIME / CLOCK_FREQ;
496
    attr.prebuf = byterate * AOUT_MAX_PREPARE_TIME / CLOCK_FREQ;
497
498
    attr.minreq = -1;
    attr.fragsize = 0; /* not used for output */
499

500
    /* Allocate structures */
501
    aout_sys_t *sys = malloc(sizeof(*sys));
502
503
    if (unlikely(sys == NULL))
        return VLC_ENOMEM;
504
    aout->output.p_sys = sys;
505
    sys->context = NULL;
506
    sys->stream = NULL;
507
    //sys->byterate = byterate;
508

509
    /* Channel volume */
510
    sys->base_volume = PA_VOLUME_NORM;
511
512
    pa_cvolume_set(&sys->cvolume, ss.channels, PA_VOLUME_NORM);

513
514
515
    /* Allocate threaded main loop */
    pa_threaded_mainloop *mainloop = pa_threaded_mainloop_new();
    if (unlikely(mainloop == NULL)) {
516
517
        free(sys);
        return VLC_ENOMEM;
518
    }
519
    sys->mainloop = mainloop;
520

521
522
    if (pa_threaded_mainloop_start(mainloop) < 0) {
        pa_threaded_mainloop_free(mainloop);
523
524
525
        free(sys);
        return VLC_ENOMEM;
    }
526
527
528
529
530
531
532
533
    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))
534
        goto fail;
535
    sys->context = ctx;
536

537
538
539
540
    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);
541
        goto fail;
542
543
    }

544
545
546
547
548
    /* 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;
549
    }
550
551
552
    sys->stream = s;
    pa_stream_set_state_callback(s, stream_state_cb, mainloop);
    pa_stream_set_moved_callback(s, stream_moved_cb, aout);
553
554
555
556
    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);
557

558
    if (pa_stream_connect_playback(s, NULL, &attr, flags, NULL, NULL) < 0
559
560
561
     || stream_wait(mainloop, s)) {
        error(aout, "cannot connect stream", ctx);
        goto fail;
562
563
    }

564
565
    const struct pa_buffer_attr *pba = pa_stream_get_buffer_attr(s);
    msg_Dbg(aout, "using buffer metrics: maxlength=%u, tlength=%u, "
566
567
            "prebuf=%u, minreq=%u",
            pba->maxlength, pba->tlength, pba->prebuf, pba->minreq);
568

569
    aout->output.i_nb_samples = pba->minreq / pa_frame_size(&ss);
570
571
572
573
574
575
576
577
578
579

    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);
580
    stream_moved_cb(s, aout);
581
582
583
    pa_threaded_mainloop_unlock(mainloop);

    aout->output.pf_play = Play;
584
    aout->output.pf_volume_set = VolumeSet;
585
586
587
    return VLC_SUCCESS;

fail:
588
589
    pa_threaded_mainloop_unlock(mainloop);
    Close(obj);
590
591
592
593
594
595
    return VLC_EGENERIC;
}

/*****************************************************************************
 * Close: close the audio device
 *****************************************************************************/
596
static void Close (vlc_object_t *obj)
597
{
598
    aout_instance_t *aout = (aout_instance_t *)obj;
599
600
601
602
603
    aout_sys_t *sys = aout->output.p_sys;
    pa_threaded_mainloop *mainloop = sys->mainloop;
    pa_context *ctx = sys->context;
    pa_stream *s = sys->stream;

604
605
606
607
608
609
    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");
    }

610
611
612
613
614
615
616
617
618
619
620
621
    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);
622
    }
623
624
625
626
    if (ctx != NULL)
        pa_context_unref(ctx);
    pa_threaded_mainloop_unlock(mainloop);
    pa_threaded_mainloop_free(mainloop);
627
    free(sys);
628
}