input.c 34.3 KB
Newer Older
1
2
3
/*****************************************************************************
 * input.c : internal management of input streams for the audio output
 *****************************************************************************
4
 * Copyright (C) 2002-2007 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
31
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

32
33
34
#include <vlc/vlc.h>

#include <stdio.h>
35
#include <string.h>
36
#include <math.h>
37

zorglub's avatar
zorglub committed
38
#include <vlc_input.h>                 /* for input_thread_t and i_pts_delay */
39

40
41
42
#ifdef HAVE_ALLOCA_H
#   include <alloca.h>
#endif
zorglub's avatar
zorglub committed
43
#include <vlc_aout.h>
44

45
46
#include "aout_internal.h"

zorglub's avatar
zorglub committed
47
48
49
/** FIXME: Ugly but needed to access the counters */
#include "input/input_internal.h"

50
static void inputFailure( aout_instance_t *, aout_input_t *, const char * );
51
52
53
static void inputDrop( aout_instance_t *, aout_input_t *, aout_buffer_t * );
static void inputResamplingStop( aout_input_t *p_input );

54
static int VisualizationCallback( vlc_object_t *, char const *,
55
56
57
                                  vlc_value_t, vlc_value_t, void * );
static int EqualizerCallback( vlc_object_t *, char const *,
                              vlc_value_t, vlc_value_t, void * );
58
59
60
static int ReplayGainCallback( vlc_object_t *, char const *,
                               vlc_value_t, vlc_value_t, void * );
static void ReplayGainSelect( aout_instance_t *, aout_input_t * );
61
62
63
/*****************************************************************************
 * aout_InputNew : allocate a new input and rework the filter pipeline
 *****************************************************************************/
64
int aout_InputNew( aout_instance_t * p_aout, aout_input_t * p_input )
65
{
66
67
    audio_sample_format_t chain_input_format;
    audio_sample_format_t chain_output_format;
68
    vlc_value_t val, text;
zorglub's avatar
zorglub committed
69
    char * psz_filters, *psz_visual;
70
    int i_visual;
71

72
73
    aout_FormatPrint( p_aout, "input", &p_input->input );

74
    p_input->i_nb_resamplers = p_input->i_nb_filters = 0;
75

76
    /* Prepare FIFO. */
77
    aout_FifoInit( p_aout, &p_input->fifo, p_aout->mixer.mixer.i_rate );
78
79
    p_input->p_first_byte_to_mix = NULL;

80
    /* Prepare format structure */
81
    memcpy( &chain_input_format, &p_input->input,
82
            sizeof(audio_sample_format_t) );
83
84
85
86
    memcpy( &chain_output_format, &p_aout->mixer.mixer,
            sizeof(audio_sample_format_t) );
    chain_output_format.i_rate = p_input->input.i_rate;
    aout_FormatPrepare( &chain_output_format );
zorglub's avatar
zorglub committed
87

88
    /* Now add user filters */
gbazin's avatar
   
gbazin committed
89
90
91
92
93
    if( var_Type( p_aout, "visual" ) == 0 )
    {
        var_Create( p_aout, "visual", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
        text.psz_string = _("Visualizations");
        var_Change( p_aout, "visual", VLC_VAR_SETTEXT, &text, NULL );
94
        val.psz_string = (char*)""; text.psz_string = _("Disable");
gbazin's avatar
   
gbazin committed
95
        var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
96
        val.psz_string = (char*)"spectrometer"; text.psz_string = _("Spectrometer");
gbazin's avatar
   
gbazin committed
97
        var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
98
        val.psz_string = (char*)"scope"; text.psz_string = _("Scope");
gbazin's avatar
   
gbazin committed
99
        var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
100
        val.psz_string = (char*)"spectrum"; text.psz_string = _("Spectrum");
gbazin's avatar
   
gbazin committed
101
        var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
102
103

        /* Look for goom plugin */
104
        if( module_Exists( VLC_OBJECT(p_aout), "goom" ) )
105
        {
106
            val.psz_string = (char*)"goom"; text.psz_string = (char*)"Goom";
107
108
109
            var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
        }

110
        /* Look for galaktos plugin */
111
        if( module_Exists( VLC_OBJECT(p_aout), "galaktos" ) )
112
        {
113
            val.psz_string = (char*)"galaktos"; text.psz_string = (char*)"GaLaktos";
114
115
116
            var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
        }

gbazin's avatar
   
gbazin committed
117
118
119
        if( var_Get( p_aout, "effect-list", &val ) == VLC_SUCCESS )
        {
            var_Set( p_aout, "visual", val );
120
            free( val.psz_string );
gbazin's avatar
   
gbazin committed
121
122
123
124
        }
        var_AddCallback( p_aout, "visual", VisualizationCallback, NULL );
    }

125
126
127
    if( var_Type( p_aout, "equalizer" ) == 0 )
    {
        module_config_t *p_config;
zorglub's avatar
zorglub committed
128
129
130
131
132
133
134
135
136
137
        int i;

        p_config = config_FindConfig( VLC_OBJECT(p_aout), "equalizer-preset" );
        if( p_config && p_config->i_list )
        {
               var_Create( p_aout, "equalizer",
                           VLC_VAR_STRING | VLC_VAR_HASCHOICE );
            text.psz_string = _("Equalizer");
            var_Change( p_aout, "equalizer", VLC_VAR_SETTEXT, &text, NULL );

138
            val.psz_string = (char*)""; text.psz_string = _("Disable");
zorglub's avatar
zorglub committed
139
140
141
142
            var_Change( p_aout, "equalizer", VLC_VAR_ADDCHOICE, &val, &text );

            for( i = 0; i < p_config->i_list; i++ )
            {
143
144
                val.psz_string = (char *)p_config->ppsz_list[i];
                text.psz_string = (char *)p_config->ppsz_list_text[i];
zorglub's avatar
zorglub committed
145
146
147
148
149
150
                var_Change( p_aout, "equalizer", VLC_VAR_ADDCHOICE,
                            &val, &text );
            }

            var_AddCallback( p_aout, "equalizer", EqualizerCallback, NULL );
        }
151
152
    }

gbazin's avatar
   
gbazin committed
153
154
155
156
157
158
159
    if( var_Type( p_aout, "audio-filter" ) == 0 )
    {
        var_Create( p_aout, "audio-filter",
                    VLC_VAR_STRING | VLC_VAR_DOINHERIT );
        text.psz_string = _("Audio filters");
        var_Change( p_aout, "audio-filter", VLC_VAR_SETTEXT, &text, NULL );
    }
zorglub's avatar
zorglub committed
160
161
162
163
164
165
166
    if( var_Type( p_aout, "audio-visual" ) == 0 )
    {
        var_Create( p_aout, "audio-visual",
                    VLC_VAR_STRING | VLC_VAR_DOINHERIT );
        text.psz_string = _("Audio visualizations");
        var_Change( p_aout, "audio-visual", VLC_VAR_SETTEXT, &text, NULL );
    }
gbazin's avatar
   
gbazin committed
167

168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
    if( var_Type( p_aout, "audio-replay-gain-mode" ) == 0 )
    {
        module_config_t *p_config;
        int i;

        p_config = config_FindConfig( VLC_OBJECT(p_aout), "audio-replay-gain-mode" );
        if( p_config && p_config->i_list )
        {
            var_Create( p_aout, "audio-replay-gain-mode",
                        VLC_VAR_STRING | VLC_VAR_DOINHERIT );

            text.psz_string = _("Replay gain");
            var_Change( p_aout, "audio-replay-gain-mode", VLC_VAR_SETTEXT, &text, NULL );

            for( i = 0; i < p_config->i_list; i++ )
            {
                val.psz_string = (char *)p_config->ppsz_list[i];
                text.psz_string = (char *)p_config->ppsz_list_text[i];
                var_Change( p_aout, "audio-replay-gain-mode", VLC_VAR_ADDCHOICE,
                            &val, &text );
            }

            var_AddCallback( p_aout, "audio-replay-gain-mode", ReplayGainCallback, NULL );
        }
    }
    if( var_Type( p_aout, "audio-replay-gain-preamp" ) == 0 )
    {
        var_Create( p_aout, "audio-replay-gain-preamp",
                    VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
    }
    if( var_Type( p_aout, "audio-replay-gain-default" ) == 0 )
    {
        var_Create( p_aout, "audio-replay-gain-default",
                    VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
    }
    if( var_Type( p_aout, "audio-replay-gain-peak-protection" ) == 0 )
    {
        var_Create( p_aout, "audio-replay-gain-peak-protection",
                    VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
    }

gbazin's avatar
   
gbazin committed
209
210
    var_Get( p_aout, "audio-filter", &val );
    psz_filters = val.psz_string;
zorglub's avatar
zorglub committed
211
212
213
    var_Get( p_aout, "audio-visual", &val );
    psz_visual = val.psz_string;

214
215
    /* parse user filter lists */
    for( i_visual = 0; i_visual < 2; i_visual++ )
zorglub's avatar
zorglub committed
216
    {
217
218
        char *psz_next = NULL;
        char *psz_parser = i_visual ? psz_visual : psz_filters;
zorglub's avatar
zorglub committed
219

220
221
        if( psz_parser == NULL || !*psz_parser )
            continue;
zorglub's avatar
zorglub committed
222

223
        while( psz_parser && *psz_parser )
zorglub's avatar
zorglub committed
224
        {
225
            aout_filter_t * p_filter = NULL;
226
227
228

            if( p_input->i_nb_filters >= AOUT_MAX_FILTERS )
            {
zorglub's avatar
zorglub committed
229
                msg_Dbg( p_aout, "max filters reached (%d)", AOUT_MAX_FILTERS );
230
                break;
zorglub's avatar
zorglub committed
231
232
            }

233
            while( *psz_parser == ' ' && *psz_parser == ':' )
234
235
236
            {
                psz_parser++;
            }
237
            if( ( psz_next = strchr( psz_parser , ':'  ) ) )
238
239
240
241
242
243
            {
                *psz_next++ = '\0';
            }
            if( *psz_parser =='\0' )
            {
                break;
zorglub's avatar
zorglub committed
244
245
            }

246
247
248
249
            /* Create a VLC object */
            p_filter = vlc_object_create( p_aout, sizeof(aout_filter_t) );
            if( p_filter == NULL )
            {
gbazin's avatar
   
gbazin committed
250
251
                msg_Err( p_aout, "cannot add user filter %s (skipped)",
                         psz_parser );
252
253
254
                psz_parser = psz_next;
                continue;
            }
255

256
            vlc_object_attach( p_filter , p_aout );
257
258

            /* try to find the requested filter */
259
260
261
262
263
264
265
            if( i_visual == 1 ) /* this can only be a visualization module */
            {
                /* request format */
                memcpy( &p_filter->input, &chain_output_format,
                        sizeof(audio_sample_format_t) );
                memcpy( &p_filter->output, &chain_output_format,
                        sizeof(audio_sample_format_t) );
zorglub's avatar
zorglub committed
266

267
268
269
270
271
272
273
274
275
276
277
278
                p_filter->p_module = module_Need( p_filter, "visualization",
                                                  psz_parser, VLC_TRUE );
            }
            else /* this can be a audio filter module as well as a visualization module */
            {
                /* request format */
                memcpy( &p_filter->input, &chain_input_format,
                        sizeof(audio_sample_format_t) );
                memcpy( &p_filter->output, &chain_output_format,
                        sizeof(audio_sample_format_t) );

                p_filter->p_module = module_Need( p_filter, "audio filter",
279
280
                                              psz_parser, VLC_TRUE );

281
                if ( p_filter->p_module == NULL )
282
                {
283
284
285
286
287
288
289
290
                    /* if the filter requested a special format, retry */
                    if ( !( AOUT_FMTS_IDENTICAL( &p_filter->input,
                                                 &chain_input_format )
                            && AOUT_FMTS_IDENTICAL( &p_filter->output,
                                                    &chain_output_format ) ) )
                    {
                        aout_FormatPrepare( &p_filter->input );
                        aout_FormatPrepare( &p_filter->output );
zorglub's avatar
zorglub committed
291
292
                        p_filter->p_module = module_Need( p_filter,
                                                          "audio filter",
293
294
295
296
297
298
299
300
301
                                                          psz_parser, VLC_TRUE );
                    }
                    /* try visual filters */
                    else
                    {
                        memcpy( &p_filter->input, &chain_output_format,
                                sizeof(audio_sample_format_t) );
                        memcpy( &p_filter->output, &chain_output_format,
                                sizeof(audio_sample_format_t) );
zorglub's avatar
zorglub committed
302
303
                        p_filter->p_module = module_Need( p_filter,
                                                          "visualization",
304
305
                                                          psz_parser, VLC_TRUE );
                    }
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
                }
            }

            /* failure */
            if ( p_filter->p_module == NULL )
            {
                msg_Err( p_aout, "cannot add user filter %s (skipped)",
                         psz_parser );

                vlc_object_detach( p_filter );
                vlc_object_destroy( p_filter );

                psz_parser = psz_next;
                continue;
            }
321

322
323
            /* complete the filter chain if necessary */
            if ( !AOUT_FMTS_IDENTICAL( &chain_input_format, &p_filter->input ) )
324
            {
325
326
327
328
                if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_filters,
                                                 &p_input->i_nb_filters,
                                                 &chain_input_format,
                                                 &p_filter->input ) < 0 )
zorglub's avatar
zorglub committed
329
330
331
332
                {
                    msg_Err( p_aout, "cannot add user filter %s (skipped)",
                             psz_parser );

333
                    module_Unneed( p_filter, p_filter->p_module );
zorglub's avatar
zorglub committed
334
335
                    vlc_object_detach( p_filter );
                    vlc_object_destroy( p_filter );
336

zorglub's avatar
zorglub committed
337
338
339
                    psz_parser = psz_next;
                    continue;
                }
340
341
            }

342
343
            /* success */
            p_filter->b_continuity = VLC_FALSE;
344
            p_input->pp_filters[p_input->i_nb_filters++] = p_filter;
345
346
            memcpy( &chain_input_format, &p_filter->output,
                    sizeof( audio_sample_format_t ) );
347
348
349
350

            /* next filter if any */
            psz_parser = psz_next;
        }
351
    }
gbazin's avatar
   
gbazin committed
352
    if( psz_filters ) free( psz_filters );
zorglub's avatar
zorglub committed
353
    if( psz_visual ) free( psz_visual );
354

355
356
    /* complete the filter chain if necessary */
    if ( !AOUT_FMTS_IDENTICAL( &chain_input_format, &chain_output_format ) )
Boris Dorès's avatar
Boris Dorès committed
357
    {
358
359
360
361
362
363
364
365
        if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_filters,
                                         &p_input->i_nb_filters,
                                         &chain_input_format,
                                         &chain_output_format ) < 0 )
        {
            inputFailure( p_aout, p_input, "couldn't set an input pipeline" );
            return -1;
        }
Boris Dorès's avatar
Boris Dorès committed
366
367
    }

368
369
370
371
    /* Prepare hints for the buffer allocator. */
    p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
    p_input->input_alloc.i_bytes_per_sec = -1;

372
    /* Create resamplers. */
373
    if ( !AOUT_FMT_NON_LINEAR( &p_aout->mixer.mixer ) )
374
    {
375
        chain_output_format.i_rate = (__MAX(p_input->input.i_rate,
376
                                            p_aout->mixer.mixer.i_rate)
377
                                 * (100 + AOUT_MAX_RESAMPLING)) / 100;
378
        if ( chain_output_format.i_rate == p_aout->mixer.mixer.i_rate )
379
380
        {
            /* Just in case... */
381
            chain_output_format.i_rate++;
382
383
384
        }
        if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_resamplers,
                                         &p_input->i_nb_resamplers,
385
                                         &chain_output_format,
386
387
                                         &p_aout->mixer.mixer ) < 0 )
        {
388
            inputFailure( p_aout, p_input, "couldn't set a resampler pipeline");
389
390
391
            return -1;
        }

gbazin's avatar
   
gbazin committed
392
393
394
        aout_FiltersHintBuffers( p_aout, p_input->pp_resamplers,
                                 p_input->i_nb_resamplers,
                                 &p_input->input_alloc );
395
        p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
gbazin's avatar
   
gbazin committed
396

gbazin's avatar
   
gbazin committed
397
398
        /* Setup the initial rate of the resampler */
        p_input->pp_resamplers[0]->input.i_rate = p_input->input.i_rate;
399
    }
400
    p_input->i_resampling_type = AOUT_RESAMPLING_NONE;
401

402
403
404
    aout_FiltersHintBuffers( p_aout, p_input->pp_filters,
                             p_input->i_nb_filters,
                             &p_input->input_alloc );
405
    p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
406
407
408
409

    /* i_bytes_per_sec is still == -1 if no filters */
    p_input->input_alloc.i_bytes_per_sec = __MAX(
                                    p_input->input_alloc.i_bytes_per_sec,
gbazin's avatar
   
gbazin committed
410
                                    (int)(p_input->input.i_bytes_per_frame
411
                                     * p_input->input.i_rate
gbazin's avatar
   
gbazin committed
412
                                     / p_input->input.i_frame_length) );
413
414
415

    ReplayGainSelect( p_aout, p_input );

416
    /* Success */
417
418
    p_input->b_error = VLC_FALSE;
    p_input->b_restart = VLC_FALSE;
419
    p_input->i_last_input_rate = INPUT_RATE_DEFAULT;
420

421
    return 0;
422
423
424
425
}

/*****************************************************************************
 * aout_InputDelete : delete an input
426
 *****************************************************************************
427
 * This function must be entered with the mixer lock.
428
 *****************************************************************************/
429
int aout_InputDelete( aout_instance_t * p_aout, aout_input_t * p_input )
430
{
431
432
    if ( p_input->b_error ) return 0;

433
434
    aout_FiltersDestroyPipeline( p_aout, p_input->pp_filters,
                                 p_input->i_nb_filters );
435
    p_input->i_nb_filters = 0;
436
437
    aout_FiltersDestroyPipeline( p_aout, p_input->pp_resamplers,
                                 p_input->i_nb_resamplers );
438
    p_input->i_nb_resamplers = 0;
439
440
441
442
443
    aout_FifoDestroy( p_aout, &p_input->fifo );

    return 0;
}

444
445
/*****************************************************************************
 * aout_InputPlay : play a buffer
446
447
 *****************************************************************************
 * This function must be entered with the input lock.
448
 *****************************************************************************/
449
450
/* XXX Do not activate it !! */
//#define AOUT_PROCESS_BEFORE_CHEKS
451
int aout_InputPlay( aout_instance_t * p_aout, aout_input_t * p_input,
452
                    aout_buffer_t * p_buffer, int i_input_rate )
453
{
gbazin's avatar
   
gbazin committed
454
    mtime_t start_date;
455

456
457
    if( p_input->b_restart )
    {
458
        aout_fifo_t fifo, dummy_fifo;
459
        byte_t      *p_first_byte_to_mix;
460

461
        vlc_mutex_lock( &p_aout->mixer_lock );
462
463
464
465
466
467

        /* A little trick to avoid loosing our input fifo */
        aout_FifoInit( p_aout, &dummy_fifo, p_aout->mixer.mixer.i_rate );
        p_first_byte_to_mix = p_input->p_first_byte_to_mix;
        fifo = p_input->fifo;
        p_input->fifo = dummy_fifo;
468
469
        aout_InputDelete( p_aout, p_input );
        aout_InputNew( p_aout, p_input );
470
471
472
        p_input->p_first_byte_to_mix = p_first_byte_to_mix;
        p_input->fifo = fifo;

473
474
475
        vlc_mutex_unlock( &p_aout->mixer_lock );
    }

476
477
478
479
480
481
    if( i_input_rate != INPUT_RATE_DEFAULT && p_input->i_nb_resamplers <= 0 )
    {
        inputDrop( p_aout, p_input, p_buffer );
        return 0;
    }

482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
#ifdef AOUT_PROCESS_BEFORE_CHEKS
    /* Run pre-filters. */
    aout_FiltersPlay( p_aout, p_input->pp_filters, p_input->i_nb_filters,
                      &p_buffer );

    /* Actually run the resampler now. */
    if ( p_input->i_nb_resamplers > 0 )
    {
        const mtime_t i_date = p_buffer->start_date;
        aout_FiltersPlay( p_aout, p_input->pp_resamplers,
                          p_input->i_nb_resamplers,
                          &p_buffer );
    }

    if( p_buffer->i_nb_samples <= 0 )
    {
        aout_BufferFree( p_buffer );
        return 0;
    }
#endif

    /* Handle input rate change by modifying resampler input rate */
    if( i_input_rate != p_input->i_last_input_rate )
    {
        unsigned int * const pi_rate = &p_input->pp_resamplers[0]->input.i_rate;
#define F(r,ir) ( INPUT_RATE_DEFAULT * (r) / (ir) )
        const int i_delta = *pi_rate - F(p_input->input.i_rate,p_input->i_last_input_rate);
        *pi_rate = F(p_input->input.i_rate + i_delta, i_input_rate);
#undef F
        p_input->i_last_input_rate = i_input_rate;
    }

514
    /* We don't care if someone changes the start date behind our back after
515
516
     * this. We'll deal with that when pushing the buffer, and compensate
     * with the next incoming buffer. */
517
    vlc_mutex_lock( &p_aout->input_fifos_lock );
518
    start_date = aout_FifoNextStart( p_aout, &p_input->fifo );
519
    vlc_mutex_unlock( &p_aout->input_fifos_lock );
520
521
522
523
524
525

    if ( start_date != 0 && start_date < mdate() )
    {
        /* The decoder is _very_ late. This can only happen if the user
         * pauses the stream (or if the decoder is buggy, which cannot
         * happen :). */
gbazin's avatar
   
gbazin committed
526
527
        msg_Warn( p_aout, "computed PTS is out of range ("I64Fd"), "
                  "clearing out", mdate() - start_date );
528
        vlc_mutex_lock( &p_aout->input_fifos_lock );
529
        aout_FifoSet( p_aout, &p_input->fifo, 0 );
gbazin's avatar
   
gbazin committed
530
        p_input->p_first_byte_to_mix = NULL;
531
        vlc_mutex_unlock( &p_aout->input_fifos_lock );
gbazin's avatar
   
gbazin committed
532
533
        if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE )
            msg_Warn( p_aout, "timing screwed, stopping resampling" );
534
        inputResamplingStop( p_input );
535
        start_date = 0;
gbazin's avatar
   
gbazin committed
536
    }
537

538
    if ( p_buffer->start_date < mdate() + AOUT_MIN_PREPARE_TIME )
539
540
541
    {
        /* The decoder gives us f*cked up PTS. It's its business, but we
         * can't present it anyway, so drop the buffer. */
gbazin's avatar
   
gbazin committed
542
        msg_Warn( p_aout, "PTS is out of range ("I64Fd"), dropping buffer",
543
                  mdate() - p_buffer->start_date );
544

545
546
        inputDrop( p_aout, p_input, p_buffer );
        inputResamplingStop( p_input );
547
        return 0;
548
549
    }

550
551
552
553
554
555
556
557
558
559
560
561
562
    /* If the audio drift is too big then it's not worth trying to resample
     * the audio. */
    if ( start_date != 0 &&
         ( start_date < p_buffer->start_date - 3 * AOUT_PTS_TOLERANCE ) )
    {
        msg_Warn( p_aout, "audio drift is too big ("I64Fd"), clearing out",
                  start_date - p_buffer->start_date );
        vlc_mutex_lock( &p_aout->input_fifos_lock );
        aout_FifoSet( p_aout, &p_input->fifo, 0 );
        p_input->p_first_byte_to_mix = NULL;
        vlc_mutex_unlock( &p_aout->input_fifos_lock );
        if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE )
            msg_Warn( p_aout, "timing screwed, stopping resampling" );
563
        inputResamplingStop( p_input );
564
565
566
567
568
569
570
        start_date = 0;
    }
    else if ( start_date != 0 &&
              ( start_date > p_buffer->start_date + 3 * AOUT_PTS_TOLERANCE ) )
    {
        msg_Warn( p_aout, "audio drift is too big ("I64Fd"), dropping buffer",
                  start_date - p_buffer->start_date );
571
        inputDrop( p_aout, p_input, p_buffer );
572
573
574
        return 0;
    }

575
576
    if ( start_date == 0 ) start_date = p_buffer->start_date;

577
#ifndef AOUT_PROCESS_BEFORE_CHEKS
578
579
580
    /* Run pre-filters. */
    aout_FiltersPlay( p_aout, p_input->pp_filters, p_input->i_nb_filters,
                      &p_buffer );
581
#endif
582

gbazin's avatar
   
gbazin committed
583
584
585
586
    /* Run the resampler if needed.
     * We first need to calculate the output rate of this resampler. */
    if ( ( p_input->i_resampling_type == AOUT_RESAMPLING_NONE ) &&
         ( start_date < p_buffer->start_date - AOUT_PTS_TOLERANCE
587
           || start_date > p_buffer->start_date + AOUT_PTS_TOLERANCE ) &&
Christophe Massiot's avatar
Christophe Massiot committed
588
         p_input->i_nb_resamplers > 0 )
589
590
591
592
593
594
595
596
597
598
    {
        /* Can happen in several circumstances :
         * 1. A problem at the input (clock drift)
         * 2. A small pause triggered by the user
         * 3. Some delay in the output stage, causing a loss of lip
         *    synchronization
         * Solution : resample the buffer to avoid a scratch.
         */
        mtime_t drift = p_buffer->start_date - start_date;

gbazin's avatar
   
gbazin committed
599
        p_input->i_resamp_start_date = mdate();
600
        p_input->i_resamp_start_drift = (int)drift;
gbazin's avatar
   
gbazin committed
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619

        if ( drift > 0 )
            p_input->i_resampling_type = AOUT_RESAMPLING_DOWN;
        else
            p_input->i_resampling_type = AOUT_RESAMPLING_UP;

        msg_Warn( p_aout, "buffer is "I64Fd" %s, triggering %ssampling",
                          drift > 0 ? drift : -drift,
                          drift > 0 ? "in advance" : "late",
                          drift > 0 ? "down" : "up");
    }

    if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE )
    {
        /* Resampling has been triggered previously (because of dates
         * mismatch). We want the resampling to happen progressively so
         * it isn't too audible to the listener. */

        if( p_input->i_resampling_type == AOUT_RESAMPLING_UP )
620
        {
621
            p_input->pp_resamplers[0]->input.i_rate += 2; /* Hz */
622
        }
gbazin's avatar
   
gbazin committed
623
        else
624
        {
625
            p_input->pp_resamplers[0]->input.i_rate -= 2; /* Hz */
626
627
        }

gbazin's avatar
   
gbazin committed
628
629
        /* Check if everything is back to normal, in which case we can stop the
         * resampling */
630
        if( p_input->pp_resamplers[0]->input.i_rate == 1000 * p_input->input.i_rate / i_input_rate )
gbazin's avatar
   
gbazin committed
631
632
        {
            p_input->i_resampling_type = AOUT_RESAMPLING_NONE;
633
634
635
636
            msg_Warn( p_aout, "resampling stopped after "I64Fi" usec "
                      "(drift: "I64Fi")",
                      mdate() - p_input->i_resamp_start_date,
                      p_buffer->start_date - start_date);
gbazin's avatar
   
gbazin committed
637
        }
638
        else if( abs( (int)(p_buffer->start_date - start_date) ) <
gbazin's avatar
   
gbazin committed
639
                 abs( p_input->i_resamp_start_drift ) / 2 )
640
        {
gbazin's avatar
   
gbazin committed
641
642
643
644
645
646
647
648
649
            /* if we reduced the drift from half, then it is time to switch
             * back the resampling direction. */
            if( p_input->i_resampling_type == AOUT_RESAMPLING_UP )
                p_input->i_resampling_type = AOUT_RESAMPLING_DOWN;
            else
                p_input->i_resampling_type = AOUT_RESAMPLING_UP;
            p_input->i_resamp_start_drift = 0;
        }
        else if( p_input->i_resamp_start_drift &&
650
                 ( abs( (int)(p_buffer->start_date - start_date) ) >
gbazin's avatar
   
gbazin committed
651
                   abs( p_input->i_resamp_start_drift ) * 3 / 2 ) )
gbazin's avatar
   
gbazin committed
652
653
654
655
        {
            /* If the drift is increasing and not decreasing, than something
             * is bad. We'd better stop the resampling right now. */
            msg_Warn( p_aout, "timing screwed, stopping resampling" );
656
            inputResamplingStop( p_input );
657
        }
658
    }
659

660
#ifndef AOUT_PROCESS_BEFORE_CHEKS
gbazin's avatar
   
gbazin committed
661
    /* Actually run the resampler now. */
gbazin's avatar
   
gbazin committed
662
    if ( p_input->i_nb_resamplers > 0 )
gbazin's avatar
   
gbazin committed
663
664
665
666
667
668
    {
        aout_FiltersPlay( p_aout, p_input->pp_resamplers,
                          p_input->i_nb_resamplers,
                          &p_buffer );
    }

669
670
671
672
673
674
675
676
677
678
679
680
    if( p_buffer->i_nb_samples <= 0 )
    {
        aout_BufferFree( p_buffer );
        return 0;
    }
#endif

    /* Adding the start date will be managed by aout_FifoPush(). */
    p_buffer->end_date = start_date +
        (p_buffer->end_date - p_buffer->start_date);
    p_buffer->start_date = start_date;

681
    vlc_mutex_lock( &p_aout->input_fifos_lock );
682
    aout_FifoPush( p_aout, &p_input->fifo, p_buffer );
683
    vlc_mutex_unlock( &p_aout->input_fifos_lock );
684
    return 0;
685
}
686

687
688
689
690
691
/*****************************************************************************
 * static functions
 *****************************************************************************/

static void inputFailure( aout_instance_t * p_aout, aout_input_t * p_input,
692
                          const char * psz_error_message )
693
694
{
    /* error message */
695
    msg_Err( p_aout, "%s", psz_error_message );
696
697
698
699
700
701
702
703
704
705
706
707

    /* clean up */
    aout_FiltersDestroyPipeline( p_aout, p_input->pp_filters,
                                 p_input->i_nb_filters );
    aout_FiltersDestroyPipeline( p_aout, p_input->pp_resamplers,
                                 p_input->i_nb_resamplers );
    aout_FifoDestroy( p_aout, &p_input->fifo );
    var_Destroy( p_aout, "visual" );
    var_Destroy( p_aout, "equalizer" );
    var_Destroy( p_aout, "audio-filter" );
    var_Destroy( p_aout, "audio-visual" );

708
709
710
711
712
    var_Destroy( p_aout, "audio-replay-gain-mode" );
    var_Destroy( p_aout, "audio-replay-gain-default" );
    var_Destroy( p_aout, "audio-replay-gain-preamp" );
    var_Destroy( p_aout, "audio-replay-gain-peak-protection" );

713
714
715
716
    /* error flag */
    p_input->b_error = 1;
}

717
static void inputDrop( aout_instance_t *p_aout, aout_input_t *p_input, aout_buffer_t *p_buffer )
718
{
719
720
    aout_BufferFree( p_buffer );

721
722
723
724
725
726
727
728
    if( !p_input->p_input_thread )
        return;

    vlc_mutex_lock( &p_input->p_input_thread->p->counters.counters_lock);
    stats_UpdateInteger( p_aout, p_input->p_input_thread->p->counters.p_lost_abuffers, 1, NULL );
    vlc_mutex_unlock( &p_input->p_input_thread->p->counters.counters_lock);
}

729
730
731
732
733
734
735
736
737
738
739
static void inputResamplingStop( aout_input_t *p_input )
{
    p_input->i_resampling_type = AOUT_RESAMPLING_NONE;
    if( p_input->i_nb_resamplers != 0 )
    {
        p_input->pp_resamplers[0]->input.i_rate = INPUT_RATE_DEFAULT *
                            p_input->input.i_rate / p_input->i_last_input_rate;
        p_input->pp_resamplers[0]->b_continuity = VLC_FALSE;
    }
}

740
741
static int ChangeFiltersString( aout_instance_t * p_aout, const char* psz_variable,
                                 const char *psz_name, vlc_bool_t b_add )
742
743
744
745
{
    vlc_value_t val;
    char *psz_parser;

746
    var_Get( p_aout, psz_variable, &val );
747
748
749
750
751
752
753
754
755
756

    if( !val.psz_string ) val.psz_string = strdup("");

    psz_parser = strstr( val.psz_string, psz_name );

    if( b_add )
    {
        if( !psz_parser )
        {
            psz_parser = val.psz_string;
757
            asprintf( &val.psz_string, (*val.psz_string) ? "%s:%s" : "%s%s",
758
759
760
                      val.psz_string, psz_name );
            free( psz_parser );
        }
761
762
763
764
        else
        {
            return 0;
        }
765
766
767
768
769
770
    }
    else
    {
        if( psz_parser )
        {
            memmove( psz_parser, psz_parser + strlen(psz_name) +
771
                     (*(psz_parser + strlen(psz_name)) == ':' ? 1 : 0 ),
772
773
774
775
776
                     strlen(psz_parser + strlen(psz_name)) + 1 );
        }
        else
        {
            free( val.psz_string );
777
            return 0;
778
779
780
        }
    }

781
    var_Set( p_aout, psz_variable, val );
782
    free( val.psz_string );
783
    return 1;
784
785
}

786
787
788
789
790
static int VisualizationCallback( vlc_object_t *p_this, char const *psz_cmd,
                       vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    aout_instance_t *p_aout = (aout_instance_t *)p_this;
    char *psz_mode = newval.psz_string;
791
792
    vlc_value_t val;
    int i;
793
    (void)psz_cmd; (void)oldval; (void)p_data;
794
795
796

    if( !psz_mode || !*psz_mode )
    {
797
798
799
        ChangeFiltersString( p_aout, "audio-visual", "goom", VLC_FALSE );
        ChangeFiltersString( p_aout, "audio-visual", "visual", VLC_FALSE );
        ChangeFiltersString( p_aout, "audio-visual", "galaktos", VLC_FALSE );
800
801
802
    }
    else
    {
803
        if( !strcmp( "goom", psz_mode ) )
804
        {
805
806
807
            ChangeFiltersString( p_aout, "audio-visual", "visual", VLC_FALSE );
            ChangeFiltersString( p_aout, "audio-visual", "goom", VLC_TRUE );
            ChangeFiltersString( p_aout, "audio-visual", "galaktos", VLC_FALSE);
808
809
810
        }
        else if( !strcmp( "galaktos", psz_mode ) )
        {
811
812
813
            ChangeFiltersString( p_aout, "audio-visual", "visual", VLC_FALSE );
            ChangeFiltersString( p_aout, "audio-visual", "goom", VLC_FALSE );
            ChangeFiltersString( p_aout, "audio-visual", "galaktos", VLC_TRUE );
814
815
816
        }
        else
        {
817
818
819
820
            val.psz_string = psz_mode;
            var_Create( p_aout, "effect-list", VLC_VAR_STRING );
            var_Set( p_aout, "effect-list", val );

821
822
823
            ChangeFiltersString( p_aout, "audio-visual", "goom", VLC_FALSE );
            ChangeFiltersString( p_aout, "audio-visual", "visual", VLC_TRUE );
            ChangeFiltersString( p_aout, "audio-visual", "galaktos", VLC_FALSE);
824
        }
825
826
827
828
829
830
831
832
833
834
    }

    /* That sucks */
    for( i = 0; i < p_aout->i_nb_inputs; i++ )
    {
        p_aout->pp_inputs[i]->b_restart = VLC_TRUE;
    }

    return VLC_SUCCESS;
}
835

836
837
838
839
840
841
842
static int EqualizerCallback( vlc_object_t *p_this, char const *psz_cmd,
                       vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    aout_instance_t *p_aout = (aout_instance_t *)p_this;
    char *psz_mode = newval.psz_string;
    vlc_value_t val;
    int i;
843
    int i_ret;
844
    (void)psz_cmd; (void)oldval; (void)p_data;
845
846
847

    if( !psz_mode || !*psz_mode )
    {
848
849
        i_ret = ChangeFiltersString( p_aout, "audio-filter", "equalizer",
                                     VLC_FALSE );
850
851
852
853
854
855
    }
    else
    {
        val.psz_string = psz_mode;
        var_Create( p_aout, "equalizer-preset", VLC_VAR_STRING );
        var_Set( p_aout, "equalizer-preset", val );
856
857
        i_ret = ChangeFiltersString( p_aout, "audio-filter", "equalizer",
                                     VLC_TRUE );
858

859
    }
gbazin's avatar
   
gbazin committed
860

861
    /* That sucks */
862
    if( i_ret == 1 )
863
    {
864
865
866
867
        for( i = 0; i < p_aout->i_nb_inputs; i++ )
        {
            p_aout->pp_inputs[i]->b_restart = VLC_TRUE;
        }
868
869
870
871
    }

    return VLC_SUCCESS;
}
872
873
874
875
876
877
878
879
880
881

static int ReplayGainCallback( vlc_object_t *p_this, char const *psz_cmd,
                               vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    aout_instance_t *p_aout = (aout_instance_t *)p_this;
    int i;

    vlc_mutex_lock( &p_aout->mixer_lock );
    for( i = 0; i < p_aout->i_nb_inputs; i++ )
        ReplayGainSelect( p_aout, p_aout->pp_inputs[i] );
882
883
884

    /* Restart the mixer (a trivial mixer may be in use) */
    aout_MixerMultiplierSet( p_aout, p_aout->mixer.f_multiplier );
885
886
887
888
889
890
891
    vlc_mutex_unlock( &p_aout->mixer_lock );

    return VLC_SUCCESS;
}

static void ReplayGainSelect( aout_instance_t *p_aout, aout_input_t *p_input )
{
892
893
    char *psz_replay_gain = var_GetNonEmptyString( p_aout,
                                                   "audio-replay-gain-mode" );
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
    int i_mode;
    int i_use;
    float f_gain;

    p_input->f_multiplier = 1.0;

    if( !psz_replay_gain )
        return;

    /* Find select mode */
    if( !strcmp( psz_replay_gain, "track" ) )
        i_mode = AUDIO_REPLAY_GAIN_TRACK;
    else if( !strcmp( psz_replay_gain, "album" ) )
        i_mode = AUDIO_REPLAY_GAIN_ALBUM;
    else
        i_mode = AUDIO_REPLAY_GAIN_MAX;

    /* If the select mode is not available, prefer the other one */
    i_use = i_mode;
    if( i_use != AUDIO_REPLAY_GAIN_MAX && !p_input->replay_gain.pb_gain[i_use] )
    {
        for( i_use = 0; i_use < AUDIO_REPLAY_GAIN_MAX; i_use++ )
        {
            if( p_input->replay_gain.pb_gain[i_use] )
                break;
        }
    }

    /* */
    if( i_use != AUDIO_REPLAY_GAIN_MAX )
        f_gain = p_input->replay_gain.pf_gain[i_use] + var_GetFloat( p_aout, "audio-replay-gain-preamp" );
    else if( i_mode != AUDIO_REPLAY_GAIN_MAX )
        f_gain = var_GetFloat( p_aout, "audio-replay-gain-default" );
    else
        f_gain = 0.0;
    p_input->f_multiplier = pow( 10.0, f_gain / 20.0 );

    /* */
    if( p_input->replay_gain.pb_peak[i_use] &&
        var_GetBool( p_aout, "audio-replay-gain-peak-protection" ) &&
        p_input->replay_gain.pf_peak[i_use] * p_input->f_multiplier > 1.0 )
    {
        p_input->f_multiplier = 1.0f / p_input->replay_gain.pf_peak[i_use];
    }

    free( psz_replay_gain );
}