output.c 16.3 KB
Newer Older
1 2 3
/*****************************************************************************
 * output.c : internal management of output streams for the audio output
 *****************************************************************************
4
 * Copyright (C) 2002-2004 the VideoLAN team
5
 * $Id$
6 7 8 9 10 11 12
 *
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
 *
 * 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.
13
 *
14 15 16 17 18 19 20
 * 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
dionoea's avatar
dionoea committed
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 23 24 25 26
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
27 28 29 30
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

31
#include <vlc_common.h>
zorglub's avatar
zorglub committed
32
#include <vlc_aout.h>
33
#include <vlc_aout_intf.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
34
#include <vlc_cpu.h>
35
#include <vlc_modules.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
36

37
#include "libvlc.h"
38 39 40 41
#include "aout_internal.h"

/*****************************************************************************
 * aout_OutputNew : allocate a new output and rework the filter pipeline
42
 *****************************************************************************
43
 * This function is entered with the mixer lock.
44
 *****************************************************************************/
45
int aout_OutputNew( audio_output_t * p_aout,
46
                    const audio_sample_format_t * p_format )
47
{
48
    vlc_assert_locked( &p_aout->lock );
49
    p_aout->format = *p_format;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
50

Christophe Massiot's avatar
Christophe Massiot committed
51
    /* Retrieve user defaults. */
52
    int i_rate = var_InheritInteger( p_aout, "aout-rate" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
53
    if ( i_rate != 0 )
54 55
        p_aout->format.i_rate = i_rate;
    aout_FormatPrepare( &p_aout->format );
56

Christophe Massiot's avatar
Christophe Massiot committed
57
    /* Find the best output plug-in. */
58 59
    p_aout->module = module_need( p_aout, "audio output", "$aout", false );
    if ( p_aout->module == NULL )
60
    {
zorglub's avatar
zorglub committed
61
        msg_Err( p_aout, "no suitable audio output module" );
62 63
        return -1;
    }
64 65

    if ( var_Type( p_aout, "audio-channels" ) ==
gbazin's avatar
 
gbazin committed
66
             (VLC_VAR_INTEGER | VLC_VAR_HASCHOICE) )
67 68
    {
        /* The user may have selected a different channels configuration. */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
69
        switch( var_InheritInteger( p_aout, "audio-channels" ) )
70
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
71
            case AOUT_VAR_CHAN_RSTEREO:
72
                p_aout->format.i_original_channels |= AOUT_CHAN_REVERSESTEREO;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
73 74
                break;
            case AOUT_VAR_CHAN_STEREO:
75
                p_aout->format.i_original_channels =
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
76 77 78
                                              AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
                break;
            case AOUT_VAR_CHAN_LEFT:
79
                p_aout->format.i_original_channels = AOUT_CHAN_LEFT;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
80 81
                break;
            case AOUT_VAR_CHAN_RIGHT:
82
                p_aout->format.i_original_channels = AOUT_CHAN_RIGHT;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
83 84
                break;
            case AOUT_VAR_CHAN_DOLBYS:
85
                p_aout->format.i_original_channels =
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
86 87
                      AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_DOLBYSTEREO;
                break;
88 89
        }
    }
90 91
    else if ( p_aout->format.i_physical_channels == AOUT_CHAN_CENTER
              && (p_aout->format.i_original_channels
92
                   & AOUT_CHAN_PHYSMASK) == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) )
93
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
94 95
        vlc_value_t val, text;

96
        /* Mono - create the audio-channels variable. */
gbazin's avatar
 
gbazin committed
97 98
        var_Create( p_aout, "audio-channels",
                    VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
hartman's avatar
hartman committed
99
        text.psz_string = _("Audio Channels");
gbazin's avatar
 
gbazin committed
100 101 102 103 104 105 106 107
        var_Change( p_aout, "audio-channels", VLC_VAR_SETTEXT, &text, NULL );

        val.i_int = AOUT_VAR_CHAN_STEREO; text.psz_string = _("Stereo");
        var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
        val.i_int = AOUT_VAR_CHAN_LEFT; text.psz_string = _("Left");
        var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
        val.i_int = AOUT_VAR_CHAN_RIGHT; text.psz_string = _("Right");
        var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
108
        if ( p_aout->format.i_original_channels & AOUT_CHAN_DUALMONO )
109 110
        {
            /* Go directly to the left channel. */
111
            p_aout->format.i_original_channels = AOUT_CHAN_LEFT;
ivoire's avatar
ivoire committed
112
            var_SetInteger( p_aout, "audio-channels", AOUT_VAR_CHAN_LEFT );
113 114 115 116
        }
        var_AddCallback( p_aout, "audio-channels", aout_ChannelsRestart,
                         NULL );
    }
117
    else if ( p_aout->format.i_physical_channels ==
118
               (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)
119
                && (p_aout->format.i_original_channels &
120
                     (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)) )
121
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
122 123
        vlc_value_t val, text;

124
        /* Stereo - create the audio-channels variable. */
gbazin's avatar
 
gbazin committed
125 126
        var_Create( p_aout, "audio-channels",
                    VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
hartman's avatar
hartman committed
127
        text.psz_string = _("Audio Channels");
gbazin's avatar
 
gbazin committed
128 129
        var_Change( p_aout, "audio-channels", VLC_VAR_SETTEXT, &text, NULL );

130
        if ( p_aout->format.i_original_channels & AOUT_CHAN_DOLBYSTEREO )
131
        {
gbazin's avatar
 
gbazin committed
132
            val.i_int = AOUT_VAR_CHAN_DOLBYS;
133
            text.psz_string = _("Dolby Surround");
134 135 136
        }
        else
        {
gbazin's avatar
 
gbazin committed
137 138
            val.i_int = AOUT_VAR_CHAN_STEREO;
            text.psz_string = _("Stereo");
139
        }
gbazin's avatar
 
gbazin committed
140
        var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
gbazin's avatar
 
gbazin committed
141
        val.i_int = AOUT_VAR_CHAN_LEFT; text.psz_string = _("Left");
gbazin's avatar
 
gbazin committed
142 143 144 145 146
        var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
        val.i_int = AOUT_VAR_CHAN_RIGHT; text.psz_string = _("Right");
        var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
        val.i_int = AOUT_VAR_CHAN_RSTEREO; text.psz_string=_("Reverse stereo");
        var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
147
        if ( p_aout->format.i_original_channels & AOUT_CHAN_DUALMONO )
148 149
        {
            /* Go directly to the left channel. */
150
            p_aout->format.i_original_channels = AOUT_CHAN_LEFT;
ivoire's avatar
ivoire committed
151
            var_SetInteger( p_aout, "audio-channels", AOUT_VAR_CHAN_LEFT );
152
        }
153 154 155
        var_AddCallback( p_aout, "audio-channels", aout_ChannelsRestart,
                         NULL );
    }
156
    var_TriggerCallback( p_aout, "intf-change" );
157

158
    aout_FormatPrepare( &p_aout->format );
159

160
    /* Prepare FIFO. */
161 162
    aout_FifoInit( p_aout, &p_aout->fifo, p_aout->format.i_rate );
    aout_FormatPrint( p_aout, "output", &p_aout->format );
163

164
    /* Choose the mixer format. */
165 166
    p_aout->mixer_format = p_aout->format;
    if ( AOUT_FMT_NON_LINEAR(&p_aout->format) )
167
        p_aout->mixer_format.i_format = p_format->i_format;
168 169 170 171 172 173 174 175 176 177 178 179 180 181
    else
    /* Most audio filters can only deal with single-precision,
     * so lets always use that when hardware supports floating point. */
    if( HAVE_FPU )
        p_aout->mixer_format.i_format = VLC_CODEC_FL32;
    else
    /* Otherwise, audio filters will not work. Use fixed-point if the input has
     * more than 16-bits depth. */
    if( p_format->i_bitspersample > 16 )
        p_aout->mixer_format.i_format = VLC_CODEC_FI32;
    else
    /* Fallback to 16-bits. This avoids pointless conversion to and from
     * 32-bits samples for the sole purpose of software mixing. */
        p_aout->mixer_format.i_format = VLC_CODEC_S16N;
182

183
    aout_FormatPrepare( &p_aout->mixer_format );
184
    aout_FormatPrint( p_aout, "mixer", &p_aout->mixer_format );
185 186

    /* Create filters. */
187 188 189
    p_aout->i_nb_filters = 0;
    if ( aout_FiltersCreatePipeline( p_aout, p_aout->pp_filters,
                                     &p_aout->i_nb_filters,
190
                                     &p_aout->mixer_format,
191
                                     &p_aout->format ) < 0 )
192
    {
zorglub's avatar
zorglub committed
193
        msg_Err( p_aout, "couldn't create audio output pipeline" );
194 195
        module_unneed( p_aout, p_aout->module );
        p_aout->module = NULL;
196 197 198 199 200 201 202
        return -1;
    }
    return 0;
}

/*****************************************************************************
 * aout_OutputDelete : delete the output
203
 *****************************************************************************
204
 * This function is entered with the mixer lock.
205
 *****************************************************************************/
206
void aout_OutputDelete( audio_output_t * p_aout )
207
{
208 209
    vlc_assert_locked( &p_aout->lock );

210
    if( p_aout->module == NULL )
Rémi Denis-Courmont's avatar
Oooops  
Rémi Denis-Courmont committed
211
        return;
212

213
    module_unneed( p_aout, p_aout->module );
214
    aout_VolumeNoneInit( p_aout ); /* clear volume callback */
215 216 217
    p_aout->module = NULL;
    aout_FiltersDestroyPipeline( p_aout->pp_filters, p_aout->i_nb_filters );
    aout_FifoDestroy( &p_aout->fifo );
218 219 220 221
}

/*****************************************************************************
 * aout_OutputPlay : play a buffer
222
 *****************************************************************************
223
 * This function is entered with the mixer lock.
224
 *****************************************************************************/
225
void aout_OutputPlay( audio_output_t * p_aout, aout_buffer_t * p_buffer )
226
{
227 228
    vlc_assert_locked( &p_aout->lock );

229
    aout_FiltersPlay( p_aout->pp_filters, p_aout->i_nb_filters, &p_buffer );
230 231
    if( !p_buffer )
        return;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
232
    if( p_buffer->i_buffer == 0 )
233
    {
234
        block_Release( p_buffer );
235 236 237
        return;
    }

238 239
    aout_FifoPush( &p_aout->fifo, p_buffer );
    p_aout->pf_play( p_aout );
240 241
}

242 243 244 245 246
/**
 * 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.
 */
247
void aout_OutputPause( audio_output_t *aout, bool pause, mtime_t date )
248
{
249 250
    vlc_assert_locked( &aout->lock );

251 252
    if( aout->pf_pause != NULL )
        aout->pf_pause( aout, pause, date );
253 254
}

255 256 257 258 259 260 261 262 263 264 265 266 267 268
/**
 * Flushes or drains the audio output buffers.
 * This enables the output to expedite seek and stop.
 * @param wait if true, wait for buffer playback (i.e. drain),
 *             if false, discard the buffers immediately (i.e. flush)
 */
void aout_OutputFlush( audio_output_t *aout, bool wait )
{
    vlc_assert_locked( &aout->lock );

    if( aout->pf_flush != NULL )
        aout->pf_flush( aout, wait );
}

269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299

/*** Volume handling ***/

/**
 * Dummy volume setter. This is the default volume setter.
 */
static int aout_VolumeNoneSet (audio_output_t *aout, float volume, bool mute)
{
    (void)aout; (void)volume; (void)mute;
    return -1;
}

/**
 * Configures the dummy volume setter.
 * @note Audio output plugins for which volume is irrelevant
 * should call this function during activation.
 */
void aout_VolumeNoneInit (audio_output_t *aout)
{
    /* aout_New() -safely- calls this function without the lock, before any
     * other thread knows of this audio output instance.
    vlc_assert_locked (&aout->lock); */
    aout->pf_volume_set = aout_VolumeNoneSet;
}

/**
 * Volume setter for software volume.
 */
static int aout_VolumeSoftSet (audio_output_t *aout, float volume, bool mute)
{
    vlc_assert_locked (&aout->lock);
300 301 302 303 304 305 306 307 308 309 310 311 312

    /* Cubic mapping from software volume to amplification factor.
     * This provides a good tradeoff between low and high volume ranges.
     *
     * This code is only used for the VLC software mixer. If you change this
     * formula, be sure to update the aout_VolumeHardInit()-based plugins also.
     */
    if (!mute)
        volume = volume * volume * volume;
    else
        volume = 0.;

    aout->mixer_multiplier = volume;
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
    return 0;
}

/**
 * Configures the volume setter for software mixing
 * and apply the default volume.
 * @note Audio output plugins that cannot apply the volume
 * should call this function during activation.
 */
void aout_VolumeSoftInit (audio_output_t *aout)
{
    audio_volume_t volume = var_InheritInteger (aout, "volume");
    bool mute = var_InheritBool (aout, "mute");

    vlc_assert_locked (&aout->lock);
    aout->pf_volume_set = aout_VolumeSoftSet;
    aout_VolumeSoftSet (aout, volume / (float)AOUT_VOLUME_DEFAULT, mute);
}

/**
 * Configures a custom volume setter. This is used by audio outputs that can
 * control the hardware volume directly and/or emulate it internally.
 * @param setter volume setter callback
 */
void aout_VolumeHardInit (audio_output_t *aout, aout_volume_cb setter)
{
    vlc_assert_locked (&aout->lock);
    aout->pf_volume_set = setter;
}

/**
 * Supply or update the current custom ("hardware") volume.
 * @note This only makes sense after calling aout_VolumeHardInit().
 * @param setter volume setter callback
 * @param volume current custom volume
 * @param mute current mute flag
 * @note Audio output plugins that cannot apply the volume
 * should call this function during activation.
 */
void aout_VolumeHardSet (audio_output_t *aout, float volume, bool mute)
{
#warning FIXME
    /* REVISIT: This is tricky. We cannot acquire the volume lock as this gets
     * called from the audio output (it would cause a lock inversion).
     * We also should not override the input manager volume, but only the
     * volume of the current audio output... FIXME */
    msg_Err (aout, "%s(%f, %u)", __func__, volume, (unsigned)mute);
}


/*** Buffer management ***/

365 366
/*****************************************************************************
 * aout_OutputNextBuffer : give the audio output plug-in the right buffer
367 368 369
 *****************************************************************************
 * If b_can_sleek is 1, the aout core functions won't try to resample
 * new buffers to catch up - that is we suppose that the output plug-in can
370
 * compensate it by itself. S/PDIF outputs should always set b_can_sleek = 1.
371
 * This function is entered with no lock at all :-).
372
 *****************************************************************************/
373
aout_buffer_t * aout_OutputNextBuffer( audio_output_t * p_aout,
374
                                       mtime_t start_date,
375
                                       bool b_can_sleek )
376
{
377
    aout_fifo_t *p_fifo = &p_aout->fifo;
378
    aout_buffer_t * p_buffer;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
379
    mtime_t now = mdate();
380

381
    aout_lock( p_aout );
382

383 384 385
    /* Drop the audio sample if the audio output is really late.
     * In the case of b_can_sleek, we don't use a resampler so we need to be
     * a lot more severe. */
386
    while( ((p_buffer = p_fifo->p_first) != NULL)
387
     && p_buffer->i_pts < (b_can_sleek ? start_date : now) - AOUT_MAX_PTS_DELAY )
388
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
389
        msg_Dbg( p_aout, "audio output is too slow (%"PRId64"), "
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
390
                 "trashing %"PRId64"us", now - p_buffer->i_pts,
391
                 p_buffer->i_length );
392
        aout_BufferFree( aout_FifoPop( p_fifo ) );
393 394
    }

395
    if( p_buffer == NULL )
396
    {
gbazin's avatar
 
gbazin committed
397
#if 0 /* This is bad because the audio output might just be trying to fill
398
       * in its internal buffers. And anyway, it's up to the audio output
gbazin's avatar
 
gbazin committed
399 400
       * to deal with this kind of starvation. */

401
        /* Set date to 0, to allow the mixer to send a new buffer ASAP */
402
        aout_FifoReset( &p_aout->fifo );
403
        if ( !p_aout->b_starving )
404
            msg_Dbg( p_aout,
405
                 "audio output is starving (no input), playing silence" );
406
        p_aout->b_starving = true;
gbazin's avatar
 
gbazin committed
407
#endif
408
        goto out;
409 410
    }

411
    mtime_t delta = start_date - p_buffer->i_pts;
412
    /* Here we suppose that all buffers have the same duration - this is
413 414
     * generally true, and anyway if it's wrong it won't be a disaster.
     */
415
    if ( 0 > delta + p_buffer->i_length )
416
    {
417
        if ( !p_aout->b_starving )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
418
            msg_Dbg( p_aout, "audio output is starving (%"PRId64"), "
419
                     "playing silence", -delta );
420
        p_aout->b_starving = true;
421 422
        p_buffer = NULL;
        goto out;
423 424
    }

425
    p_aout->b_starving = false;
426
    p_buffer = aout_FifoPop( p_fifo );
427

428 429
    if( !b_can_sleek
     && ( delta > AOUT_MAX_PTS_DELAY || delta < -AOUT_MAX_PTS_ADVANCE ) )
430
    {
431 432 433
        /* Try to compensate the drift by doing some resampling. */
        msg_Warn( p_aout, "output date isn't PTS date, requesting "
                  "resampling (%"PRId64")", delta );
434

435
        aout_FifoMoveDates( &p_aout->p_input->fifo, delta );
436
        aout_FifoMoveDates( p_fifo, delta );
437
    }
438 439
out:
    aout_unlock( p_aout );
440 441
    return p_buffer;
}