pulse.c 25.7 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
    mtime_t paused; /**< Time when (last) paused */
70 71 72
    mtime_t pts; /**< Play time of buffer write offset */
    mtime_t desync; /**< Measured desynchronization */
    unsigned rate; /**< Current stream sample rate */
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 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
static void sink_input_info_cb(pa_context *, const pa_sink_input_info *,
                               int, void *);

/*** Context ***/
static void context_cb(pa_context *ctx, pa_subscription_event_type_t type,
                       uint32_t idx, void *userdata)
{
    audio_output_t *aout = userdata;
    aout_sys_t *sys = aout->sys;
    pa_operation *op;

    switch (type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK)
    {
      case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
        if (idx != pa_stream_get_index(sys->stream))
            break; /* only interested in our sink input */

        /* Gee... PA will not provide the infos directly in the event. */
        switch (type & PA_SUBSCRIPTION_EVENT_TYPE_MASK)
        {
          case PA_SUBSCRIPTION_EVENT_REMOVE:
            msg_Err(aout, "sink input killed!");
            break;

          default:
            op = pa_context_get_sink_input_info(ctx, idx, sink_input_info_cb,
                                                aout);
            if (likely(op != NULL))
                pa_operation_unref(op);
            break;
        }
        break;

      default: /* unsubscribed facility?! */
        assert(0);
    }
}


114
/*** Sink ***/
115 116 117
static void sink_list_cb(pa_context *c, const pa_sink_info *i, int eol,
                         void *userdata)
{
118
    audio_output_t *aout = userdata;
119 120 121 122 123 124 125 126 127 128 129 130 131
    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);
}

132 133 134
static void sink_info_cb(pa_context *c, const pa_sink_info *i, int eol,
                         void *userdata)
{
135
    audio_output_t *aout = userdata;
136
    aout_sys_t *sys = aout->sys;
137 138 139 140 141

    if (eol)
        return;
    (void) c;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
142 143 144 145 146
    /* 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. */
147 148 149 150
    if (i->flags & PA_SINK_FLAT_VOLUME)
        sys->base_volume = i->base_volume;
    else
        sys->base_volume = PA_VOLUME_NORM;
151
    msg_Dbg(aout, "base volume: %"PRIu32, sys->base_volume);
152 153
}

154 155 156 157 158 159 160 161 162 163 164 165 166 167 168

/*** Latency management and lip synchronization ***/
static mtime_t vlc_pa_get_latency(audio_output_t *aout,
                                  pa_context *ctx, pa_stream *s)
{
    pa_usec_t latency;
    int negative;

    if (pa_stream_get_latency(s, &latency, &negative)) {
        vlc_pa_error(aout, "unknown latency", ctx);
        return VLC_TS_INVALID;
    }
    return negative ? -latency : +latency;
}

169
static void stream_reset_sync(pa_stream *s, audio_output_t *aout)
170
{
171 172
    aout_sys_t *sys = aout->sys;
    const unsigned rate = aout->format.i_rate;
173 174 175 176 177 178 179 180 181 182

    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;
}

183 184 185 186 187 188 189
/**
 * Starts or resumes the playback stream.
 * Tries start playing back audio samples at the most accurate time
 * in order to minimize desync and resampling during early playback.
 * @note PulseAudio lock required.
 */
static void stream_resync(audio_output_t *aout, pa_stream *s)
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 215 216 217 218 219 220 221 222 223 224 225 226 227 228
    aout_sys_t *sys = aout->sys;
    pa_operation *op;
    mtime_t delta;

    assert (pa_stream_is_corked(s) > 0);
    assert (sys->pts != VLC_TS_INVALID);

    delta = vlc_pa_get_latency(aout, sys->context, s);
    if (unlikely(delta == VLC_TS_INVALID))
        delta = 0; /* screwed */

    delta = (sys->pts - mdate()) - delta;

    /* TODO: adjust prebuf instead of padding? */
    if (delta > 0) {
        size_t nb = (delta * sys->rate) / CLOCK_FREQ;
        size_t size = aout->format.i_bytes_per_frame;
        float *zeroes = calloc (nb, size);

        msg_Dbg(aout, "starting with %zu zeroes (%"PRId64" us)", nb,
                delta);
#if 0 /* Fault injector: add delay */
        pa_stream_write(s, zeroes, nb * size, NULL, 0, PA_SEEK_RELATIVE);
        pa_stream_write(s, zeroes, nb * size, NULL, 0, PA_SEEK_RELATIVE);
#endif
        if (likely(zeroes != NULL))
            if (pa_stream_write(s, zeroes, nb * size, free, 0,
                                PA_SEEK_RELATIVE) < 0)
                free(zeroes);
    } else
        msg_Warn(aout, "starting late (%"PRId64" us)", delta);

    op = pa_stream_cork(s, 0, NULL, NULL);
    if (op != NULL)
        pa_operation_unref(op);
    op = pa_stream_trigger(s, NULL, NULL);
    if (op != NULL)
        pa_operation_unref(op);
229 230
}

231 232
static void stream_latency_cb(pa_stream *s, void *userdata)
{
233
    audio_output_t *aout = userdata;
234
    aout_sys_t *sys = aout->sys;
235 236
    mtime_t delta, change;

237 238
    if (pa_stream_is_corked(s))
        return;
239 240 241 242 243 244 245
    if (sys->pts == VLC_TS_INVALID)
    {
        msg_Dbg(aout, "missing latency from input");
        return;
    }

    /* Compute lip desynchronization */
246 247 248
    delta = vlc_pa_get_latency(aout, sys->context, s);
    if (delta == VLC_TS_INVALID)
        return;
249

250
    delta = (sys->pts - mdate()) - delta;
251 252 253 254 255
    change = delta - sys->desync;
    sys->desync = delta;
    //msg_Dbg(aout, "desync: %+"PRId64" us (variation: %+"PRId64" us)",
    //        delta, change);

256 257 258 259
    const unsigned inrate = aout->format.i_rate;
    unsigned outrate = sys->rate;
    bool sync = false;

260
    if (delta < -AOUT_MAX_PTS_DELAY)
261
        msg_Warn(aout, "too late by %"PRId64" us", -delta);
262
    else if (delta > +AOUT_MAX_PTS_ADVANCE)
263
        msg_Warn(aout, "too early by %"PRId64" us", delta);
264 265 266 267
    else if (outrate  == inrate)
        return; /* In sync, do not add unnecessary disturbance! */
    else
        sync = true;
268

269
    /* Compute playback sample rate */
270
    /* This is empirical. Feel free to define something smarter. */
271 272 273 274 275 276 277 278 279
    int adj = sync ? (outrate - inrate)
                   : outrate * (delta + change) / (CLOCK_FREQ << 4);
    /* This avoids too quick rate variation. It sounds really bad and
     * causes unstability (e.g. oscillation around the correct rate). */
    int limit = inrate >> 10;
    /* However, to improve stability and try to converge, closing to the
     * nominal rate is favored over drifting from it. */
    if ((adj > 0) == (sys->rate > inrate))
        limit *= 2;
280 281 282 283
    if (adj > +limit)
        adj = +limit;
    if (adj < -limit)
        adj = -limit;
284
    outrate -= adj;
285 286 287

    /* This keeps the effective rate within specified range
     * (+/-AOUT_MAX_RESAMPLING% - see <vlc_aout.h>) of the nominal rate. */
288
    limit = inrate * AOUT_MAX_RESAMPLING / 100;
289
    if (outrate > inrate + limit)
290
        outrate = inrate + limit;
291
    if (outrate < inrate - limit)
292 293 294
        outrate = inrate - limit;

    /* Apply adjusted sample rate */
295
    if (outrate == sys->rate)
296 297 298 299 300 301 302
        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);
303
    msg_Dbg(aout, "changed sample rate to %u Hz",outrate);
304 305 306
    sys->rate = outrate;
}

307 308 309 310 311 312 313 314 315 316 317 318 319 320 321

/*** Stream helpers ***/
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:
            vlc_pa_signal(0);
        default:
            break;
    }
    (void) userdata;
}

322 323
static void stream_moved_cb(pa_stream *s, void *userdata)
{
324
    audio_output_t *aout = userdata;
325
    aout_sys_t *sys = aout->sys;
326 327
    pa_operation *op;
    uint32_t idx = pa_stream_get_device_index(s);
328

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
329 330 331 332
    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);
333 334
    if (likely(op != NULL))
        pa_operation_unref(op);
335 336 337 338

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

341 342
static void stream_overflow_cb(pa_stream *s, void *userdata)
{
343
    audio_output_t *aout = userdata;
344 345 346 347 348 349 350

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

static void stream_started_cb(pa_stream *s, void *userdata)
{
351
    audio_output_t *aout = userdata;
352 353 354 355 356 357 358

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

static void stream_suspended_cb(pa_stream *s, void *userdata)
{
359
    audio_output_t *aout = userdata;
360 361

    msg_Dbg(aout, "suspended");
362
    stream_reset_sync(s, aout);
363 364 365 366
}

static void stream_underflow_cb(pa_stream *s, void *userdata)
{
367
    audio_output_t *aout = userdata;
368
    pa_operation *op;
369

370 371 372 373
    msg_Warn(aout, "underflow");
    op = pa_stream_cork(s, 1, NULL, NULL);
    if (op != NULL)
        pa_operation_unref(op);
374
    stream_reset_sync(s, aout);
375 376
}

377
static int stream_wait(pa_stream *stream)
378 379 380 381 382 383
{
    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;
384
        vlc_pa_wait();
385 386 387 388
    }
    return 0;
}

389 390 391 392 393 394 395 396 397 398
#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

399 400 401 402 403 404

/*** Sink input ***/
static void sink_input_info_cb(pa_context *ctx, const pa_sink_input_info *i,
                               int eol, void *userdata)
{
    audio_output_t *aout = userdata;
405 406
    aout_sys_t *sys = aout->sys;
    float volume;
407 408 409

    if (eol)
        return;
410
    (void) ctx;
411

412
    sys->cvolume = i->volume;
413
    volume = pa_cvolume_max(&i->volume) / (float)PA_VOLUME_NORM;
414 415 416 417 418 419
    aout_VolumeHardSet(aout, volume, i->mute);
}


/*** VLC audio output callbacks ***/

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
/* 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;
}

444 445 446
/**
 * Queue one audio frame to the playabck stream
 */
447
static void Play(audio_output_t *aout)
448
{
449
    aout_sys_t *sys = aout->sys;
450 451
    pa_stream *s = sys->stream;

452
    /* This function is called exactly once per block in the output FIFO. */
453
    block_t *block = aout_FifoPop(&aout->fifo);
454 455 456 457 458 459 460 461 462
    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;

463 464 465 466 467
    /* 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. */
468
    vlc_pa_lock();
469

470 471 472
    sys->pts = pts;
    if (pa_stream_is_corked(s) > 0)
        stream_resync(aout, s);
473

474
#if 0 /* Fault injector to test underrun recovery */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
475 476
    static volatile unsigned u = 0;
    if ((++u % 1000) == 0) {
477
        msg_Err(aout, "fault injection");
478
        pa_operation_unref(pa_stream_flush(s, NULL, NULL));
479 480 481
    }
#endif

482
    if (pa_stream_write(s, ptr, len, data_free, 0, PA_SEEK_RELATIVE) < 0) {
483
        vlc_pa_error(aout, "cannot write", sys->context);
484
        block_Release(block);
485 486
    }

487
    vlc_pa_unlock();
488 489
}

490 491 492
/**
 * Cork or uncork the playback stream
 */
493
static void Pause(audio_output_t *aout, bool paused, mtime_t date)
494
{
495
    aout_sys_t *sys = aout->sys;
496
    pa_stream *s = sys->stream;
497
    pa_operation *op;
498

499
    vlc_pa_lock();
500

501 502 503 504 505 506 507 508 509 510 511 512 513
    if (paused) {
        sys->paused = date;
        op = pa_stream_cork(s, paused, NULL, NULL);
        if (op != NULL)
            pa_operation_unref(op);
    } else {
        assert (sys->paused != VLC_TS_INVALID);
        date -= sys->paused;
        msg_Dbg(aout, "resuming after %"PRId64" us", date);
        sys->paused = VLC_TS_INVALID;
        sys->pts += date;
        stream_resync(aout, s);
    }
514

515
    vlc_pa_unlock();
516 517
}

518
static int VolumeSet(audio_output_t *aout, float vol, bool mute)
519
{
520
    aout_sys_t *sys = aout->sys;
521 522 523
    pa_operation *op;
    uint32_t idx = pa_stream_get_index(sys->stream);

524
    pa_cvolume cvolume = sys->cvolume;
525
    pa_volume_t volume = sys->base_volume;
526 527

    pa_cvolume_scale(&cvolume, PA_VOLUME_NORM); /* preserve balance */
528 529 530 531 532 533 534 535

    /* VLC provides the software volume so convert directly to PulseAudio
     * software volume, pa_volume_t. This is not a linear amplification factor
     * so do not use PulseAudio linear amplification! */
    vol *= PA_VOLUME_NORM;
    if (unlikely(vol >= PA_VOLUME_MAX))
        vol = PA_VOLUME_MAX;
    volume = pa_sw_volume_multiply(volume, lround(vol));
536
    pa_sw_cvolume_multiply_scalar(&cvolume, &cvolume, volume);
537

538
    assert(pa_cvolume_valid(&cvolume));
539

540
    vlc_pa_lock();
541
    op = pa_context_set_sink_input_volume(sys->context, idx, &cvolume, NULL, NULL);
542 543
    if (likely(op != NULL))
        pa_operation_unref(op);
544
    op = pa_context_set_sink_input_mute(sys->context, idx, mute, NULL, NULL);
545 546
    if (likely(op != NULL))
        pa_operation_unref(op);
547
    vlc_pa_unlock();
548 549 550

    return 0;
}
551

552 553 554
static int StreamMove(vlc_object_t *obj, const char *varname, vlc_value_t old,
                      vlc_value_t val, void *userdata)
{
555
    audio_output_t *aout = (audio_output_t *)obj;
556
    aout_sys_t *sys = aout->sys;
557 558 559 560 561
    pa_stream *s = userdata;
    pa_operation *op;
    uint32_t idx = pa_stream_get_index(s);
    uint32_t sink_idx = val.i_int;

562 563
    (void) varname; (void) old;

564
    vlc_pa_lock();
565 566
    op = pa_context_move_sink_input_by_index(sys->context, idx, sink_idx,
                                             NULL, NULL);
567 568 569 570
    if (likely(op != NULL)) {
        pa_operation_unref(op);
        msg_Dbg(aout, "moving to sink %"PRIu32, sink_idx);
    } else
571 572
        vlc_pa_error(obj, "cannot move sink", sys->context);
    vlc_pa_unlock();
573 574

    return (op != NULL) ? VLC_SUCCESS : VLC_EGENERIC;
575 576 577
}


578 579 580
/**
 * Create a PulseAudio playback stream, a.k.a. a sink input.
 */
581
static int Open(vlc_object_t *obj)
582
{
583
    audio_output_t *aout = (audio_output_t *)obj;
584
    pa_operation *op;
585

586 587
    /* Sample format specification */
    struct pa_sample_spec ss;
588
    vlc_fourcc_t format = aout->format.i_format;
Danny Wood's avatar
Danny Wood committed
589

590
    switch(format)
Danny Wood's avatar
Danny Wood committed
591
    {
592
        case VLC_CODEC_F64B:
593
            format = VLC_CODEC_F32B;
594 595
        case VLC_CODEC_F32B:
            ss.format = PA_SAMPLE_FLOAT32BE;
596
            break;
597
        case VLC_CODEC_F64L:
598
            format = VLC_CODEC_F32L;
599 600
        case VLC_CODEC_F32L:
            ss.format = PA_SAMPLE_FLOAT32LE;
601
            break;
602
        case VLC_CODEC_FI32:
603
            format = VLC_CODEC_FL32;
604
            ss.format = PA_SAMPLE_FLOAT32NE;
605
            break;
606 607
        case VLC_CODEC_S32B:
            ss.format = PA_SAMPLE_S32BE;
608
            break;
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
        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:
625
            format = VLC_CODEC_U8;
626 627
        case VLC_CODEC_U8:
            ss.format = PA_SAMPLE_U8;
628 629
            break;
        default:
630 631
            if (HAVE_FPU)
            {
632
                format = VLC_CODEC_FL32;
633 634 635 636
                ss.format = PA_SAMPLE_FLOAT32NE;
            }
            else
            {
637
                format = VLC_CODEC_S16N;
638 639 640
                ss.format = PA_SAMPLE_S16NE;
            }
            break;
Danny Wood's avatar
Danny Wood committed
641 642
    }

643 644
    ss.rate = aout->format.i_rate;
    ss.channels = aout_FormatNbChannels(&aout->format);
645
    if (!pa_sample_spec_valid(&ss)) {
646
        msg_Err(aout, "unsupported sample specification");
647
        return VLC_EGENERIC;
648
    }
649

650
    /* Channel mapping (order defined in vlc_aout.h) */
651 652 653
    struct pa_channel_map map;
    map.channels = 0;

654
    if (aout->format.i_physical_channels & AOUT_CHAN_LEFT)
655
        map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_LEFT;
656
    if (aout->format.i_physical_channels & AOUT_CHAN_RIGHT)
657
        map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_RIGHT;
658
    if (aout->format.i_physical_channels & AOUT_CHAN_MIDDLELEFT)
659
        map.map[map.channels++] = PA_CHANNEL_POSITION_SIDE_LEFT;
660
    if (aout->format.i_physical_channels & AOUT_CHAN_MIDDLERIGHT)
661
        map.map[map.channels++] = PA_CHANNEL_POSITION_SIDE_RIGHT;
662
    if (aout->format.i_physical_channels & AOUT_CHAN_REARLEFT)
663
        map.map[map.channels++] = PA_CHANNEL_POSITION_REAR_LEFT;
664
    if (aout->format.i_physical_channels & AOUT_CHAN_REARRIGHT)
665
        map.map[map.channels++] = PA_CHANNEL_POSITION_REAR_RIGHT;
666
    if (aout->format.i_physical_channels & AOUT_CHAN_REARCENTER)
667
        map.map[map.channels++] = PA_CHANNEL_POSITION_REAR_CENTER;
668
    if (aout->format.i_physical_channels & AOUT_CHAN_CENTER)
669 670 671 672 673 674
    {
        if (ss.channels == 1)
            map.map[map.channels++] = PA_CHANNEL_POSITION_MONO;
        else
            map.map[map.channels++] = PA_CHANNEL_POSITION_FRONT_CENTER;
    }
675
    if (aout->format.i_physical_channels & AOUT_CHAN_LFE)
676 677 678 679
        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;
680
        msg_Warn(aout, "mapping channel %"PRIu8" to AUX%u", map.channels, i);
681
    }
682

683
    if (!pa_channel_map_valid(&map)) {
684
        msg_Err(aout, "unsupported channel map");
685 686
        return VLC_EGENERIC;
    } else {
687
        const char *name = pa_channel_map_to_name(&map);
688
        msg_Dbg(aout, "using %s channel map", (name != NULL) ? name : "?");
689
    }
690

691
    /* Stream parameters */
692 693
    const pa_stream_flags_t flags = PA_STREAM_START_CORKED
                                  //| PA_STREAM_INTERPOLATE_TIMING
694 695
                                  | PA_STREAM_AUTO_TIMING_UPDATE
                                  | PA_STREAM_VARIABLE_RATE;
696 697

    struct pa_buffer_attr attr;
698
    attr.maxlength = -1;
699 700 701 702 703 704
    /* 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. */
705
    attr.tlength = pa_usec_to_bytes(AOUT_MIN_PREPARE_TIME, &ss);
706
    attr.prebuf = 0; /* trigger manually */
707 708
    attr.minreq = -1;
    attr.fragsize = 0; /* not used for output */
709

710
    /* Allocate structures */
711
    aout_sys_t *sys = malloc(sizeof(*sys));
712 713
    if (unlikely(sys == NULL))
        return VLC_ENOMEM;
714 715 716 717 718 719 720 721

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

722
    aout->sys = sys;
723
    sys->stream = NULL;
724
    sys->context = ctx;
725
    sys->paused = VLC_TS_INVALID;
726 727 728
    sys->pts = VLC_TS_INVALID;
    sys->desync = 0;
    sys->rate = ss.rate;
729

730 731 732 733 734 735 736 737
    /* Context events */
    const pa_subscription_mask_t mask = PA_SUBSCRIPTION_MASK_SINK_INPUT;

    pa_context_set_subscribe_callback(ctx, context_cb, aout);
    op = pa_context_subscribe(ctx, mask, NULL, NULL);
    if (likely(op != NULL))
       pa_operation_unref(op);

738
    /* Channel volume */
739
    sys->base_volume = PA_VOLUME_NORM;
740 741
    pa_cvolume_set(&sys->cvolume, ss.channels, PA_VOLUME_NORM);

742
    vlc_pa_lock();
743 744 745
    /* Create a playback stream */
    pa_stream *s = pa_stream_new(ctx, "audio stream", &ss, &map);
    if (s == NULL) {
746
        vlc_pa_error(obj, "stream creation failure", ctx);
747
        goto fail;
748
    }
749
    sys->stream = s;
750
    pa_stream_set_state_callback(s, stream_state_cb, NULL);
751
    pa_stream_set_latency_update_callback(s, stream_latency_cb, aout);
752
    pa_stream_set_moved_callback(s, stream_moved_cb, aout);
753 754 755 756
    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);
757

758
    if (pa_stream_connect_playback(s, NULL, &attr, flags, NULL, NULL) < 0
759 760
     || stream_wait(s)) {
        vlc_pa_error(obj, "stream connection failure", ctx);
761
        goto fail;
762 763
    }

764 765
    const struct pa_buffer_attr *pba = pa_stream_get_buffer_attr(s);
    msg_Dbg(aout, "using buffer metrics: maxlength=%u, tlength=%u, "
766 767
            "prebuf=%u, minreq=%u",
            pba->maxlength, pba->tlength, pba->prebuf, pba->minreq);
768

769
    aout->i_nb_samples = pba->minreq / pa_frame_size(&ss);
770 771 772 773 774 775 776 777 778 779

    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);
780
    stream_moved_cb(s, aout);
781
    vlc_pa_unlock();
782

783
    aout->format.i_format = format;
784 785
    aout->pf_play = Play;
    aout->pf_pause = Pause;
786
    aout->pf_flush = NULL;
787
    aout->pf_volume_set = VolumeSet;
788 789 790
    return VLC_SUCCESS;

fail:
791
    vlc_pa_unlock();
792
    Close(obj);
793 794 795
    return VLC_EGENERIC;
}

796 797 798
/**
 * Removes a PulseAudio playback stream
 */
799
static void Close (vlc_object_t *obj)
800
{
801
    audio_output_t *aout = (audio_output_t *)obj;
802
    aout_sys_t *sys = aout->sys;
803 804 805
    pa_context *ctx = sys->context;
    pa_stream *s = sys->stream;

806 807 808 809 810 811
    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");
    }

812
    vlc_pa_lock();
813 814 815
    if (s != NULL) {
        pa_operation *op;

816 817 818 819 820 821 822 823 824 825 826
        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
827
            pa_operation_unref(op);
828 829
        }

830 831
        pa_stream_disconnect(s);
        pa_stream_unref(s);
832
    }
833 834 835
    vlc_pa_unlock();

    vlc_pa_disconnect(obj, ctx);
836
    free(sys);
837
}