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