output.c 13.6 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>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
33
#include <vlc_cpu.h>
34
#include <vlc_modules.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
35

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

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

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

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

    if ( var_Type( p_aout, "audio-channels" ) ==
gbazin's avatar
   
gbazin committed
65
             (VLC_VAR_INTEGER | VLC_VAR_HASCHOICE) )
66
67
    {
        /* The user may have selected a different channels configuration. */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
68
        switch( var_InheritInteger( p_aout, "audio-channels" ) )
69
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
            case AOUT_VAR_CHAN_RSTEREO:
                p_aout->output.output.i_original_channels |=
                                                       AOUT_CHAN_REVERSESTEREO;
                break;
            case AOUT_VAR_CHAN_STEREO:
                p_aout->output.output.i_original_channels =
                                              AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
                break;
            case AOUT_VAR_CHAN_LEFT:
                p_aout->output.output.i_original_channels = AOUT_CHAN_LEFT;
                break;
            case AOUT_VAR_CHAN_RIGHT:
                p_aout->output.output.i_original_channels = AOUT_CHAN_RIGHT;
                break;
            case AOUT_VAR_CHAN_DOLBYS:
                p_aout->output.output.i_original_channels =
                      AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_DOLBYSTEREO;
                break;
88
89
        }
    }
90
91
92
    else if ( p_aout->output.output.i_physical_channels == AOUT_CHAN_CENTER
              && (p_aout->output.output.i_original_channels
                   & 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
109
110
111
        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;
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->output.output.i_physical_channels ==
118
119
120
               (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)
                && (p_aout->output.output.i_original_channels &
                     (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
131
        if ( p_aout->output.output.i_original_channels & AOUT_CHAN_DOLBYSTEREO )
        {
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
148
149
150
        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;
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->output.output );
159

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

164
    /* Choose the mixer format. */
165
    p_aout->mixer_format = p_aout->output.output;
166
    if ( AOUT_FMT_NON_LINEAR(&p_aout->output.output) )
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
    p_aout->output.i_nb_filters = 0;
188
189
    if ( aout_FiltersCreatePipeline( p_aout, p_aout->output.pp_filters,
                                     &p_aout->output.i_nb_filters,
190
                                     &p_aout->mixer_format,
191
192
                                     &p_aout->output.output ) < 0 )
    {
zorglub's avatar
zorglub committed
193
        msg_Err( p_aout, "couldn't create audio output pipeline" );
194
        module_unneed( p_aout, p_aout->output.p_module );
195
        p_aout->output.p_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
207
 *****************************************************************************/
void aout_OutputDelete( aout_instance_t * p_aout )
{
208
209
    vlc_assert_locked( &p_aout->lock );

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

213
    module_unneed( p_aout, p_aout->output.p_module );
214
    p_aout->output.p_module = NULL;
215
    aout_FiltersDestroyPipeline( p_aout->output.pp_filters,
216
                                 p_aout->output.i_nb_filters );
217
    aout_FifoDestroy( &p_aout->output.fifo );
218
219
220
221
}

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

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

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

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

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

256
257
/*****************************************************************************
 * aout_OutputNextBuffer : give the audio output plug-in the right buffer
258
259
260
 *****************************************************************************
 * 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
261
 * compensate it by itself. S/PDIF outputs should always set b_can_sleek = 1.
262
 * This function is entered with no lock at all :-).
263
264
 *****************************************************************************/
aout_buffer_t * aout_OutputNextBuffer( aout_instance_t * p_aout,
265
                                       mtime_t start_date,
266
                                       bool b_can_sleek )
267
{
268
    aout_fifo_t *p_fifo = &p_aout->output.fifo;
269
    aout_buffer_t * p_buffer;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
270
    mtime_t now = mdate();
271

272
    aout_lock( p_aout );
273

274
275
276
    /* 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. */
277
278
    while( ((p_buffer = p_fifo->p_first) != NULL)
     && p_buffer->i_pts < (b_can_sleek ? start_date : now) - AOUT_PTS_TOLERANCE )
279
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
280
        msg_Dbg( p_aout, "audio output is too slow (%"PRId64"), "
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
281
                 "trashing %"PRId64"us", now - p_buffer->i_pts,
282
                 p_buffer->i_length );
283
        aout_BufferFree( aout_FifoPop( p_fifo ) );
284
285
    }

286
    if( p_buffer == NULL )
287
    {
gbazin's avatar
   
gbazin committed
288
#if 0 /* This is bad because the audio output might just be trying to fill
289
       * in its internal buffers. And anyway, it's up to the audio output
gbazin's avatar
   
gbazin committed
290
291
       * to deal with this kind of starvation. */

292
        /* Set date to 0, to allow the mixer to send a new buffer ASAP */
293
        aout_FifoSet( &p_aout->output.fifo, 0 );
294
295
        if ( !p_aout->output.b_starving )
            msg_Dbg( p_aout,
296
                 "audio output is starving (no input), playing silence" );
297
        p_aout->output.b_starving = true;
gbazin's avatar
   
gbazin committed
298
#endif
299
        goto out;
300
301
    }

302
    mtime_t delta = start_date - p_buffer->i_pts;
303
    /* Here we suppose that all buffers have the same duration - this is
304
305
     * generally true, and anyway if it's wrong it won't be a disaster.
     */
306
    if ( 0 > delta + p_buffer->i_length )
gbazin's avatar
   
gbazin committed
307
308
309
310
311
312
    /*
     *                   + AOUT_PTS_TOLERANCE )
     * There is no reason to want that, it just worsen the scheduling of
     * an audio sample after an output starvation (ie. on start or on resume)
     * --Gibalou
     */
313
    {
314
        if ( !p_aout->output.b_starving )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
315
            msg_Dbg( p_aout, "audio output is starving (%"PRId64"), "
316
317
                     "playing silence", -delta );
        p_aout->output.b_starving = true;
318
319
        p_buffer = NULL;
        goto out;
320
321
    }

322
323
    p_aout->output.b_starving = false;
    p_buffer = aout_FifoPop( p_fifo );
324

325
    if( !b_can_sleek )
326
    {
327
        if( delta > AOUT_PTS_TOLERANCE || delta < -AOUT_PTS_TOLERANCE )
328
        {
329
330
            /* Try to compensate the drift by doing some resampling. */
            msg_Warn( p_aout, "output date isn't PTS date, requesting "
331
                      "resampling (%"PRId64")", delta );
332

333
334
            aout_FifoMoveDates( &p_aout->p_input->mixer.fifo, delta );
            aout_FifoMoveDates( p_fifo, delta );
335
336
        }
    }
337
338
out:
    aout_unlock( p_aout );
339
340
    return p_buffer;
}