]> git.sesse.net Git - vlc/blob - src/audio_output/input.c
- Remove config_FindModule as module_Find nowadays does the same thing
[vlc] / src / audio_output / input.c
1 /*****************************************************************************
2  * input.c : internal management of input streams for the audio output
3  *****************************************************************************
4  * Copyright (C) 2002-2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #include <vlc/vlc.h>
29
30 #include <stdio.h>
31 #include <string.h>
32 #include <math.h>
33
34 #include <vlc_input.h>                 /* for input_thread_t and i_pts_delay */
35
36 #ifdef HAVE_ALLOCA_H
37 #   include <alloca.h>
38 #endif
39 #include <vlc_aout.h>
40
41 #include "aout_internal.h"
42
43 /** FIXME: Ugly but needed to access the counters */
44 #include "input/input_internal.h"
45
46 static void inputFailure( aout_instance_t *, aout_input_t *, const char * );
47 static void inputDrop( aout_instance_t *, aout_input_t *, aout_buffer_t * );
48 static void inputResamplingStop( aout_input_t *p_input );
49
50 static int VisualizationCallback( vlc_object_t *, char const *,
51                                   vlc_value_t, vlc_value_t, void * );
52 static int EqualizerCallback( vlc_object_t *, char const *,
53                               vlc_value_t, vlc_value_t, void * );
54 static int ReplayGainCallback( vlc_object_t *, char const *,
55                                vlc_value_t, vlc_value_t, void * );
56 static void ReplayGainSelect( aout_instance_t *, aout_input_t * );
57 /*****************************************************************************
58  * aout_InputNew : allocate a new input and rework the filter pipeline
59  *****************************************************************************/
60 int aout_InputNew( aout_instance_t * p_aout, aout_input_t * p_input )
61 {
62     audio_sample_format_t chain_input_format;
63     audio_sample_format_t chain_output_format;
64     vlc_value_t val, text;
65     char * psz_filters, *psz_visual;
66     int i_visual;
67
68     aout_FormatPrint( p_aout, "input", &p_input->input );
69
70     p_input->i_nb_resamplers = p_input->i_nb_filters = 0;
71
72     /* Prepare FIFO. */
73     aout_FifoInit( p_aout, &p_input->fifo, p_aout->mixer.mixer.i_rate );
74     p_input->p_first_byte_to_mix = NULL;
75
76     /* Prepare format structure */
77     memcpy( &chain_input_format, &p_input->input,
78             sizeof(audio_sample_format_t) );
79     memcpy( &chain_output_format, &p_aout->mixer.mixer,
80             sizeof(audio_sample_format_t) );
81     chain_output_format.i_rate = p_input->input.i_rate;
82     aout_FormatPrepare( &chain_output_format );
83
84     /* Now add user filters */
85     if( var_Type( p_aout, "visual" ) == 0 )
86     {
87         var_Create( p_aout, "visual", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
88         text.psz_string = _("Visualizations");
89         var_Change( p_aout, "visual", VLC_VAR_SETTEXT, &text, NULL );
90         val.psz_string = ""; text.psz_string = _("Disable");
91         var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
92         val.psz_string = "spectrometer"; text.psz_string = _("Spectrometer");
93         var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
94         val.psz_string = "scope"; text.psz_string = _("Scope");
95         var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
96         val.psz_string = "spectrum"; text.psz_string = _("Spectrum");
97         var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
98
99         /* Look for goom plugin */
100         if( module_Exists( VLC_OBJECT(p_aout), "goom" ) )
101         {
102             val.psz_string = "goom"; text.psz_string = "Goom";
103             var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
104         }
105
106         /* Look for galaktos plugin */
107         if( module_Exists( VLC_OBJECT(p_aout), "galaktos" ) )
108         {
109             val.psz_string = "galaktos"; text.psz_string = "GaLaktos";
110             var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
111         }
112
113         if( var_Get( p_aout, "effect-list", &val ) == VLC_SUCCESS )
114         {
115             var_Set( p_aout, "visual", val );
116             if( val.psz_string ) free( val.psz_string );
117         }
118         var_AddCallback( p_aout, "visual", VisualizationCallback, NULL );
119     }
120
121     if( var_Type( p_aout, "equalizer" ) == 0 )
122     {
123         module_config_t *p_config;
124         int i;
125
126         p_config = config_FindConfig( VLC_OBJECT(p_aout), "equalizer-preset" );
127         if( p_config && p_config->i_list )
128         {
129                var_Create( p_aout, "equalizer",
130                            VLC_VAR_STRING | VLC_VAR_HASCHOICE );
131             text.psz_string = _("Equalizer");
132             var_Change( p_aout, "equalizer", VLC_VAR_SETTEXT, &text, NULL );
133
134             val.psz_string = ""; text.psz_string = _("Disable");
135             var_Change( p_aout, "equalizer", VLC_VAR_ADDCHOICE, &val, &text );
136
137             for( i = 0; i < p_config->i_list; i++ )
138             {
139                 val.psz_string = (char *)p_config->ppsz_list[i];
140                 text.psz_string = (char *)p_config->ppsz_list_text[i];
141                 var_Change( p_aout, "equalizer", VLC_VAR_ADDCHOICE,
142                             &val, &text );
143             }
144
145             var_AddCallback( p_aout, "equalizer", EqualizerCallback, NULL );
146         }
147     }
148
149     if( var_Type( p_aout, "audio-filter" ) == 0 )
150     {
151         var_Create( p_aout, "audio-filter",
152                     VLC_VAR_STRING | VLC_VAR_DOINHERIT );
153         text.psz_string = _("Audio filters");
154         var_Change( p_aout, "audio-filter", VLC_VAR_SETTEXT, &text, NULL );
155     }
156     if( var_Type( p_aout, "audio-visual" ) == 0 )
157     {
158         var_Create( p_aout, "audio-visual",
159                     VLC_VAR_STRING | VLC_VAR_DOINHERIT );
160         text.psz_string = _("Audio visualizations");
161         var_Change( p_aout, "audio-visual", VLC_VAR_SETTEXT, &text, NULL );
162     }
163
164     if( var_Type( p_aout, "audio-replay-gain-mode" ) == 0 )
165     {
166         module_config_t *p_config;
167         int i;
168
169         p_config = config_FindConfig( VLC_OBJECT(p_aout), "audio-replay-gain-mode" );
170         if( p_config && p_config->i_list )
171         {
172             var_Create( p_aout, "audio-replay-gain-mode",
173                         VLC_VAR_STRING | VLC_VAR_DOINHERIT );
174
175             text.psz_string = _("Replay gain");
176             var_Change( p_aout, "audio-replay-gain-mode", VLC_VAR_SETTEXT, &text, NULL );
177
178             for( i = 0; i < p_config->i_list; i++ )
179             {
180                 val.psz_string = (char *)p_config->ppsz_list[i];
181                 text.psz_string = (char *)p_config->ppsz_list_text[i];
182                 var_Change( p_aout, "audio-replay-gain-mode", VLC_VAR_ADDCHOICE,
183                             &val, &text );
184             }
185
186             var_AddCallback( p_aout, "audio-replay-gain-mode", ReplayGainCallback, NULL );
187         }
188     }
189     if( var_Type( p_aout, "audio-replay-gain-preamp" ) == 0 )
190     {
191         var_Create( p_aout, "audio-replay-gain-preamp",
192                     VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
193     }
194     if( var_Type( p_aout, "audio-replay-gain-default" ) == 0 )
195     {
196         var_Create( p_aout, "audio-replay-gain-default",
197                     VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
198     }
199     if( var_Type( p_aout, "audio-replay-gain-peak-protection" ) == 0 )
200     {
201         var_Create( p_aout, "audio-replay-gain-peak-protection",
202                     VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
203     }
204
205     var_Get( p_aout, "audio-filter", &val );
206     psz_filters = val.psz_string;
207     var_Get( p_aout, "audio-visual", &val );
208     psz_visual = val.psz_string;
209
210     /* parse user filter lists */
211     for( i_visual = 0; i_visual < 2; i_visual++ )
212     {
213         char *psz_next = NULL;
214         char *psz_parser = i_visual ? psz_visual : psz_filters;
215
216         if( psz_parser == NULL || !*psz_parser )
217             continue;
218
219         while( psz_parser && *psz_parser )
220         {
221             aout_filter_t * p_filter = NULL;
222
223             if( p_input->i_nb_filters >= AOUT_MAX_FILTERS )
224             {
225                 msg_Dbg( p_aout, "max filters reached (%d)", AOUT_MAX_FILTERS );
226                 break;
227             }
228
229             while( *psz_parser == ' ' && *psz_parser == ':' )
230             {
231                 psz_parser++;
232             }
233             if( ( psz_next = strchr( psz_parser , ':'  ) ) )
234             {
235                 *psz_next++ = '\0';
236             }
237             if( *psz_parser =='\0' )
238             {
239                 break;
240             }
241
242             /* Create a VLC object */
243             p_filter = vlc_object_create( p_aout, sizeof(aout_filter_t) );
244             if( p_filter == NULL )
245             {
246                 msg_Err( p_aout, "cannot add user filter %s (skipped)",
247                          psz_parser );
248                 psz_parser = psz_next;
249                 continue;
250             }
251
252             vlc_object_attach( p_filter , p_aout );
253
254             /* try to find the requested filter */
255             if( i_visual == 1 ) /* this can only be a visualization module */
256             {
257                 /* request format */
258                 memcpy( &p_filter->input, &chain_output_format,
259                         sizeof(audio_sample_format_t) );
260                 memcpy( &p_filter->output, &chain_output_format,
261                         sizeof(audio_sample_format_t) );
262
263                 p_filter->p_module = module_Need( p_filter, "visualization",
264                                                   psz_parser, VLC_TRUE );
265             }
266             else /* this can be a audio filter module as well as a visualization module */
267             {
268                 /* request format */
269                 memcpy( &p_filter->input, &chain_input_format,
270                         sizeof(audio_sample_format_t) );
271                 memcpy( &p_filter->output, &chain_output_format,
272                         sizeof(audio_sample_format_t) );
273
274                 p_filter->p_module = module_Need( p_filter, "audio filter",
275                                               psz_parser, VLC_TRUE );
276
277                 if ( p_filter->p_module == NULL )
278                 {
279                     /* if the filter requested a special format, retry */
280                     if ( !( AOUT_FMTS_IDENTICAL( &p_filter->input,
281                                                  &chain_input_format )
282                             && AOUT_FMTS_IDENTICAL( &p_filter->output,
283                                                     &chain_output_format ) ) )
284                     {
285                         aout_FormatPrepare( &p_filter->input );
286                         aout_FormatPrepare( &p_filter->output );
287                         p_filter->p_module = module_Need( p_filter,
288                                                           "audio filter",
289                                                           psz_parser, VLC_TRUE );
290                     }
291                     /* try visual filters */
292                     else
293                     {
294                         memcpy( &p_filter->input, &chain_output_format,
295                                 sizeof(audio_sample_format_t) );
296                         memcpy( &p_filter->output, &chain_output_format,
297                                 sizeof(audio_sample_format_t) );
298                         p_filter->p_module = module_Need( p_filter,
299                                                           "visualization",
300                                                           psz_parser, VLC_TRUE );
301                     }
302                 }
303             }
304
305             /* failure */
306             if ( p_filter->p_module == NULL )
307             {
308                 msg_Err( p_aout, "cannot add user filter %s (skipped)",
309                          psz_parser );
310
311                 vlc_object_detach( p_filter );
312                 vlc_object_destroy( p_filter );
313
314                 psz_parser = psz_next;
315                 continue;
316             }
317
318             /* complete the filter chain if necessary */
319             if ( !AOUT_FMTS_IDENTICAL( &chain_input_format, &p_filter->input ) )
320             {
321                 if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_filters,
322                                                  &p_input->i_nb_filters,
323                                                  &chain_input_format,
324                                                  &p_filter->input ) < 0 )
325                 {
326                     msg_Err( p_aout, "cannot add user filter %s (skipped)",
327                              psz_parser );
328
329                     module_Unneed( p_filter, p_filter->p_module );
330                     vlc_object_detach( p_filter );
331                     vlc_object_destroy( p_filter );
332
333                     psz_parser = psz_next;
334                     continue;
335                 }
336             }
337
338             /* success */
339             p_filter->b_continuity = VLC_FALSE;
340             p_input->pp_filters[p_input->i_nb_filters++] = p_filter;
341             memcpy( &chain_input_format, &p_filter->output,
342                     sizeof( audio_sample_format_t ) );
343
344             /* next filter if any */
345             psz_parser = psz_next;
346         }
347     }
348     if( psz_filters ) free( psz_filters );
349     if( psz_visual ) free( psz_visual );
350
351     /* complete the filter chain if necessary */
352     if ( !AOUT_FMTS_IDENTICAL( &chain_input_format, &chain_output_format ) )
353     {
354         if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_filters,
355                                          &p_input->i_nb_filters,
356                                          &chain_input_format,
357                                          &chain_output_format ) < 0 )
358         {
359             inputFailure( p_aout, p_input, "couldn't set an input pipeline" );
360             return -1;
361         }
362     }
363
364     /* Prepare hints for the buffer allocator. */
365     p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
366     p_input->input_alloc.i_bytes_per_sec = -1;
367
368     /* Create resamplers. */
369     if ( !AOUT_FMT_NON_LINEAR( &p_aout->mixer.mixer ) )
370     {
371         chain_output_format.i_rate = (__MAX(p_input->input.i_rate,
372                                             p_aout->mixer.mixer.i_rate)
373                                  * (100 + AOUT_MAX_RESAMPLING)) / 100;
374         if ( chain_output_format.i_rate == p_aout->mixer.mixer.i_rate )
375         {
376             /* Just in case... */
377             chain_output_format.i_rate++;
378         }
379         if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_resamplers,
380                                          &p_input->i_nb_resamplers,
381                                          &chain_output_format,
382                                          &p_aout->mixer.mixer ) < 0 )
383         {
384             inputFailure( p_aout, p_input, "couldn't set a resampler pipeline");
385             return -1;
386         }
387
388         aout_FiltersHintBuffers( p_aout, p_input->pp_resamplers,
389                                  p_input->i_nb_resamplers,
390                                  &p_input->input_alloc );
391         p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
392
393         /* Setup the initial rate of the resampler */
394         p_input->pp_resamplers[0]->input.i_rate = p_input->input.i_rate;
395     }
396     p_input->i_resampling_type = AOUT_RESAMPLING_NONE;
397
398     aout_FiltersHintBuffers( p_aout, p_input->pp_filters,
399                              p_input->i_nb_filters,
400                              &p_input->input_alloc );
401     p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
402
403     /* i_bytes_per_sec is still == -1 if no filters */
404     p_input->input_alloc.i_bytes_per_sec = __MAX(
405                                     p_input->input_alloc.i_bytes_per_sec,
406                                     (int)(p_input->input.i_bytes_per_frame
407                                      * p_input->input.i_rate
408                                      / p_input->input.i_frame_length) );
409
410     ReplayGainSelect( p_aout, p_input );
411
412     /* Success */
413     p_input->b_error = VLC_FALSE;
414     p_input->b_restart = VLC_FALSE;
415     p_input->i_last_input_rate = INPUT_RATE_DEFAULT;
416
417     return 0;
418 }
419
420 /*****************************************************************************
421  * aout_InputDelete : delete an input
422  *****************************************************************************
423  * This function must be entered with the mixer lock.
424  *****************************************************************************/
425 int aout_InputDelete( aout_instance_t * p_aout, aout_input_t * p_input )
426 {
427     if ( p_input->b_error ) return 0;
428
429     aout_FiltersDestroyPipeline( p_aout, p_input->pp_filters,
430                                  p_input->i_nb_filters );
431     p_input->i_nb_filters = 0;
432     aout_FiltersDestroyPipeline( p_aout, p_input->pp_resamplers,
433                                  p_input->i_nb_resamplers );
434     p_input->i_nb_resamplers = 0;
435     aout_FifoDestroy( p_aout, &p_input->fifo );
436
437     return 0;
438 }
439
440 /*****************************************************************************
441  * aout_InputPlay : play a buffer
442  *****************************************************************************
443  * This function must be entered with the input lock.
444  *****************************************************************************/
445 /* XXX Do not activate it !! */
446 //#define AOUT_PROCESS_BEFORE_CHEKS
447 int aout_InputPlay( aout_instance_t * p_aout, aout_input_t * p_input,
448                     aout_buffer_t * p_buffer, int i_input_rate )
449 {
450     mtime_t start_date;
451
452     if( p_input->b_restart )
453     {
454         aout_fifo_t fifo, dummy_fifo;
455         byte_t      *p_first_byte_to_mix;
456
457         vlc_mutex_lock( &p_aout->mixer_lock );
458
459         /* A little trick to avoid loosing our input fifo */
460         aout_FifoInit( p_aout, &dummy_fifo, p_aout->mixer.mixer.i_rate );
461         p_first_byte_to_mix = p_input->p_first_byte_to_mix;
462         fifo = p_input->fifo;
463         p_input->fifo = dummy_fifo;
464         aout_InputDelete( p_aout, p_input );
465         aout_InputNew( p_aout, p_input );
466         p_input->p_first_byte_to_mix = p_first_byte_to_mix;
467         p_input->fifo = fifo;
468
469         vlc_mutex_unlock( &p_aout->mixer_lock );
470     }
471
472     if( i_input_rate != INPUT_RATE_DEFAULT && p_input->i_nb_resamplers <= 0 )
473     {
474         inputDrop( p_aout, p_input, p_buffer );
475         return 0;
476     }
477
478 #ifdef AOUT_PROCESS_BEFORE_CHEKS
479     /* Run pre-filters. */
480     aout_FiltersPlay( p_aout, p_input->pp_filters, p_input->i_nb_filters,
481                       &p_buffer );
482
483     /* Actually run the resampler now. */
484     if ( p_input->i_nb_resamplers > 0 )
485     {
486         const mtime_t i_date = p_buffer->start_date;
487         aout_FiltersPlay( p_aout, p_input->pp_resamplers,
488                           p_input->i_nb_resamplers,
489                           &p_buffer );
490     }
491
492     if( p_buffer->i_nb_samples <= 0 )
493     {
494         aout_BufferFree( p_buffer );
495         return 0;
496     }
497 #endif
498
499     /* Handle input rate change by modifying resampler input rate */
500     if( i_input_rate != p_input->i_last_input_rate )
501     {
502         unsigned int * const pi_rate = &p_input->pp_resamplers[0]->input.i_rate;
503 #define F(r,ir) ( INPUT_RATE_DEFAULT * (r) / (ir) )
504         const int i_delta = *pi_rate - F(p_input->input.i_rate,p_input->i_last_input_rate);
505         *pi_rate = F(p_input->input.i_rate + i_delta, i_input_rate);
506 #undef F
507         p_input->i_last_input_rate = i_input_rate;
508     }
509
510     /* We don't care if someone changes the start date behind our back after
511      * this. We'll deal with that when pushing the buffer, and compensate
512      * with the next incoming buffer. */
513     vlc_mutex_lock( &p_aout->input_fifos_lock );
514     start_date = aout_FifoNextStart( p_aout, &p_input->fifo );
515     vlc_mutex_unlock( &p_aout->input_fifos_lock );
516
517     if ( start_date != 0 && start_date < mdate() )
518     {
519         /* The decoder is _very_ late. This can only happen if the user
520          * pauses the stream (or if the decoder is buggy, which cannot
521          * happen :). */
522         msg_Warn( p_aout, "computed PTS is out of range ("I64Fd"), "
523                   "clearing out", mdate() - start_date );
524         vlc_mutex_lock( &p_aout->input_fifos_lock );
525         aout_FifoSet( p_aout, &p_input->fifo, 0 );
526         p_input->p_first_byte_to_mix = NULL;
527         vlc_mutex_unlock( &p_aout->input_fifos_lock );
528         if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE )
529             msg_Warn( p_aout, "timing screwed, stopping resampling" );
530         inputResamplingStop( p_input );
531         start_date = 0;
532     }
533
534     if ( p_buffer->start_date < mdate() + AOUT_MIN_PREPARE_TIME )
535     {
536         /* The decoder gives us f*cked up PTS. It's its business, but we
537          * can't present it anyway, so drop the buffer. */
538         msg_Warn( p_aout, "PTS is out of range ("I64Fd"), dropping buffer",
539                   mdate() - p_buffer->start_date );
540
541         inputDrop( p_aout, p_input, p_buffer );
542         inputResamplingStop( p_input );
543         return 0;
544     }
545
546     /* If the audio drift is too big then it's not worth trying to resample
547      * the audio. */
548     if ( start_date != 0 &&
549          ( start_date < p_buffer->start_date - 3 * AOUT_PTS_TOLERANCE ) )
550     {
551         msg_Warn( p_aout, "audio drift is too big ("I64Fd"), clearing out",
552                   start_date - p_buffer->start_date );
553         vlc_mutex_lock( &p_aout->input_fifos_lock );
554         aout_FifoSet( p_aout, &p_input->fifo, 0 );
555         p_input->p_first_byte_to_mix = NULL;
556         vlc_mutex_unlock( &p_aout->input_fifos_lock );
557         if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE )
558             msg_Warn( p_aout, "timing screwed, stopping resampling" );
559         inputResamplingStop( p_input );
560         start_date = 0;
561     }
562     else if ( start_date != 0 &&
563               ( start_date > p_buffer->start_date + 3 * AOUT_PTS_TOLERANCE ) )
564     {
565         msg_Warn( p_aout, "audio drift is too big ("I64Fd"), dropping buffer",
566                   start_date - p_buffer->start_date );
567         inputDrop( p_aout, p_input, p_buffer );
568         return 0;
569     }
570
571     if ( start_date == 0 ) start_date = p_buffer->start_date;
572
573 #ifndef AOUT_PROCESS_BEFORE_CHEKS
574     /* Run pre-filters. */
575     aout_FiltersPlay( p_aout, p_input->pp_filters, p_input->i_nb_filters,
576                       &p_buffer );
577 #endif
578
579     /* Run the resampler if needed.
580      * We first need to calculate the output rate of this resampler. */
581     if ( ( p_input->i_resampling_type == AOUT_RESAMPLING_NONE ) &&
582          ( start_date < p_buffer->start_date - AOUT_PTS_TOLERANCE
583            || start_date > p_buffer->start_date + AOUT_PTS_TOLERANCE ) &&
584          p_input->i_nb_resamplers > 0 )
585     {
586         /* Can happen in several circumstances :
587          * 1. A problem at the input (clock drift)
588          * 2. A small pause triggered by the user
589          * 3. Some delay in the output stage, causing a loss of lip
590          *    synchronization
591          * Solution : resample the buffer to avoid a scratch.
592          */
593         mtime_t drift = p_buffer->start_date - start_date;
594
595         p_input->i_resamp_start_date = mdate();
596         p_input->i_resamp_start_drift = (int)drift;
597
598         if ( drift > 0 )
599             p_input->i_resampling_type = AOUT_RESAMPLING_DOWN;
600         else
601             p_input->i_resampling_type = AOUT_RESAMPLING_UP;
602
603         msg_Warn( p_aout, "buffer is "I64Fd" %s, triggering %ssampling",
604                           drift > 0 ? drift : -drift,
605                           drift > 0 ? "in advance" : "late",
606                           drift > 0 ? "down" : "up");
607     }
608
609     if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE )
610     {
611         /* Resampling has been triggered previously (because of dates
612          * mismatch). We want the resampling to happen progressively so
613          * it isn't too audible to the listener. */
614
615         if( p_input->i_resampling_type == AOUT_RESAMPLING_UP )
616         {
617             p_input->pp_resamplers[0]->input.i_rate += 2; /* Hz */
618         }
619         else
620         {
621             p_input->pp_resamplers[0]->input.i_rate -= 2; /* Hz */
622         }
623
624         /* Check if everything is back to normal, in which case we can stop the
625          * resampling */
626         if( p_input->pp_resamplers[0]->input.i_rate == 1000 * p_input->input.i_rate / i_input_rate )
627         {
628             p_input->i_resampling_type = AOUT_RESAMPLING_NONE;
629             msg_Warn( p_aout, "resampling stopped after "I64Fi" usec "
630                       "(drift: "I64Fi")",
631                       mdate() - p_input->i_resamp_start_date,
632                       p_buffer->start_date - start_date);
633         }
634         else if( abs( (int)(p_buffer->start_date - start_date) ) <
635                  abs( p_input->i_resamp_start_drift ) / 2 )
636         {
637             /* if we reduced the drift from half, then it is time to switch
638              * back the resampling direction. */
639             if( p_input->i_resampling_type == AOUT_RESAMPLING_UP )
640                 p_input->i_resampling_type = AOUT_RESAMPLING_DOWN;
641             else
642                 p_input->i_resampling_type = AOUT_RESAMPLING_UP;
643             p_input->i_resamp_start_drift = 0;
644         }
645         else if( p_input->i_resamp_start_drift &&
646                  ( abs( (int)(p_buffer->start_date - start_date) ) >
647                    abs( p_input->i_resamp_start_drift ) * 3 / 2 ) )
648         {
649             /* If the drift is increasing and not decreasing, than something
650              * is bad. We'd better stop the resampling right now. */
651             msg_Warn( p_aout, "timing screwed, stopping resampling" );
652             inputResamplingStop( p_input );
653         }
654     }
655
656 #ifndef AOUT_PROCESS_BEFORE_CHEKS
657     /* Actually run the resampler now. */
658     if ( p_input->i_nb_resamplers > 0 )
659     {
660         aout_FiltersPlay( p_aout, p_input->pp_resamplers,
661                           p_input->i_nb_resamplers,
662                           &p_buffer );
663     }
664
665     if( p_buffer->i_nb_samples <= 0 )
666     {
667         aout_BufferFree( p_buffer );
668         return 0;
669     }
670 #endif
671
672     /* Adding the start date will be managed by aout_FifoPush(). */
673     p_buffer->end_date = start_date +
674         (p_buffer->end_date - p_buffer->start_date);
675     p_buffer->start_date = start_date;
676
677     vlc_mutex_lock( &p_aout->input_fifos_lock );
678     aout_FifoPush( p_aout, &p_input->fifo, p_buffer );
679     vlc_mutex_unlock( &p_aout->input_fifos_lock );
680     return 0;
681 }
682
683 /*****************************************************************************
684  * static functions
685  *****************************************************************************/
686
687 static void inputFailure( aout_instance_t * p_aout, aout_input_t * p_input,
688                           const char * psz_error_message )
689 {
690     /* error message */
691     msg_Err( p_aout, "%s", psz_error_message );
692
693     /* clean up */
694     aout_FiltersDestroyPipeline( p_aout, p_input->pp_filters,
695                                  p_input->i_nb_filters );
696     aout_FiltersDestroyPipeline( p_aout, p_input->pp_resamplers,
697                                  p_input->i_nb_resamplers );
698     aout_FifoDestroy( p_aout, &p_input->fifo );
699     var_Destroy( p_aout, "visual" );
700     var_Destroy( p_aout, "equalizer" );
701     var_Destroy( p_aout, "audio-filter" );
702     var_Destroy( p_aout, "audio-visual" );
703
704     var_Destroy( p_aout, "audio-replay-gain-mode" );
705     var_Destroy( p_aout, "audio-replay-gain-default" );
706     var_Destroy( p_aout, "audio-replay-gain-preamp" );
707     var_Destroy( p_aout, "audio-replay-gain-peak-protection" );
708
709     /* error flag */
710     p_input->b_error = 1;
711 }
712
713 static void inputDrop( aout_instance_t *p_aout, aout_input_t *p_input, aout_buffer_t *p_buffer )
714 {
715     aout_BufferFree( p_buffer );
716
717     if( !p_input->p_input_thread )
718         return;
719
720     vlc_mutex_lock( &p_input->p_input_thread->p->counters.counters_lock);
721     stats_UpdateInteger( p_aout, p_input->p_input_thread->p->counters.p_lost_abuffers, 1, NULL );
722     vlc_mutex_unlock( &p_input->p_input_thread->p->counters.counters_lock);
723 }
724
725 static void inputResamplingStop( aout_input_t *p_input )
726 {
727     p_input->i_resampling_type = AOUT_RESAMPLING_NONE;
728     if( p_input->i_nb_resamplers != 0 )
729     {
730         p_input->pp_resamplers[0]->input.i_rate = INPUT_RATE_DEFAULT *
731                             p_input->input.i_rate / p_input->i_last_input_rate;
732         p_input->pp_resamplers[0]->b_continuity = VLC_FALSE;
733     }
734 }
735
736 static int ChangeFiltersString( aout_instance_t * p_aout, const char* psz_variable,
737                                  const char *psz_name, vlc_bool_t b_add )
738 {
739     vlc_value_t val;
740     char *psz_parser;
741
742     var_Get( p_aout, psz_variable, &val );
743
744     if( !val.psz_string ) val.psz_string = strdup("");
745
746     psz_parser = strstr( val.psz_string, psz_name );
747
748     if( b_add )
749     {
750         if( !psz_parser )
751         {
752             psz_parser = val.psz_string;
753             asprintf( &val.psz_string, (*val.psz_string) ? "%s:%s" : "%s%s",
754                       val.psz_string, psz_name );
755             free( psz_parser );
756         }
757         else
758         {
759             return 0;
760         }
761     }
762     else
763     {
764         if( psz_parser )
765         {
766             memmove( psz_parser, psz_parser + strlen(psz_name) +
767                      (*(psz_parser + strlen(psz_name)) == ':' ? 1 : 0 ),
768                      strlen(psz_parser + strlen(psz_name)) + 1 );
769         }
770         else
771         {
772             free( val.psz_string );
773             return 0;
774         }
775     }
776
777     var_Set( p_aout, psz_variable, val );
778     free( val.psz_string );
779     return 1;
780 }
781
782 static int VisualizationCallback( vlc_object_t *p_this, char const *psz_cmd,
783                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
784 {
785     aout_instance_t *p_aout = (aout_instance_t *)p_this;
786     char *psz_mode = newval.psz_string;
787     vlc_value_t val;
788     int i;
789     (void)psz_cmd; (void)oldval; (void)p_data;
790
791     if( !psz_mode || !*psz_mode )
792     {
793         ChangeFiltersString( p_aout, "audio-visual", "goom", VLC_FALSE );
794         ChangeFiltersString( p_aout, "audio-visual", "visual", VLC_FALSE );
795         ChangeFiltersString( p_aout, "audio-visual", "galaktos", VLC_FALSE );
796     }
797     else
798     {
799         if( !strcmp( "goom", psz_mode ) )
800         {
801             ChangeFiltersString( p_aout, "audio-visual", "visual", VLC_FALSE );
802             ChangeFiltersString( p_aout, "audio-visual", "goom", VLC_TRUE );
803             ChangeFiltersString( p_aout, "audio-visual", "galaktos", VLC_FALSE);
804         }
805         else if( !strcmp( "galaktos", psz_mode ) )
806         {
807             ChangeFiltersString( p_aout, "audio-visual", "visual", VLC_FALSE );
808             ChangeFiltersString( p_aout, "audio-visual", "goom", VLC_FALSE );
809             ChangeFiltersString( p_aout, "audio-visual", "galaktos", VLC_TRUE );
810         }
811         else
812         {
813             val.psz_string = psz_mode;
814             var_Create( p_aout, "effect-list", VLC_VAR_STRING );
815             var_Set( p_aout, "effect-list", val );
816
817             ChangeFiltersString( p_aout, "audio-visual", "goom", VLC_FALSE );
818             ChangeFiltersString( p_aout, "audio-visual", "visual", VLC_TRUE );
819             ChangeFiltersString( p_aout, "audio-visual", "galaktos", VLC_FALSE);
820         }
821     }
822
823     /* That sucks */
824     for( i = 0; i < p_aout->i_nb_inputs; i++ )
825     {
826         p_aout->pp_inputs[i]->b_restart = VLC_TRUE;
827     }
828
829     return VLC_SUCCESS;
830 }
831
832 static int EqualizerCallback( vlc_object_t *p_this, char const *psz_cmd,
833                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
834 {
835     aout_instance_t *p_aout = (aout_instance_t *)p_this;
836     char *psz_mode = newval.psz_string;
837     vlc_value_t val;
838     int i;
839     int i_ret;
840     (void)psz_cmd; (void)oldval; (void)p_data;
841
842     if( !psz_mode || !*psz_mode )
843     {
844         i_ret = ChangeFiltersString( p_aout, "audio-filter", "equalizer",
845                                      VLC_FALSE );
846     }
847     else
848     {
849         val.psz_string = psz_mode;
850         var_Create( p_aout, "equalizer-preset", VLC_VAR_STRING );
851         var_Set( p_aout, "equalizer-preset", val );
852         i_ret = ChangeFiltersString( p_aout, "audio-filter", "equalizer",
853                                      VLC_TRUE );
854
855     }
856
857     /* That sucks */
858     if( i_ret == 1 )
859     {
860         for( i = 0; i < p_aout->i_nb_inputs; i++ )
861         {
862             p_aout->pp_inputs[i]->b_restart = VLC_TRUE;
863         }
864     }
865
866     return VLC_SUCCESS;
867 }
868
869 static int ReplayGainCallback( vlc_object_t *p_this, char const *psz_cmd,
870                                vlc_value_t oldval, vlc_value_t newval, void *p_data )
871 {
872     aout_instance_t *p_aout = (aout_instance_t *)p_this;
873     int i;
874
875     vlc_mutex_lock( &p_aout->mixer_lock );
876     for( i = 0; i < p_aout->i_nb_inputs; i++ )
877         ReplayGainSelect( p_aout, p_aout->pp_inputs[i] );
878
879     /* Restart the mixer (a trivial mixer may be in use) */
880     aout_MixerMultiplierSet( p_aout, p_aout->mixer.f_multiplier );
881     vlc_mutex_unlock( &p_aout->mixer_lock );
882
883     return VLC_SUCCESS;
884 }
885
886 static void ReplayGainSelect( aout_instance_t *p_aout, aout_input_t *p_input )
887 {
888     char *psz_replay_gain = var_GetNonEmptyString( p_aout,
889                                                    "audio-replay-gain-mode" );
890     int i_mode;
891     int i_use;
892     float f_gain;
893
894     p_input->f_multiplier = 1.0;
895
896     if( !psz_replay_gain )
897         return;
898
899     /* Find select mode */
900     if( !strcmp( psz_replay_gain, "track" ) )
901         i_mode = AUDIO_REPLAY_GAIN_TRACK;
902     else if( !strcmp( psz_replay_gain, "album" ) )
903         i_mode = AUDIO_REPLAY_GAIN_ALBUM;
904     else
905         i_mode = AUDIO_REPLAY_GAIN_MAX;
906
907     /* If the select mode is not available, prefer the other one */
908     i_use = i_mode;
909     if( i_use != AUDIO_REPLAY_GAIN_MAX && !p_input->replay_gain.pb_gain[i_use] )
910     {
911         for( i_use = 0; i_use < AUDIO_REPLAY_GAIN_MAX; i_use++ )
912         {
913             if( p_input->replay_gain.pb_gain[i_use] )
914                 break;
915         }
916     }
917
918     /* */
919     if( i_use != AUDIO_REPLAY_GAIN_MAX )
920         f_gain = p_input->replay_gain.pf_gain[i_use] + var_GetFloat( p_aout, "audio-replay-gain-preamp" );
921     else if( i_mode != AUDIO_REPLAY_GAIN_MAX )
922         f_gain = var_GetFloat( p_aout, "audio-replay-gain-default" );
923     else
924         f_gain = 0.0;
925     p_input->f_multiplier = pow( 10.0, f_gain / 20.0 );
926
927     /* */
928     if( p_input->replay_gain.pb_peak[i_use] &&
929         var_GetBool( p_aout, "audio-replay-gain-peak-protection" ) &&
930         p_input->replay_gain.pf_peak[i_use] * p_input->f_multiplier > 1.0 )
931     {
932         p_input->f_multiplier = 1.0f / p_input->replay_gain.pf_peak[i_use];
933     }
934
935     free( psz_replay_gain );
936 }
937