pulse.c 22.8 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

#include <pulse/pulseaudio.h>
34
#include <vlc_pulse.h>
35
36
37
38

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
51
52
53
54
55
/* TODO:
 * - pause input on policy event
 * - resample to compensate for long term drift
 * - select music or video stream property correctly (?)
 * - set further appropriate stream properties
 * - update output devices list dynamically
 */
56

57
58
/* NOTE:
 * Be careful what you do when the PulseAudio mainloop is held, which is to say
59
 * within PulseAudio callbacks, or after vlc_pa_lock().
60
61
 * In particular, a VLC variable callback cannot be triggered nor deleted with
 * the PulseAudio mainloop lock held, if the callback acquires the lock. */
62

63
64
65
66
struct aout_sys_t
{
    pa_stream *stream; /**< PulseAudio playback stream object */
    pa_context *context; /**< PulseAudio connection context */
67
68
    pa_volume_t base_volume; /**< 0dB reference volume */
    pa_cvolume cvolume; /**< actual sink input volume */
69
70
71
    mtime_t pts; /**< Play time of buffer write offset */
    mtime_t desync; /**< Measured desynchronization */
    unsigned rate; /**< Current stream sample rate */
72
73
};

74
/*** Sink ***/
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
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);
}

92
93
94
95
96
97
98
99
100
101
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
102
103
104
105
106
    /* 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. */
107
108
109
110
111
112
113
    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));
}

114
/*** Stream helpers ***/
115
116
117
118
119
120
121
122
123
124
125
126
127
128
static void stream_reset_sync(pa_stream *s, aout_instance_t *aout)
{
    aout_sys_t *sys = aout->output.p_sys;
    const unsigned rate = aout->output.output.i_rate;

    sys->pts = VLC_TS_INVALID;
    sys->desync = 0;
    pa_operation *op = pa_stream_update_sample_rate(s, rate, NULL, NULL);
    if (unlikely(op == NULL))
        return;
    pa_operation_unref(op);
    sys->rate = rate;
}

129
130
131
132
133
134
static void stream_state_cb(pa_stream *s, void *userdata)
{
    switch (pa_stream_get_state(s)) {
        case PA_STREAM_READY:
        case PA_STREAM_FAILED:
        case PA_STREAM_TERMINATED:
135
            vlc_pa_signal(0);
136
137
138
        default:
            break;
    }
139
    (void) userdata;
140
141
}

142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/* Latency management and lip synchronization */
static void stream_latency_cb(pa_stream *s, void *userdata)
{
    aout_instance_t *aout = userdata;
    aout_sys_t *sys = aout->output.p_sys;
    mtime_t delta, change;

    if (sys->pts == VLC_TS_INVALID)
    {
        msg_Dbg(aout, "missing latency from input");
        return;
    }

    /* Compute lip desynchronization */
    {
        pa_usec_t latency;
        int negative;

        if (pa_stream_get_latency(s, &latency, &negative)) {
            vlc_pa_error(aout, "missing latency", sys->context);
            return;
        }
        delta = sys->pts - mdate();
        if (unlikely(negative))
           delta += latency;
        else
           delta -= latency;
    }

    change = delta - sys->desync;
    sys->desync = delta;
    //msg_Dbg(aout, "desync: %+"PRId64" us (variation: %+"PRId64" us)",
    //        delta, change);

    /* Compute playback sample rate */
    const unsigned inrate = aout->output.output.i_rate;
    /* NOTE: AOUT_MAX_RESAMPLING (10%) is way too high... */
    const int limit = inrate >> 6;

181
    if (delta < -AOUT_MAX_PTS_DELAY) {
182
183
184
        msg_Warn(aout, "too late by %"PRId64" us", -delta);
        if (change < 0)
            delta += change; /* be more severe if really out of sync */
185
    } else if (delta > +AOUT_MAX_PTS_ADVANCE) {
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
        msg_Warn(aout, "too early by %"PRId64" us", delta);
        if (change > 0)
            delta += change;
    }

    /* This is empirical. Feel free to define something smarter. */
    int outrate = inrate * delta / -(CLOCK_FREQ * 10);
    if (16 * abs(outrate) < limit)
        outrate = inrate; /* favor native rate to avoid resampling */
    else if (outrate > limit)
        outrate = inrate + limit;
    else if (outrate < -limit)
        outrate = inrate - limit;
    else
        outrate += inrate;

    /* Apply adjusted sample rate */
    if (outrate == (int)sys->rate)
        return;
    pa_operation *op = pa_stream_update_sample_rate(s, outrate, NULL, NULL);
    if (unlikely(op == NULL)) {
        vlc_pa_error(aout, "cannot change sample rate", sys->context);
        return;
    }
    pa_operation_unref(op);
    msg_Dbg(aout, "changed sample rate to %d Hz", outrate);
    sys->rate = outrate;
}

215
216
static void stream_moved_cb(pa_stream *s, void *userdata)
{
217
218
219
220
    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);
221

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
222
223
224
225
    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);
226
227
    if (likely(op != NULL))
        pa_operation_unref(op);
228
229
230
231

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

234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
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");
255
    stream_reset_sync(s, aout);
256
257
258
259
260
}

static void stream_underflow_cb(pa_stream *s, void *userdata)
{
    aout_instance_t *aout = userdata;
261
    pa_operation *op;
262

263
264
265
266
    msg_Warn(aout, "underflow");
    op = pa_stream_cork(s, 1, NULL, NULL);
    if (op != NULL)
        pa_operation_unref(op);
267
    stream_reset_sync(s, aout);
268
269
}

270
static int stream_wait(pa_stream *stream)
271
272
273
274
275
276
{
    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;
277
        vlc_pa_wait();
278
279
280
281
    }
    return 0;
}

282
283
284
285
286
287
288
289
290
291
#ifdef LIBPULSE_GETS_A_CLUE
static void stream_success_cb(pa_stream *s, int success, void *userdata)
{
    vlc_pa_signal(0);
    (void) s; (void) success; (void) userdata;
}
#else
# define stream_success_cb NULL
#endif

292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
/* 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;
}

316
317
318
/**
 * Queue one audio frame to the playabck stream
 */
319
320
321
322
323
static void Play(aout_instance_t *aout)
{
    aout_sys_t *sys = aout->output.p_sys;
    pa_stream *s = sys->stream;

324
325
326
327
328
329
330
331
332
333
334
    /* This function is called exactly once per block in the output FIFO. */
    block_t *block = aout_FifoPop(&aout->output.fifo);
    assert (block != NULL);

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

    size_t len = block->i_buffer;
    mtime_t pts = block->i_pts + block->i_length;

335
336
337
338
339
    /* 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. */
340
    vlc_pa_lock();
341

342
    if (pa_stream_is_corked(s) > 0) {
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
        /* Start or resume the stream. Zeroes are prepended to sync.
         * This does not really work because PulseAudio latency measurement is
         * garbage at start. */
        pa_operation *op;
        pa_usec_t latency;
        int negative;

        if (pa_stream_get_latency(s, &latency, &negative) == 0)
            msg_Dbg(aout, "starting with %c%"PRIu64" us latency",
                    negative ? '-' : '+', latency);
        else
            latency = negative = 0;

        mtime_t advance = block->i_pts - mdate();
        if (negative)
            advance += latency;
        else
            advance -= latency;

        if (advance > 0) {
            size_t nb = (advance * aout->output.output.i_rate) / CLOCK_FREQ;
            size_t size = aout->output.output.i_bytes_per_frame;
            float *zeroes = calloc (nb, size);

            msg_Dbg(aout, "prepending %zu zeroes", nb);
            if (likely(zeroes != NULL))
369
#if 1 /* Fault injection: remove this to be too early */
370
371
                if (pa_stream_write(s, zeroes, nb * size, free, 0,
                                    PA_SEEK_RELATIVE) < 0)
372
#endif
373
374
375
376
377
378
379
                    free(zeroes);
        }

        op = pa_stream_cork(s, 0, NULL, NULL);
        if (op != NULL)
            pa_operation_unref(op);
        op = pa_stream_trigger(s, NULL, NULL);
380
381
382
383
384
        if (op != NULL)
            pa_operation_unref(op);
        msg_Dbg(aout, "uncorking");
    }

385
#if 0 /* Fault injector to be too late / test underrun recovery */
386
387
388
    static unsigned u = 0;
    if ((++u % 500) == 0) {
        msg_Err(aout, "fault injection");
389
        pa_operation_unref(pa_stream_flush(s, NULL, NULL));
390
391
392
    }
#endif

393
    if (pa_stream_write(s, ptr, len, data_free, 0, PA_SEEK_RELATIVE) < 0) {
394
        vlc_pa_error(aout, "cannot write", sys->context);
395
        block_Release(block);
396
    }
397
    sys->pts = pts;
398

399
    vlc_pa_unlock();
400
401
}

402
403
404
/**
 * Cork or uncork the playback stream
 */
405
406
407
408
409
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;

410
411
412
    if (!b_paused)
        return; /* nothing to do - yet */

413
    vlc_pa_lock();
414

415
    pa_operation *op = pa_stream_cork(s, 1, NULL, NULL);
416
417
    if (op != NULL)
        pa_operation_unref(op);
418
    stream_reset_sync(s, aout);
419

420
    vlc_pa_unlock();
421
    (void) i_date;
422
423
}

424
static int VolumeSet(aout_instance_t *aout, float vol, bool mute)
425
426
427
428
429
{
    aout_sys_t *sys = aout->output.p_sys;
    pa_operation *op;

    uint32_t idx = pa_stream_get_index(sys->stream);
430
    pa_volume_t volume = pa_sw_volume_from_linear(vol);
431
    pa_cvolume cvolume;
432

433
434
    /* TODO: do not ruin the channel balance (if set outside VLC) */
    /* TODO: notify UI about volume changes by other PulseAudio clients */
435
    pa_cvolume_set(&sys->cvolume, sys->cvolume.channels, volume);
436
437
    pa_sw_cvolume_multiply_scalar(&cvolume, &sys->cvolume, sys->base_volume);
    assert(pa_cvolume_valid(&cvolume));
438

439
    vlc_pa_lock();
440
    op = pa_context_set_sink_input_volume(sys->context, idx, &cvolume, NULL, NULL);
441
442
    if (likely(op != NULL))
        pa_operation_unref(op);
443
    op = pa_context_set_sink_input_mute(sys->context, idx, mute, NULL, NULL);
444
445
    if (likely(op != NULL))
        pa_operation_unref(op);
446
    vlc_pa_unlock();
447
448
449

    return 0;
}
450

451
452
453
454
455
456
457
458
459
460
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;

461
462
    (void) varname; (void) old;

463
    vlc_pa_lock();
464
465
    op = pa_context_move_sink_input_by_index(sys->context, idx, sink_idx,
                                             NULL, NULL);
466
467
468
469
    if (likely(op != NULL)) {
        pa_operation_unref(op);
        msg_Dbg(aout, "moving to sink %"PRIu32, sink_idx);
    } else
470
471
        vlc_pa_error(obj, "cannot move sink", sys->context);
    vlc_pa_unlock();
472
473

    return (op != NULL) ? VLC_SUCCESS : VLC_EGENERIC;
474
475
476
}


477
478
479
/**
 * Create a PulseAudio playback stream, a.k.a. a sink input.
 */
480
static int Open(vlc_object_t *obj)
481
{
482
    aout_instance_t *aout = (aout_instance_t *)obj;
483
    pa_operation *op;
484

485
486
    /* Sample format specification */
    struct pa_sample_spec ss;
Danny Wood's avatar
Danny Wood committed
487

488
    switch(aout->output.output.i_format)
Danny Wood's avatar
Danny Wood committed
489
    {
490
        case VLC_CODEC_F64B:
491
            aout->output.output.i_format = VLC_CODEC_F32B;
492
493
        case VLC_CODEC_F32B:
            ss.format = PA_SAMPLE_FLOAT32BE;
494
            break;
495
        case VLC_CODEC_F64L:
496
            aout->output.output.i_format = VLC_CODEC_F32L;
497
498
        case VLC_CODEC_F32L:
            ss.format = PA_SAMPLE_FLOAT32LE;
499
            break;
500
        case VLC_CODEC_FI32:
501
            aout->output.output.i_format = VLC_CODEC_FL32;
502
            ss.format = PA_SAMPLE_FLOAT32NE;
503
            break;
504
505
        case VLC_CODEC_S32B:
            ss.format = PA_SAMPLE_S32BE;
506
            break;
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
        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:
523
            aout->output.output.i_format = VLC_CODEC_U8;
524
525
        case VLC_CODEC_U8:
            ss.format = PA_SAMPLE_U8;
526
527
            break;
        default:
528
529
            if (HAVE_FPU)
            {
530
                aout->output.output.i_format = VLC_CODEC_FL32;
531
532
533
534
                ss.format = PA_SAMPLE_FLOAT32NE;
            }
            else
            {
535
                aout->output.output.i_format = VLC_CODEC_S16N;
536
537
538
                ss.format = PA_SAMPLE_S16NE;
            }
            break;
Danny Wood's avatar
Danny Wood committed
539
540
    }

541
542
    ss.rate = aout->output.output.i_rate;
    ss.channels = aout_FormatNbChannels(&aout->output.output);
543
    if (!pa_sample_spec_valid(&ss)) {
544
        msg_Err(aout, "unsupported sample specification");
545
        return VLC_EGENERIC;
546
    }
547

548
    /* Channel mapping (order defined in vlc_aout.h) */
549
550
551
    struct pa_channel_map map;
    map.channels = 0;

552
    if (aout->output.output.i_physical_channels & AOUT_CHAN_LEFT)
553
        map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_LEFT;
554
    if (aout->output.output.i_physical_channels & AOUT_CHAN_RIGHT)
555
        map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_RIGHT;
556
    if (aout->output.output.i_physical_channels & AOUT_CHAN_MIDDLELEFT)
557
        map.map[map.channels++] = PA_CHANNEL_POSITION_SIDE_LEFT;
558
    if (aout->output.output.i_physical_channels & AOUT_CHAN_MIDDLERIGHT)
559
        map.map[map.channels++] = PA_CHANNEL_POSITION_SIDE_RIGHT;
560
561
562
563
564
565
566
567
568
569
570
571
572
    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;
    }
573
    if (aout->output.output.i_physical_channels & AOUT_CHAN_LFE)
574
575
576
577
        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;
578
        msg_Warn(aout, "mapping channel %"PRIu8" to AUX%u", map.channels, i);
579
    }
580

581
    if (!pa_channel_map_valid(&map)) {
582
        msg_Err(aout, "unsupported channel map");
583
584
        return VLC_EGENERIC;
    } else {
585
        const char *name = pa_channel_map_to_name(&map);
586
        msg_Dbg(aout, "using %s channel map", (name != NULL) ? name : "?");
587
    }
588

589
    /* Stream parameters */
590
591
    const pa_stream_flags_t flags = PA_STREAM_START_CORKED
                                  //| PA_STREAM_INTERPOLATE_TIMING
592
593
                                  | PA_STREAM_AUTO_TIMING_UPDATE
                                  | PA_STREAM_VARIABLE_RATE;
594

595
    const uint32_t byterate = pa_bytes_per_second(&ss);
596
    struct pa_buffer_attr attr;
597
    attr.maxlength = -1;
598
599
600
601
602
603
604
    /* PulseAudio assumes that tlength bytes are available in the buffer. Thus
     * we need to be conservative and set the minimum value that the VLC
     * audio decoder thread warrants. Otherwise, PulseAudio buffers will
     * underrun on hardware with large buffers. VLC keeps at least
     * AOUT_MIN_PREPARE and at most AOUT_MAX_PREPARE worth of audio buffers.
     * TODO? tlength could be adaptively increased to reduce wakeups. */
    attr.tlength = byterate * AOUT_MIN_PREPARE_TIME / CLOCK_FREQ;
605
    attr.prebuf = 0; /* trigger manually */
606
607
    attr.minreq = -1;
    attr.fragsize = 0; /* not used for output */
608

609
    /* Allocate structures */
610
    aout_sys_t *sys = malloc(sizeof(*sys));
611
612
    if (unlikely(sys == NULL))
        return VLC_ENOMEM;
613
614
615
616
617
618
619
620

    pa_context *ctx = vlc_pa_connect (obj);
    if (ctx == NULL)
    {
        free (sys);
        return VLC_EGENERIC;
    }

621
    aout->output.p_sys = sys;
622
    sys->stream = NULL;
623
    sys->context = ctx;
624
625
626
    sys->pts = VLC_TS_INVALID;
    sys->desync = 0;
    sys->rate = ss.rate;
627

628
    /* Channel volume */
629
    sys->base_volume = PA_VOLUME_NORM;
630
631
    pa_cvolume_set(&sys->cvolume, ss.channels, PA_VOLUME_NORM);

632
    vlc_pa_lock();
633
634
635
    /* Create a playback stream */
    pa_stream *s = pa_stream_new(ctx, "audio stream", &ss, &map);
    if (s == NULL) {
636
        vlc_pa_error(obj, "stream creation failure", ctx);
637
        goto fail;
638
    }
639
    sys->stream = s;
640
    pa_stream_set_state_callback(s, stream_state_cb, NULL);
641
    pa_stream_set_latency_update_callback(s, stream_latency_cb, aout);
642
    pa_stream_set_moved_callback(s, stream_moved_cb, aout);
643
644
645
646
    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);
647

648
    if (pa_stream_connect_playback(s, NULL, &attr, flags, NULL, NULL) < 0
649
650
     || stream_wait(s)) {
        vlc_pa_error(obj, "stream connection failure", ctx);
651
        goto fail;
652
653
    }

654
655
    const struct pa_buffer_attr *pba = pa_stream_get_buffer_attr(s);
    msg_Dbg(aout, "using buffer metrics: maxlength=%u, tlength=%u, "
656
657
            "prebuf=%u, minreq=%u",
            pba->maxlength, pba->tlength, pba->prebuf, pba->minreq);
658

659
    aout->output.i_nb_samples = pba->minreq / pa_frame_size(&ss);
660
661
662
663
664
665
666
667
668
669

    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);
670
    stream_moved_cb(s, aout);
671
    vlc_pa_unlock();
672
673

    aout->output.pf_play = Play;
674
    aout->output.pf_pause = Pause;
675
    aout->output.pf_volume_set = VolumeSet;
676
677
678
    return VLC_SUCCESS;

fail:
679
    vlc_pa_unlock();
680
    Close(obj);
681
682
683
    return VLC_EGENERIC;
}

684
685
686
/**
 * Removes a PulseAudio playback stream
 */
687
static void Close (vlc_object_t *obj)
688
{
689
    aout_instance_t *aout = (aout_instance_t *)obj;
690
691
692
693
    aout_sys_t *sys = aout->output.p_sys;
    pa_context *ctx = sys->context;
    pa_stream *s = sys->stream;

694
695
696
697
698
699
    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");
    }

700
    vlc_pa_lock();
701
702
703
    if (s != NULL) {
        pa_operation *op;

704
705
706
707
708
709
710
711
712
713
714
        if (pa_stream_is_corked(s) > 0)
            /* Stream paused: discard all buffers */
            op = pa_stream_flush(s, stream_success_cb, NULL);
        else
            /* Stream playing: wait until buffers are played */
            op = pa_stream_drain(s, stream_success_cb, NULL);
        if (likely(op != NULL)) {
#ifdef LIBPULSE_GETS_A_CLUE
            while (pa_operation_get_state(op) == PA_OPERATION_RUNNING)
                vlc_pa_wait();
#endif
715
            pa_operation_unref(op);
716
717
        }

718
719
        pa_stream_disconnect(s);
        pa_stream_unref(s);
720
    }
721
722
723
    vlc_pa_unlock();

    vlc_pa_disconnect(obj, ctx);
724
    free(sys);
725
}