output.c 12.6 KB
Newer Older
1 2 3 4
/*****************************************************************************
 * output.c : internal management of output streams for the audio output
 *****************************************************************************
 * Copyright (C) 2002 VideoLAN
5
 * $Id: output.c,v 1.24 2002/11/14 22:38:48 massiot Exp $
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
 *
 * 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.
 * 
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>                            /* calloc(), malloc(), free() */
#include <string.h>

#include <vlc/vlc.h>

#include "audio_output.h"
#include "aout_internal.h"

/*****************************************************************************
 * aout_OutputNew : allocate a new output and rework the filter pipeline
37
 *****************************************************************************
38
 * This function is entered with the mixer lock.
39 40 41 42
 *****************************************************************************/
int aout_OutputNew( aout_instance_t * p_aout,
                    audio_sample_format_t * p_format )
{
Christophe Massiot's avatar
Christophe Massiot committed
43
    /* Retrieve user defaults. */
44 45 46 47
    char * psz_name = config_GetPsz( p_aout, "aout" );
    int i_rate = config_GetInt( p_aout, "aout-rate" );

    memcpy( &p_aout->output.output, p_format, sizeof(audio_sample_format_t) );
48 49
    if ( i_rate != -1 )
        p_aout->output.output.i_rate = i_rate;
Christophe Massiot's avatar
Christophe Massiot committed
50
    aout_FormatPrepare( &p_aout->output.output );
51

52 53
    vlc_mutex_lock( &p_aout->output_fifo_lock );

Christophe Massiot's avatar
Christophe Massiot committed
54 55 56 57 58
    /* Find the best output plug-in. */
    p_aout->output.p_module = module_Need( p_aout, "audio output",
                                           psz_name );
    if ( psz_name != NULL ) free( psz_name );
    if ( p_aout->output.p_module == NULL )
59
    {
Christophe Massiot's avatar
Christophe Massiot committed
60
        msg_Err( p_aout, "no suitable aout module" );
61
        vlc_mutex_unlock( &p_aout->output_fifo_lock );
62 63
        return -1;
    }
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 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

    if ( var_Type( p_aout, "audio-channels" ) ==
             (VLC_VAR_STRING | VLC_VAR_ISLIST) )
    {
        /* The user may have selected a different channels configuration. */
        vlc_value_t val;
        var_Get( p_aout, "audio-channels", &val );

        if ( !strcmp( val.psz_string, N_("Both") ) )
        {
            p_aout->output.output.i_original_channels =
                AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
        }
        else if ( !strcmp( val.psz_string, N_("Left") ) )
        {
            p_aout->output.output.i_original_channels = AOUT_CHAN_LEFT;
        }
        else if ( !strcmp( val.psz_string, N_("Right") ) )
        {
            p_aout->output.output.i_original_channels = AOUT_CHAN_RIGHT;
        }
        else if ( !strcmp( val.psz_string, N_("Dolby Surround") ) )
        {
            p_aout->output.output.i_original_channels
                = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_DOLBYSTEREO;
        }
        free( val.psz_string );
    }
    else if ( p_aout->output.output.i_physical_channels == AOUT_CHAN_CENTER )
    {
        /* Mono - create the audio-channels variable. */
        vlc_value_t val;
        var_Create( p_aout, "audio-channels", VLC_VAR_STRING | VLC_VAR_ISLIST );
        if ( p_aout->output.output.i_original_channels & AOUT_CHAN_DUALMONO )
        {
            /* Go directly to the left channel. */
            p_aout->output.output.i_original_channels = AOUT_CHAN_LEFT;
        }
        else
        {
            val.psz_string = N_("Both");
            var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val );
        }
        val.psz_string = N_("Left");
        var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val );
        val.psz_string = N_("Right");
        var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val );
        var_AddCallback( p_aout, "audio-channels", aout_ChannelsRestart,
                         NULL );
    }
    else if ( p_aout->output.output.i_physical_channels == 
                 (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)
              && p_aout->output.output.i_original_channels == 
                 (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) )
    {
        /* Stereo - create the audio-channels variable. */
        vlc_value_t val;
        var_Create( p_aout, "audio-channels", VLC_VAR_STRING | VLC_VAR_ISLIST );
        val.psz_string = N_("Both");
        var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val );
        val.psz_string = N_("Left");
        var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val );
        val.psz_string = N_("Right");
        var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val );
        if ( p_aout->output.output.i_original_channels & AOUT_CHAN_DOLBYSTEREO )
        {
            val.psz_string = N_("Dolby Surround");
            var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val );
        }
        p_aout->output.output.i_original_channels &= ~AOUT_CHAN_DOLBYSTEREO;
        var_AddCallback( p_aout, "audio-channels", aout_ChannelsRestart,
                         NULL );
    }

138
    aout_FormatPrepare( &p_aout->output.output );
139

140 141 142
    /* Prepare FIFO. */
    aout_FifoInit( p_aout, &p_aout->output.fifo, p_aout->output.output.i_rate );

143 144
    vlc_mutex_unlock( &p_aout->output_fifo_lock );

145
    aout_FormatPrint( p_aout, "output", &p_aout->output.output );
146 147

    /* Calculate the resulting mixer output format. */
148 149
    memcpy( &p_aout->mixer.mixer, &p_aout->output.output,
            sizeof(audio_sample_format_t) );
150
    if ( !AOUT_FMT_NON_LINEAR(&p_aout->output.output) )
151 152
    {
        /* Non-S/PDIF mixer only deals with float32 or fixed32. */
153
        p_aout->mixer.mixer.i_format
154
                     = (p_aout->p_libvlc->i_cpu & CPU_CAPABILITY_FPU) ?
155 156
                        VLC_FOURCC('f','l','3','2') :
                        VLC_FOURCC('f','i','3','2');
157
        aout_FormatPrepare( &p_aout->mixer.mixer );
158
    }
159 160
    else
    {
161
        p_aout->mixer.mixer.i_format = p_format->i_format;
162 163
    }

164
    aout_FormatPrint( p_aout, "mixer", &p_aout->output.output );
165 166 167 168

    /* Create filters. */
    if ( aout_FiltersCreatePipeline( p_aout, p_aout->output.pp_filters,
                                     &p_aout->output.i_nb_filters,
169
                                     &p_aout->mixer.mixer,
170 171 172 173 174 175 176 177 178 179
                                     &p_aout->output.output ) < 0 )
    {
        msg_Err( p_aout, "couldn't set an output pipeline" );
        module_Unneed( p_aout, p_aout->output.p_module );
        return -1;
    }

    /* Prepare hints for the buffer allocator. */
    p_aout->mixer.output_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
    p_aout->mixer.output_alloc.i_bytes_per_sec
180 181 182
                        = p_aout->mixer.mixer.i_bytes_per_frame
                           * p_aout->mixer.mixer.i_rate
                           / p_aout->mixer.mixer.i_frame_length;
183 184 185 186 187 188 189 190 191 192

    aout_FiltersHintBuffers( p_aout, p_aout->output.pp_filters,
                             p_aout->output.i_nb_filters,
                             &p_aout->mixer.output_alloc );

    return 0;
}

/*****************************************************************************
 * aout_OutputDelete : delete the output
193
 *****************************************************************************
194
 * This function is entered with the mixer lock.
195 196 197 198 199 200 201 202 203 204 205 206
 *****************************************************************************/
void aout_OutputDelete( aout_instance_t * p_aout )
{
    module_Unneed( p_aout, p_aout->output.p_module );

    aout_FiltersDestroyPipeline( p_aout, p_aout->output.pp_filters,
                                 p_aout->output.i_nb_filters );
    aout_FifoDestroy( p_aout, &p_aout->output.fifo );
}

/*****************************************************************************
 * aout_OutputPlay : play a buffer
207
 *****************************************************************************
208
 * This function is entered with the mixer lock.
209 210 211 212 213 214 215
 *****************************************************************************/
void aout_OutputPlay( aout_instance_t * p_aout, aout_buffer_t * p_buffer )
{
    aout_FiltersPlay( p_aout, p_aout->output.pp_filters,
                      p_aout->output.i_nb_filters,
                      &p_buffer );

216
    vlc_mutex_lock( &p_aout->output_fifo_lock );
217 218
    aout_FifoPush( p_aout, &p_aout->output.fifo, p_buffer );
    p_aout->output.pf_play( p_aout );
219
    vlc_mutex_unlock( &p_aout->output_fifo_lock );
220 221 222 223
}

/*****************************************************************************
 * aout_OutputNextBuffer : give the audio output plug-in the right buffer
224 225 226
 *****************************************************************************
 * 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
227
 * compensate it by itself. S/PDIF outputs should always set b_can_sleek = 1.
228
 * This function is entered with no lock at all :-).
229 230
 *****************************************************************************/
aout_buffer_t * aout_OutputNextBuffer( aout_instance_t * p_aout,
231
                                       mtime_t start_date,
232
                                       vlc_bool_t b_can_sleek )
233 234 235
{
    aout_buffer_t * p_buffer;

236
    vlc_mutex_lock( &p_aout->output_fifo_lock );
237

238
    p_buffer = p_aout->output.fifo.p_first;
gbazin's avatar
 
gbazin committed
239
    while ( p_buffer && p_buffer->start_date < mdate() )
240
    {
gbazin's avatar
 
gbazin committed
241 242
        msg_Dbg( p_aout, "audio output is too slow ("I64Fd"), "
                 "trashing "I64Fd"us", mdate() - p_buffer->start_date,
243
                 p_buffer->end_date - p_buffer->start_date );
244 245 246 247 248 249 250
        p_buffer = p_buffer->p_next;
    }

    p_aout->output.fifo.p_first = p_buffer;
    if ( p_buffer == NULL )
    {
        p_aout->output.fifo.pp_last = &p_aout->output.fifo.p_first;
gbazin's avatar
 
gbazin committed
251 252 253 254 255

#if 0 /* This is bad because the audio output might just be trying to fill
       * in it's internal buffers. And anyway, it's up to the audio output
       * to deal with this kind of starvation. */

256
        /* Set date to 0, to allow the mixer to send a new buffer ASAP */
257
        aout_FifoSet( p_aout, &p_aout->output.fifo, 0 );
258 259
        if ( !p_aout->output.b_starving )
            msg_Dbg( p_aout,
260
                 "audio output is starving (no input), playing silence" );
261
        p_aout->output.b_starving = 1;
gbazin's avatar
 
gbazin committed
262 263 264
#endif

        vlc_mutex_unlock( &p_aout->output_fifo_lock );
265 266 267
        return NULL;
    }

268 269
    /* Here we suppose that all buffers have the same duration - this is
     * generally true, and anyway if it's wrong it won't be a disaster. */
270
    if ( p_buffer->start_date > start_date
271
                         + (p_buffer->end_date - p_buffer->start_date) )
272
    {
273
        vlc_mutex_unlock( &p_aout->output_fifo_lock );
274
        if ( !p_aout->output.b_starving )
gbazin's avatar
 
gbazin committed
275 276
            msg_Dbg( p_aout, "audio output is starving ("I64Fd"), "
                     "playing silence", p_buffer->start_date - start_date );
277
        p_aout->output.b_starving = 1;
278 279 280
        return NULL;
    }

281 282
    p_aout->output.b_starving = 0;

283 284 285
    if ( !b_can_sleek &&
          ( (p_buffer->start_date - start_date > AOUT_PTS_TOLERANCE)
             || (start_date - p_buffer->start_date > AOUT_PTS_TOLERANCE) ) )
286 287 288
    {
        /* Try to compensate the drift by doing some resampling. */
        int i;
gbazin's avatar
 
gbazin committed
289 290 291
        mtime_t difference = start_date - p_buffer->start_date;
        msg_Warn( p_aout, "output date isn't PTS date, requesting "
                  "resampling ("I64Fd")", difference );
292

293
        vlc_mutex_lock( &p_aout->input_fifos_lock );
294
        for ( i = 0; i < p_aout->i_nb_inputs; i++ )
295
        {
296 297 298
            aout_fifo_t * p_fifo = &p_aout->pp_inputs[i]->fifo;

            aout_FifoMoveDates( p_aout, p_fifo, difference );
299
        }
300 301

        aout_FifoMoveDates( p_aout, &p_aout->output.fifo, difference );
302
        vlc_mutex_unlock( &p_aout->input_fifos_lock );
303
    }
304 305 306 307 308 309 310

    p_aout->output.fifo.p_first = p_buffer->p_next;
    if ( p_buffer->p_next == NULL )
    {
        p_aout->output.fifo.pp_last = &p_aout->output.fifo.p_first;
    }

311
    vlc_mutex_unlock( &p_aout->output_fifo_lock );
312 313
    return p_buffer;
}