output.c 26.5 KB
Newer Older
1 2 3
/*****************************************************************************
 * output.c : internal management of output streams for the audio output
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2002-2004 VLC authors and VideoLAN
5
 * $Id$
6 7 8
 *
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
9 10 11
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
12
 * (at your option) any later version.
13
 *
14 15
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
16 17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
18
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
19 20 21
 * You should have received a copy of the GNU Lesser 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.
22 23
 *****************************************************************************/

24 25 26 27
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

28 29 30
#include <stdlib.h>
#include <assert.h>

31
#include <vlc_common.h>
Clément Stenac's avatar
Clément Stenac committed
32
#include <vlc_aout.h>
33
#include <vlc_modules.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
34

35
#include "libvlc.h"
36 37
#include "aout_internal.h"

38 39
static const char unset_str[1] = ""; /* Non-NULL constant string pointer */

40 41 42 43 44 45 46 47
struct aout_dev
{
    aout_dev_t *next;
    char *name;
    char id[1];
};


48
/* Local functions */
49 50 51 52 53 54 55
static void aout_OutputAssertLocked (audio_output_t *aout)
{
    aout_owner_t *owner = aout_owner (aout);

    vlc_assert_locked (&owner->lock);
}

56 57 58 59 60 61 62 63 64 65 66
static void aout_Destructor( vlc_object_t * p_this );

static int var_Copy (vlc_object_t *src, const char *name, vlc_value_t prev,
                     vlc_value_t value, void *data)
{
    vlc_object_t *dst = data;

    (void) src; (void) prev;
    return var_Set (dst, name, value);
}

67 68 69 70 71 72 73 74 75
static int var_CopyDevice (vlc_object_t *src, const char *name,
                           vlc_value_t prev, vlc_value_t value, void *data)
{
    vlc_object_t *dst = data;

    (void) src; (void) name; (void) prev;
    return var_Set (dst, "audio-device", value);
}

76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
/**
 * Supply or update the current custom ("hardware") volume.
 * @note This only makes sense after calling aout_VolumeHardInit().
 * @param volume current custom volume
 *
 * @warning The caller (i.e. the audio output plug-in) is responsible for
 * interlocking and synchronizing call to this function and to the
 * audio_output_t.volume_set callback. This ensures that VLC gets correct
 * volume information (possibly with a latency).
 */
static void aout_VolumeNotify (audio_output_t *aout, float volume)
{
    var_SetFloat (aout, "volume", volume);
}

static void aout_MuteNotify (audio_output_t *aout, bool mute)
{
    var_SetBool (aout, "mute", mute);
}

static void aout_PolicyNotify (audio_output_t *aout, bool cork)
{
98
    (cork ? var_IncInteger : var_DecInteger) (aout->obj.parent, "corks");
99 100
}

101 102
static void aout_DeviceNotify (audio_output_t *aout, const char *id)
{
103
    var_SetString (aout, "device", (id != NULL) ? id : "");
104 105
}

106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
static void aout_HotplugNotify (audio_output_t *aout,
                                const char *id, const char *name)
{
    aout_owner_t *owner = aout_owner (aout);
    aout_dev_t *dev, **pp = &owner->dev.list;

    vlc_mutex_lock (&owner->dev.lock);
    while ((dev = *pp) != NULL)
    {
        if (!strcmp (id, dev->id))
            break;
        pp = &dev->next;
    }

    if (name != NULL)
    {
        if (dev == NULL) /* Added device */
        {
            dev = malloc (sizeof (*dev) + strlen (id));
            if (unlikely(dev == NULL))
                goto out;
            dev->next = NULL;
            strcpy (dev->id, id);
            *pp = dev;
            owner->dev.count++;
        }
        else /* Modified device */
            free (dev->name);
        dev->name = strdup (name);
    }
    else
    {
        if (dev != NULL) /* Removed device */
        {
            owner->dev.count--;
            *pp = dev->next;
            free (dev->name);
            free (dev);
        }
    }
out:
    vlc_mutex_unlock (&owner->dev.lock);
}

150 151 152 153 154
static void aout_RestartNotify (audio_output_t *aout, unsigned mode)
{
    aout_RequestRestart (aout, mode);
}

155 156 157 158
static int aout_GainNotify (audio_output_t *aout, float gain)
{
    aout_owner_t *owner = aout_owner (aout);

159
    aout_OutputAssertLocked (aout);
160 161 162 163 164
    aout_volume_SetVolume (owner->volume, gain);
    /* XXX: ideally, return -1 if format cannot be amplified */
    return 0;
}

165 166 167
static int FilterCallback (vlc_object_t *obj, const char *var,
                           vlc_value_t prev, vlc_value_t cur, void *data)
{
168 169 170
    if (strcmp(prev.psz_string, cur.psz_string))
        aout_InputRequestRestart ((audio_output_t *)obj);
    (void) var; (void) data;
171 172 173
    return VLC_SUCCESS;
}

174 175 176 177 178 179
static int StereoModeCallback (vlc_object_t *obj, const char *varname,
                               vlc_value_t oldval, vlc_value_t newval, void *data)
{
    audio_output_t *aout = (audio_output_t *)obj;
    (void)varname; (void)oldval; (void)newval; (void)data;

180
    aout_RestartRequest (aout, AOUT_RESTART_STEREOMODE);
181 182 183
    return 0;
}

184 185 186 187 188 189 190 191 192
static int ViewpointCallback (vlc_object_t *obj, const char *var,
                              vlc_value_t prev, vlc_value_t cur, void *data)
{
    if( cur.p_address != NULL )
        aout_ChangeViewpoint((audio_output_t *)obj, cur.p_address );
    (void) var; (void) data; (void) prev;
    return VLC_SUCCESS;
}

193 194 195 196 197 198
#undef aout_New
/**
 * Creates an audio output object and initializes an output module.
 */
audio_output_t *aout_New (vlc_object_t *parent)
{
199 200
    vlc_value_t val, text;

201 202 203 204 205 206 207 208
    audio_output_t *aout = vlc_custom_create (parent, sizeof (aout_instance_t),
                                              "audio output");
    if (unlikely(aout == NULL))
        return NULL;

    aout_owner_t *owner = aout_owner (aout);

    vlc_mutex_init (&owner->lock);
209
    vlc_mutex_init (&owner->req.lock);
210
    vlc_mutex_init (&owner->dev.lock);
211
    vlc_mutex_init (&owner->vp.lock);
212 213
    vlc_viewpoint_init (&owner->vp.value);
    atomic_init (&owner->vp.update, false);
214
    owner->req.device = (char *)unset_str;
215 216 217
    owner->req.volume = -1.f;
    owner->req.mute = -1;

218 219 220 221 222 223 224
    vlc_object_set_destructor (aout, aout_Destructor);

    /* Audio output module callbacks */
    var_Create (aout, "volume", VLC_VAR_FLOAT);
    var_AddCallback (aout, "volume", var_Copy, parent);
    var_Create (aout, "mute", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
    var_AddCallback (aout, "mute", var_Copy, parent);
225
    var_Create (aout, "device", VLC_VAR_STRING);
226
    var_AddCallback (aout, "device", var_CopyDevice, parent);
227 228 229 230

    aout->event.volume_report = aout_VolumeNotify;
    aout->event.mute_report = aout_MuteNotify;
    aout->event.policy_report = aout_PolicyNotify;
231 232
    aout->event.device_report = aout_DeviceNotify;
    aout->event.hotplug_report = aout_HotplugNotify;
233
    aout->event.gain_request = aout_GainNotify;
234
    aout->event.restart_request = aout_RestartNotify;
235 236 237 238 239 240

    /* Audio output module initialization */
    aout->start = NULL;
    aout->stop = NULL;
    aout->volume_set = NULL;
    aout->mute_set = NULL;
241
    aout->device_select = NULL;
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
    owner->module = module_need (aout, "audio output", "$aout", false);
    if (owner->module == NULL)
    {
        msg_Err (aout, "no suitable audio output module");
        vlc_object_release (aout);
        return NULL;
    }

    /*
     * Persistent audio output variables
     */
    module_config_t *cfg;
    char *str;

    /* Visualizations */
257
    var_Create (aout, "visual", VLC_VAR_STRING);
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
    text.psz_string = _("Visualizations");
    var_Change (aout, "visual", VLC_VAR_SETTEXT, &text, NULL);
    val.psz_string = (char *)"";
    text.psz_string = _("Disable");
    var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
    val.psz_string = (char *)"spectrometer";
    text.psz_string = _("Spectrometer");
    var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
    val.psz_string = (char *)"scope";
    text.psz_string = _("Scope");
    var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
    val.psz_string = (char *)"spectrum";
    text.psz_string = _("Spectrum");
    var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
    val.psz_string = (char *)"vuMeter";
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
273
    text.psz_string = _("VU meter");
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
    var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
    /* Look for goom plugin */
    if (module_exists ("goom"))
    {
        val.psz_string = (char *)"goom";
        text.psz_string = (char *)"Goom";
        var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
    }
    /* Look for libprojectM plugin */
    if (module_exists ("projectm"))
    {
        val.psz_string = (char *)"projectm";
        text.psz_string = (char*)"projectM";
        var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
    }
    /* Look for VSXu plugin */
    if (module_exists ("vsxu"))
    {
        val.psz_string = (char *)"vsxu";
        text.psz_string = (char*)"Vovoid VSXu";
        var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
    }
296 297 298 299 300 301 302
    /* Look for glspectrum plugin */
    if (module_exists ("glspectrum"))
    {
        val.psz_string = (char *)"glspectrum";
        text.psz_string = (char*)"3D spectrum";
        var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
    }
303 304 305 306 307 308 309 310
    str = var_GetNonEmptyString (aout, "effect-list");
    if (str != NULL)
    {
        var_SetString (aout, "visual", str);
        free (str);
    }

    var_Create (aout, "audio-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
311
    var_AddCallback (aout, "audio-filter", FilterCallback, NULL);
312 313 314
    text.psz_string = _("Audio filters");
    var_Change (aout, "audio-filter", VLC_VAR_SETTEXT, &text, NULL);

315
    var_Create (aout, "viewpoint", VLC_VAR_ADDRESS );
316
    var_AddCallback (aout, "viewpoint", ViewpointCallback, NULL);
317 318 319 320 321 322 323 324 325 326

    var_Create (aout, "audio-visual", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
    text.psz_string = _("Audio visualizations");
    var_Change (aout, "audio-visual", VLC_VAR_SETTEXT, &text, NULL);

    /* Replay gain */
    var_Create (aout, "audio-replay-gain-mode",
                VLC_VAR_STRING | VLC_VAR_DOINHERIT );
    text.psz_string = _("Replay gain");
    var_Change (aout, "audio-replay-gain-mode", VLC_VAR_SETTEXT, &text, NULL);
327
    cfg = config_FindConfig("audio-replay-gain-mode");
328 329 330
    if (likely(cfg != NULL))
        for (unsigned i = 0; i < cfg->list_count; i++)
        {
331
            val.psz_string = (char *)cfg->list.psz[i];
332 333 334 335 336
            text.psz_string = vlc_gettext(cfg->list_text[i]);
            var_Change (aout, "audio-replay-gain-mode", VLC_VAR_ADDCHOICE,
                            &val, &text);
        }

337 338 339 340 341 342 343
    /* Stereo mode */
    var_Create (aout, "stereo-mode", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
    var_AddCallback (aout, "stereo-mode", StereoModeCallback, NULL);
    vlc_value_t txt;
    txt.psz_string = _("Stereo audio mode");
    var_Change (aout, "stereo-mode", VLC_VAR_SETTEXT, &txt, NULL);

344
    /* Equalizer */
Mark Lee's avatar
Mark Lee committed
345 346
    var_Create (aout, "equalizer-preamp", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
    var_Create (aout, "equalizer-bands", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
347
    var_Create (aout, "equalizer-preset", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
Mark Lee's avatar
Mark Lee committed
348

349 350 351 352 353 354 355 356 357 358
    return aout;
}

/**
 * Deinitializes an audio output module and destroys an audio output object.
 */
void aout_Destroy (audio_output_t *aout)
{
    aout_owner_t *owner = aout_owner (aout);

359
    aout_OutputLock (aout);
360 361 362 363
    module_unneed (aout, owner->module);
    /* Protect against late call from intf.c */
    aout->volume_set = NULL;
    aout->mute_set = NULL;
364
    aout->device_select = NULL;
365
    aout_OutputUnlock (aout);
366

367
    var_DelCallback (aout, "viewpoint", ViewpointCallback, NULL);
368
    var_DelCallback (aout, "audio-filter", FilterCallback, NULL);
369 370
    var_DelCallback (aout, "device", var_CopyDevice, aout->obj.parent);
    var_DelCallback (aout, "mute", var_Copy, aout->obj.parent);
371
    var_SetFloat (aout, "volume", -1.f);
372
    var_DelCallback (aout, "volume", var_Copy, aout->obj.parent);
373
    var_DelCallback (aout, "stereo-mode", StereoModeCallback, NULL);
374 375 376 377 378 379 380 381 382 383 384
    vlc_object_release (aout);
}

/**
 * Destroys the audio output lock used (asynchronously) by interface functions.
 */
static void aout_Destructor (vlc_object_t *obj)
{
    audio_output_t *aout = (audio_output_t *)obj;
    aout_owner_t *owner = aout_owner (aout);

385 386 387 388 389 390 391 392
    vlc_mutex_destroy (&owner->dev.lock);
    for (aout_dev_t *dev = owner->dev.list, *next; dev != NULL; dev = next)
    {
        next = dev->next;
        free (dev->name);
        free (dev);
    }

393
    assert (owner->req.device == unset_str);
394
    vlc_mutex_destroy (&owner->vp.lock);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
395
    vlc_mutex_destroy (&owner->req.lock);
396 397 398
    vlc_mutex_destroy (&owner->lock);
}

399 400 401
static void aout_PrepareStereoMode (audio_output_t *aout,
                                    audio_sample_format_t *restrict fmt,
                                    aout_filters_cfg_t *filters_cfg,
402 403 404
                                    audio_channel_type_t input_chan_type,
                                    unsigned i_nb_input_channels,
                                    int i_forced_stereo_mode)
405
{
406 407
    /* Fill Stereo mode choices */
    var_Change (aout, "stereo-mode", VLC_VAR_CLEARCHOICES, NULL, NULL);
408
    vlc_value_t val, txt, default_val = { .i_int = AOUT_VAR_CHAN_UNSET };
409
    val.i_int = 0;
410

411 412 413
    if (!AOUT_FMT_LINEAR(fmt))
        return;

414
    if (i_nb_input_channels != 2)
415
    {
416 417
        val.i_int = AOUT_VAR_CHAN_UNSET;
        txt.psz_string = _("Original");
418
        var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
419
    }
420
    if (fmt->i_chan_mode & AOUT_CHANMODE_DOLBYSTEREO)
421 422 423 424 425 426 427 428 429 430 431
    {
        val.i_int = AOUT_VAR_CHAN_DOLBYS;
        txt.psz_string = _("Dolby Surround");
    }
    else
    {
        val.i_int = AOUT_VAR_CHAN_STEREO;
        txt.psz_string = _("Stereo");
    }
    var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);

432
    if (i_nb_input_channels == 2)
433
    {
434 435
        default_val.i_int = val.i_int; /* Stereo or Dolby Surround */

436 437 438 439 440 441
        val.i_int = AOUT_VAR_CHAN_LEFT;
        txt.psz_string = _("Left");
        var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
        val.i_int = AOUT_VAR_CHAN_RIGHT;
        txt.psz_string = _("Right");
        var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
442 443 444 445

        val.i_int = AOUT_VAR_CHAN_RSTEREO;
        txt.psz_string = _("Reverse stereo");
        var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
446 447
    }

448 449
    if (input_chan_type == AUDIO_CHANNEL_TYPE_AMBISONICS
     || i_nb_input_channels > 2)
450 451 452 453
    {
        val.i_int = AOUT_VAR_CHAN_HEADPHONES;
        txt.psz_string = _("Headphones");
        var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
454 455 456 457 458 459 460 461 462

        if (i_forced_stereo_mode == AOUT_VAR_CHAN_UNSET
         && aout->current_sink_info.headphones)
        {
            i_forced_stereo_mode = AOUT_VAR_CHAN_HEADPHONES;
            default_val.i_int = val.i_int;
            var_Change (aout, "stereo-mode", VLC_VAR_SETVALUE, &default_val,
                        NULL);
        }
463 464
    }

465
    /* The user may have selected a different channels configuration. */
466
    switch (i_forced_stereo_mode)
467
    {
468
        case AOUT_VAR_CHAN_RSTEREO:
469 470
            filters_cfg->remap[AOUT_CHANIDX_LEFT] = AOUT_CHANIDX_RIGHT;
            filters_cfg->remap[AOUT_CHANIDX_RIGHT] = AOUT_CHANIDX_LEFT;
471
            break;
472 473 474
        case AOUT_VAR_CHAN_STEREO:
            break;
        case AOUT_VAR_CHAN_LEFT:
475
            filters_cfg->remap[AOUT_CHANIDX_RIGHT] = AOUT_CHANIDX_DISABLE;
476 477
            break;
        case AOUT_VAR_CHAN_RIGHT:
478
            filters_cfg->remap[AOUT_CHANIDX_LEFT] = AOUT_CHANIDX_DISABLE;
479 480
            break;
        case AOUT_VAR_CHAN_DOLBYS:
481
            fmt->i_chan_mode = AOUT_CHANMODE_DOLBYSTEREO;
482
            break;
483 484 485
        case AOUT_VAR_CHAN_HEADPHONES:
            filters_cfg->headphones = true;
            break;
486
        default:
487 488
            if (i_nb_input_channels == 2
             && fmt->i_chan_mode & AOUT_CHANMODE_DUALMONO)
489
            {   /* Go directly to the left channel. */
490
                filters_cfg->remap[AOUT_CHANIDX_RIGHT] = AOUT_CHANIDX_DISABLE;
491
                default_val.i_int = val.i_int = AOUT_VAR_CHAN_LEFT;
492
            }
493 494
            var_Change (aout, "stereo-mode", VLC_VAR_SETVALUE, &default_val,
                        NULL);
495
            break;
496
    }
497 498 499 500 501 502 503 504 505 506 507 508
}

/**
 * Starts an audio output stream.
 * \param fmt audio output stream format [IN/OUT]
 * \warning The caller must hold the audio output lock.
 */
int aout_OutputNew (audio_output_t *aout, audio_sample_format_t *restrict fmt,
                    aout_filters_cfg_t *filters_cfg)
{
    aout_OutputAssertLocked (aout);

509
    audio_channel_type_t input_chan_type = fmt->channel_type;
510
    int i_forced_stereo_mode = AOUT_VAR_CHAN_UNSET;
511
    unsigned i_nb_input_channels = fmt->i_channels;
512 513 514 515 516

    /* Ideally, the audio filters would be created before the audio output,
     * and the ideal audio format would be the output of the filters chain.
     * But that scheme would not really play well with digital pass-through. */
    if (AOUT_FMT_LINEAR(fmt))
Thomas Guillem's avatar
Thomas Guillem committed
517 518 519 520 521 522 523 524 525
    {
        if (fmt->channel_type == AUDIO_CHANNEL_TYPE_BITMAP
         && aout_FormatNbChannels(fmt) == 0)
        {
            /* The output channel map is unknown, use the WAVE one. */
            assert(fmt->i_channels > 0);
            aout_SetWavePhysicalChannels(fmt);
        }

526 527 528 529 530 531 532 533
        if (fmt->channel_type == AUDIO_CHANNEL_TYPE_AMBISONICS)
        {
            /* Set the maximum of channels to render ambisonics contents. The
             * aout module will still be free to select less channels in order
             * to respect the sink setup. */
            fmt->i_physical_channels = AOUT_CHANS_7_1;
        }

Thomas Guillem's avatar
Thomas Guillem committed
534
        /* Try to stay in integer domain if possible for no/slow FPU. */
535 536 537
        fmt->i_format = (fmt->i_bitspersample > 16) ? VLC_CODEC_FL32
                                                    : VLC_CODEC_S16N;

538 539 540 541 542 543 544 545 546 547
        i_forced_stereo_mode = var_GetInteger (aout, "stereo-mode");
        if (i_forced_stereo_mode != AOUT_VAR_CHAN_UNSET)
        {
            if (i_forced_stereo_mode == AOUT_VAR_CHAN_LEFT
             || i_forced_stereo_mode == AOUT_VAR_CHAN_RIGHT)
                fmt->i_physical_channels = AOUT_CHAN_CENTER;
            else
                fmt->i_physical_channels = AOUT_CHANS_STEREO;
        }

548
        aout_FormatPrepare (fmt);
549
        assert (aout_FormatNbChannels(fmt) > 0);
550 551
    }

552 553
    aout->current_sink_info.headphones = false;

554 555 556 557 558 559
    if (aout->start (aout, fmt))
    {
        msg_Err (aout, "module not functional");
        return -1;
    }

560 561
    aout_PrepareStereoMode (aout, fmt, filters_cfg, input_chan_type,
                            i_nb_input_channels, i_forced_stereo_mode);
562

563
    aout_FormatPrepare (fmt);
Thomas Guillem's avatar
Thomas Guillem committed
564
    assert (fmt->i_bytes_per_frame > 0 && fmt->i_frame_length > 0);
565
    aout_FormatPrint (aout, "output", fmt);
566 567 568
    return 0;
}

569
/**
570
 * Stops the audio output stream (undoes aout_OutputNew()).
571
 * \note This can only be called after a successful aout_OutputNew().
572
 * \warning The caller must hold the audio output lock.
573 574
 */
void aout_OutputDelete (audio_output_t *aout)
575
{
576
    aout_OutputAssertLocked (aout);
577

578 579
    if (aout->stop != NULL)
        aout->stop (aout);
580 581
}

582
int aout_OutputTimeGet (audio_output_t *aout, mtime_t *delay)
583
{
584
    aout_OutputAssertLocked (aout);
585 586 587

    if (aout->time_get == NULL)
        return -1;
588
    return aout->time_get (aout, delay);
589 590
}

591 592
/**
 * Plays a decoded audio buffer.
593
 * \note This can only be called after a successful aout_OutputNew().
594
 * \warning The caller must hold the audio output lock.
595
 */
596
void aout_OutputPlay (audio_output_t *aout, block_t *block)
597
{
598
    aout_OutputAssertLocked (aout);
Thomas Guillem's avatar
Thomas Guillem committed
599 600
#ifndef NDEBUG
    aout_owner_t *owner = aout_owner (aout);
601 602
    assert (owner->mixer_format.i_frame_length > 0);
    assert (block->i_buffer == 0 || block->i_buffer / block->i_nb_samples ==
Thomas Guillem's avatar
Thomas Guillem committed
603 604 605
            owner->mixer_format.i_bytes_per_frame /
            owner->mixer_format.i_frame_length);
#endif
606
    aout->play (aout, block);
607 608
}

609 610 611 612 613 614 615
static void PauseDefault (audio_output_t *aout, bool pause, mtime_t date)
{
    if (pause)
        aout_OutputFlush (aout, false);
    (void) date;
}

616 617 618 619
/**
 * Notifies the audio output (if any) of pause/resume events.
 * This enables the output to expedite pause, instead of waiting for its
 * buffers to drain.
620
 * \note This can only be called after a successful aout_OutputNew().
621
 * \warning The caller must hold the audio output lock.
622
 */
623
void aout_OutputPause( audio_output_t *aout, bool pause, mtime_t date )
624
{
625
    aout_OutputAssertLocked (aout);
626
    ((aout->pause != NULL) ? aout->pause : PauseDefault) (aout, pause, date);
627 628
}

629 630 631
/**
 * Flushes or drains the audio output buffers.
 * This enables the output to expedite seek and stop.
632
 * \param wait if true, wait for buffer playback (i.e. drain),
633
 *             if false, discard the buffers immediately (i.e. flush)
634
 * \note This can only be called after a successful aout_OutputNew().
635
 * \warning The caller must hold the audio output lock.
636 637 638
 */
void aout_OutputFlush( audio_output_t *aout, bool wait )
{
639
    aout_OutputAssertLocked( aout );
640
    aout->flush (aout, wait);
641
}
642

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660
static int aout_OutputVolumeSet (audio_output_t *aout, float vol)
{
    aout_OutputAssertLocked (aout);
    return (aout->volume_set != NULL) ? aout->volume_set (aout, vol) : -1;
}

static int aout_OutputMuteSet (audio_output_t *aout, bool mute)
{
    aout_OutputAssertLocked (aout);
    return (aout->mute_set != NULL) ? aout->mute_set (aout, mute) : -1;
}

static int aout_OutputDeviceSet (audio_output_t *aout, const char *id)
{
    aout_OutputAssertLocked (aout);
    return (aout->device_select != NULL) ? aout->device_select (aout, id) : -1;
}

661 662 663 664 665 666 667
void aout_OutputLock (audio_output_t *aout)
{
    aout_owner_t *owner = aout_owner (aout);

    vlc_mutex_lock (&owner->lock);
}

668 669 670 671 672 673 674
static int aout_OutputTryLock (audio_output_t *aout)
{
    aout_owner_t *owner = aout_owner (aout);

    return vlc_mutex_trylock (&owner->lock);
}

675 676 677 678
void aout_OutputUnlock (audio_output_t *aout)
{
    aout_owner_t *owner = aout_owner (aout);

679 680 681
    vlc_assert_locked (&owner->lock);
    vlc_mutex_lock (&owner->req.lock);

682
    if (owner->req.device != unset_str)
683 684 685
    {
        aout_OutputDeviceSet (aout, owner->req.device);
        free (owner->req.device);
686
        owner->req.device = (char *)unset_str;
687 688 689 690 691 692 693 694 695 696 697 698 699 700
    }

    if (owner->req.volume >= 0.f)
    {
        aout_OutputVolumeSet (aout, owner->req.volume);
        owner->req.volume = -1.f;
    }

    if (owner->req.mute >= 0)
    {
        aout_OutputMuteSet (aout, owner->req.mute);
        owner->req.mute = -1;
    }

701
    vlc_mutex_unlock (&owner->lock);
702 703 704 705 706 707
    /* If another thread is blocked waiting for owner->req.lock at this point,
     * this aout_OutputUnlock() call will not see and apply its change request.
     * The other thread will need to apply the change request itself, which
     * implies it is able to (try-)lock owner->lock. Therefore this thread must
     * release owner->lock _before_ owner->req.lock. Do not reorder!!! */
    vlc_mutex_unlock (&owner->req.lock);
708
}
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
709 710 711 712 713 714 715 716 717 718 719 720 721 722

/**
 * Gets the volume of the audio output stream (independent of mute).
 * \return Current audio volume (0. = silent, 1. = nominal),
 * or a strictly negative value if undefined.
 */
float aout_VolumeGet (audio_output_t *aout)
{
    return var_GetFloat (aout, "volume");
}

/**
 * Sets the volume of the audio output stream.
 * \note The mute status is not changed.
723
 * \return 0 on success, -1 on failure (TODO).
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
724 725 726
 */
int aout_VolumeSet (audio_output_t *aout, float vol)
{
727
    aout_owner_t *owner = aout_owner (aout);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
728

729 730 731 732 733 734 735 736
    assert (vol >= 0.f);
    vlc_mutex_lock (&owner->req.lock);
    owner->req.volume = vol;
    vlc_mutex_unlock (&owner->req.lock);

    if (aout_OutputTryLock (aout) == 0)
        aout_OutputUnlock (aout);
    return 0;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
737 738 739 740 741 742 743 744 745 746 747 748 749
}

/**
 * Gets the audio output stream mute flag.
 * \return 0 if not muted, 1 if muted, -1 if undefined.
 */
int aout_MuteGet (audio_output_t *aout)
{
    return var_InheritBool (aout, "mute");
}

/**
 * Sets the audio output stream mute flag.
750
 * \return 0 on success, -1 on failure (TODO).
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
751 752 753
 */
int aout_MuteSet (audio_output_t *aout, bool mute)
{
754
    aout_owner_t *owner = aout_owner (aout);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
755

756 757 758 759 760 761 762
    vlc_mutex_lock (&owner->req.lock);
    owner->req.mute = mute;
    vlc_mutex_unlock (&owner->req.lock);

    if (aout_OutputTryLock (aout) == 0)
        aout_OutputUnlock (aout);
    return 0;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777
}

/**
 * Gets the currently selected device.
 * \return the selected device ID (caller must free() it)
 *         NULL if no device is selected or in case of error.
 */
char *aout_DeviceGet (audio_output_t *aout)
{
    return var_GetNonEmptyString (aout, "device");
}

/**
 * Selects an audio output device.
 * \param id device ID to select, or NULL for the default device
778
 * \return zero on success, non-zero on error (TODO).
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
779 780 781
 */
int aout_DeviceSet (audio_output_t *aout, const char *id)
{
782
    aout_owner_t *owner = aout_owner (aout);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
783

784 785 786 787 788 789 790 791 792
    char *dev = NULL;
    if (id != NULL)
    {
        dev = strdup (id);
        if (unlikely(dev == NULL))
            return -1;
    }

    vlc_mutex_lock (&owner->req.lock);
793 794
    if (owner->req.device != unset_str)
        free (owner->req.device);
795 796 797 798 799 800
    owner->req.device = dev;
    vlc_mutex_unlock (&owner->req.lock);

    if (aout_OutputTryLock (aout) == 0)
        aout_OutputUnlock (aout);
    return 0;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
801 802 803 804 805 806 807 808
}

/**
 * Enumerates possible audio output devices.
 *
 * The function will heap-allocate two tables of heap-allocated strings;
 * the caller is responsible for freeing all strings and both tables.
 *
809
 * \param ids pointer to a table of device identifiers [OUT]
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
810 811 812 813 814 815
 * \param names pointer to a table of device human-readable descriptions [OUT]
 * \return the number of devices, or negative on error.
 * \note In case of error, *ids and *names are undefined.
 */
int aout_DevicesList (audio_output_t *aout, char ***ids, char ***names)
{
816 817
    aout_owner_t *owner = aout_owner (aout);
    char **tabid, **tabname;
818
    unsigned i = 0;
819 820

    vlc_mutex_lock (&owner->dev.lock);
821 822 823
    tabid = malloc (sizeof (*tabid) * owner->dev.count);
    tabname = malloc (sizeof (*tabname) * owner->dev.count);

824 825 826 827 828 829 830 831 832 833 834
    if (unlikely(tabid == NULL || tabname == NULL))
        goto error;

    *ids = tabid;
    *names = tabname;

    for (aout_dev_t *dev = owner->dev.list; dev != NULL; dev = dev->next)
    {
        tabid[i] = strdup(dev->id);
        if (unlikely(tabid[i] == NULL))
            goto error;
835

836 837 838 839 840
        tabname[i] = strdup(dev->name);
        if (unlikely(tabname[i] == NULL))
        {
            free(tabid[i]);
            goto error;
841
        }
842 843

        i++;
844 845
    }
    vlc_mutex_unlock (&owner->dev.lock);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
846

847 848 849 850 851
    return i;

error:
    vlc_mutex_unlock(&owner->dev.lock);
    while (i > 0)
852
    {
853 854 855
        i--;
        free(tabname[i]);
        free(tabid[i]);
856
    }
857 858 859
    free(tabname);
    free(tabid);
    return -1;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
860
}