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