input.c 26.1 KB
Newer Older
1
2
3
/*****************************************************************************
 * input.c : internal management of input streams for the audio output
 *****************************************************************************
zorglub's avatar
zorglub committed
4
 * Copyright (C) 2002-2004 VideoLAN
5
 * $Id$
6
7
8
9
10
11
12
 *
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
13
 *
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>                            /* calloc(), malloc(), free() */
#include <string.h>

#include <vlc/vlc.h>
31
#include <vlc/input.h>                 /* for input_thread_t and i_pts_delay */
32

33
34
35
36
#ifdef HAVE_ALLOCA_H
#   include <alloca.h>
#endif

37
38
39
#include "audio_output.h"
#include "aout_internal.h"

40
static int VisualizationCallback( vlc_object_t *, char const *,
41
42
43
                                  vlc_value_t, vlc_value_t, void * );
static int EqualizerCallback( vlc_object_t *, char const *,
                              vlc_value_t, vlc_value_t, void * );
Boris Dorès's avatar
Boris Dorès committed
44
45
46
static aout_filter_t * allocateUserChannelMixer( aout_instance_t *,
                                                 audio_sample_format_t *,
                                                 audio_sample_format_t * );
zorglub's avatar
zorglub committed
47

48
49
50
/*****************************************************************************
 * aout_InputNew : allocate a new input and rework the filter pipeline
 *****************************************************************************/
51
int aout_InputNew( aout_instance_t * p_aout, aout_input_t * p_input )
52
{
Boris Dorès's avatar
Boris Dorès committed
53
54
    audio_sample_format_t user_filter_format;
    audio_sample_format_t intermediate_format;/* input of resampler */
55
    vlc_value_t val, text;
56
    char * psz_filters;
Boris Dorès's avatar
Boris Dorès committed
57
    aout_filter_t * p_user_channel_mixer;
58

59
60
    aout_FormatPrint( p_aout, "input", &p_input->input );

61
    /* Prepare FIFO. */
62
    aout_FifoInit( p_aout, &p_input->fifo, p_aout->mixer.mixer.i_rate );
63
64
    p_input->p_first_byte_to_mix = NULL;

65
    /* Prepare format structure */
66
67
    memcpy( &intermediate_format, &p_aout->mixer.mixer,
            sizeof(audio_sample_format_t) );
68
69
    intermediate_format.i_rate = p_input->input.i_rate;

Boris Dorès's avatar
Boris Dorès committed
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
    /* Try to use the channel mixer chosen by the user */
    memcpy ( &user_filter_format, &intermediate_format,
             sizeof(audio_sample_format_t) );
    user_filter_format.i_physical_channels = p_input->input.i_physical_channels;
    user_filter_format.i_original_channels = p_input->input.i_original_channels;
    user_filter_format.i_bytes_per_frame = user_filter_format.i_bytes_per_frame
                              * aout_FormatNbChannels( &user_filter_format )
                              / aout_FormatNbChannels( &intermediate_format );
    p_user_channel_mixer = allocateUserChannelMixer( p_aout, &user_filter_format,
                                                   &intermediate_format );
    /* If it failed, let the main pipeline do channel mixing */
    if ( ! p_user_channel_mixer )
    {
        memcpy ( &user_filter_format, &intermediate_format,
                 sizeof(audio_sample_format_t) );
    }

zorglub's avatar
zorglub committed
87
88
89
90
    /* Create filters. */
    if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_filters,
                                     &p_input->i_nb_filters,
                                     &p_input->input,
Boris Dorès's avatar
Boris Dorès committed
91
                                     &user_filter_format
zorglub's avatar
zorglub committed
92
93
94
95
96
97
                                     ) < 0 )
    {
        msg_Err( p_aout, "couldn't set an input pipeline" );

        aout_FifoDestroy( p_aout, &p_input->fifo );
        p_input->b_error = 1;
gbazin's avatar
   
gbazin committed
98
99
        return -1;
    }
zorglub's avatar
zorglub committed
100

101
    /* Now add user filters */
gbazin's avatar
   
gbazin committed
102
103
    if( var_Type( p_aout, "visual" ) == 0 )
    {
104
        module_t *p_module;
gbazin's avatar
   
gbazin committed
105
106
107
108
109
110
111
112
113
114
115
        var_Create( p_aout, "visual", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
        text.psz_string = _("Visualizations");
        var_Change( p_aout, "visual", VLC_VAR_SETTEXT, &text, NULL );
        val.psz_string = ""; text.psz_string = _("Disable");
        var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
        val.psz_string = "random"; text.psz_string = _("Random");
        var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
        val.psz_string = "scope"; text.psz_string = _("Scope");
        var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
        val.psz_string = "spectrum"; text.psz_string = _("Spectrum");
        var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
116
117
118
119
120

        /* Look for goom plugin */
        p_module = config_FindModule( VLC_OBJECT(p_aout), "goom" );
        if( p_module )
        {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
121
            val.psz_string = "goom"; text.psz_string = "Goom";
122
123
124
            var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
        }

125
126
127
128
        /* Look for galaktos plugin */
        p_module = config_FindModule( VLC_OBJECT(p_aout), "galaktos" );
        if( p_module )
        {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
129
            val.psz_string = "galaktos"; text.psz_string = "GaLaktos";
130
131
132
            var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
        }

gbazin's avatar
   
gbazin committed
133
134
135
136
137
138
139
140
        if( var_Get( p_aout, "effect-list", &val ) == VLC_SUCCESS )
        {
            var_Set( p_aout, "visual", val );
            if( val.psz_string ) free( val.psz_string );
        }
        var_AddCallback( p_aout, "visual", VisualizationCallback, NULL );
    }

141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
    if( var_Type( p_aout, "equalizer" ) == 0 )
    {
        module_config_t *p_config;
	int i;

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

	    val.psz_string = ""; text.psz_string = _("Disable");
	    var_Change( p_aout, "equalizer", VLC_VAR_ADDCHOICE, &val, &text );

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

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

gbazin's avatar
   
gbazin committed
169
170
171
172
173
174
175
176
177
178
179
    if( var_Type( p_aout, "audio-filter" ) == 0 )
    {
        var_Create( p_aout, "audio-filter",
                    VLC_VAR_STRING | VLC_VAR_DOINHERIT );
        text.psz_string = _("Audio filters");
        var_Change( p_aout, "audio-filter", VLC_VAR_SETTEXT, &text, NULL );
    }

    var_Get( p_aout, "audio-filter", &val );
    psz_filters = val.psz_string;
    if( psz_filters && *psz_filters )
zorglub's avatar
zorglub committed
180
    {
181
182
183
184
        char *psz_parser = psz_filters;
        char *psz_next;

        while( psz_parser && *psz_parser )
zorglub's avatar
zorglub committed
185
        {
186
187
188
189
190
191
            aout_filter_t * p_filter;

            if( p_input->i_nb_filters >= AOUT_MAX_FILTERS )
            {
                msg_Dbg( p_aout, "max filter reached (%d)", AOUT_MAX_FILTERS );
                break;
zorglub's avatar
zorglub committed
192
193
            }

194
195
196
197
198
199
200
201
202
203
204
            while( *psz_parser == ' ' && *psz_parser == ',' )
            {
                psz_parser++;
            }
            if( ( psz_next = strchr( psz_parser , ','  ) ) )
            {
                *psz_next++ = '\0';
            }
            if( *psz_parser =='\0' )
            {
                break;
zorglub's avatar
zorglub committed
205
206
            }

207
            msg_Dbg( p_aout, "user filter \"%s\"", psz_parser );
208

209
210
211
212
            /* Create a VLC object */
            p_filter = vlc_object_create( p_aout, sizeof(aout_filter_t) );
            if( p_filter == NULL )
            {
gbazin's avatar
   
gbazin committed
213
214
                msg_Err( p_aout, "cannot add user filter %s (skipped)",
                         psz_parser );
215
216
217
                psz_parser = psz_next;
                continue;
            }
218

219
            vlc_object_attach( p_filter , p_aout );
Boris Dorès's avatar
Boris Dorès committed
220
            memcpy( &p_filter->input, &user_filter_format,
gbazin's avatar
   
gbazin committed
221
                    sizeof(audio_sample_format_t) );
Boris Dorès's avatar
Boris Dorès committed
222
            memcpy( &p_filter->output, &user_filter_format,
gbazin's avatar
   
gbazin committed
223
                    sizeof(audio_sample_format_t) );
224

225
            p_filter->p_module =
gbazin's avatar
   
gbazin committed
226
                module_Need( p_filter,"audio filter", psz_parser, VLC_TRUE );
227
228
229

            if( p_filter->p_module== NULL )
            {
gbazin's avatar
   
gbazin committed
230
231
                msg_Err( p_aout, "cannot add user filter %s (skipped)",
                         psz_parser );
232
233
234
235
236
237
238
239
240
241
242
243
244
245

                vlc_object_detach( p_filter );
                vlc_object_destroy( p_filter );
                psz_parser = psz_next;
                continue;

            }
            p_filter->b_continuity = VLC_FALSE;

            p_input->pp_filters[p_input->i_nb_filters++] = p_filter;

            /* next filter if any */
            psz_parser = psz_next;
        }
246
    }
gbazin's avatar
   
gbazin committed
247
    if( psz_filters ) free( psz_filters );
248

Boris Dorès's avatar
Boris Dorès committed
249
250
251
252
253
254
    /* Attach the user channel mixer */
    if ( p_user_channel_mixer )
    {
        p_input->pp_filters[p_input->i_nb_filters++] = p_user_channel_mixer;
    }

255
256
257
258
259
    /* Prepare hints for the buffer allocator. */
    p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
    p_input->input_alloc.i_bytes_per_sec = -1;

    if ( AOUT_FMT_NON_LINEAR( &p_aout->mixer.mixer ) )
260
    {
261
        p_input->i_nb_resamplers = 0;
262
    }
263
    else
264
    {
265
        /* Create resamplers. */
266
267
        intermediate_format.i_rate = (__MAX(p_input->input.i_rate,
                                            p_aout->mixer.mixer.i_rate)
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
                                 * (100 + AOUT_MAX_RESAMPLING)) / 100;
        if ( intermediate_format.i_rate == p_aout->mixer.mixer.i_rate )
        {
            /* Just in case... */
            intermediate_format.i_rate++;
        }
        if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_resamplers,
                                         &p_input->i_nb_resamplers,
                                         &intermediate_format,
                                         &p_aout->mixer.mixer ) < 0 )
        {
            msg_Err( p_aout, "couldn't set a resampler pipeline" );

            aout_FiltersDestroyPipeline( p_aout, p_input->pp_filters,
                                         p_input->i_nb_filters );
            aout_FifoDestroy( p_aout, &p_input->fifo );
gbazin's avatar
   
gbazin committed
284
            var_Destroy( p_aout, "visual" );
285
286
287
288
289
            p_input->b_error = 1;

            return -1;
        }

gbazin's avatar
   
gbazin committed
290
291
292
293
        aout_FiltersHintBuffers( p_aout, p_input->pp_resamplers,
                                 p_input->i_nb_resamplers,
                                 &p_input->input_alloc );

gbazin's avatar
   
gbazin committed
294
295
        /* Setup the initial rate of the resampler */
        p_input->pp_resamplers[0]->input.i_rate = p_input->input.i_rate;
296
    }
297
    p_input->i_resampling_type = AOUT_RESAMPLING_NONE;
298

299
300
301
302
303
304
305
306
    p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP;
    aout_FiltersHintBuffers( p_aout, p_input->pp_filters,
                             p_input->i_nb_filters,
                             &p_input->input_alloc );

    /* i_bytes_per_sec is still == -1 if no filters */
    p_input->input_alloc.i_bytes_per_sec = __MAX(
                                    p_input->input_alloc.i_bytes_per_sec,
gbazin's avatar
   
gbazin committed
307
                                    (int)(p_input->input.i_bytes_per_frame
308
                                     * p_input->input.i_rate
gbazin's avatar
   
gbazin committed
309
                                     / p_input->input.i_frame_length) );
310
311
312
    /* Allocate in the heap, it is more convenient for the decoder. */
    p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP;

313
314
    p_input->b_error = VLC_FALSE;
    p_input->b_restart = VLC_FALSE;
315

316
    return 0;
317
318
319
320
}

/*****************************************************************************
 * aout_InputDelete : delete an input
321
 *****************************************************************************
322
 * This function must be entered with the mixer lock.
323
 *****************************************************************************/
324
int aout_InputDelete( aout_instance_t * p_aout, aout_input_t * p_input )
325
{
326
327
    if ( p_input->b_error ) return 0;

328
329
    aout_FiltersDestroyPipeline( p_aout, p_input->pp_filters,
                                 p_input->i_nb_filters );
330
331
    aout_FiltersDestroyPipeline( p_aout, p_input->pp_resamplers,
                                 p_input->i_nb_resamplers );
332
333
334
335
336
    aout_FifoDestroy( p_aout, &p_input->fifo );

    return 0;
}

337
338
/*****************************************************************************
 * aout_InputPlay : play a buffer
339
340
 *****************************************************************************
 * This function must be entered with the input lock.
341
 *****************************************************************************/
342
343
int aout_InputPlay( aout_instance_t * p_aout, aout_input_t * p_input,
                    aout_buffer_t * p_buffer )
344
{
gbazin's avatar
   
gbazin committed
345
    mtime_t start_date;
346

347
348
    if( p_input->b_restart )
    {
349
        aout_fifo_t fifo, dummy_fifo;
350
        byte_t      *p_first_byte_to_mix;
351

352
        vlc_mutex_lock( &p_aout->mixer_lock );
353
354
355
356
357
358

        /* A little trick to avoid loosing our input fifo */
        aout_FifoInit( p_aout, &dummy_fifo, p_aout->mixer.mixer.i_rate );
        p_first_byte_to_mix = p_input->p_first_byte_to_mix;
        fifo = p_input->fifo;
        p_input->fifo = dummy_fifo;
359
360
        aout_InputDelete( p_aout, p_input );
        aout_InputNew( p_aout, p_input );
361
362
363
        p_input->p_first_byte_to_mix = p_first_byte_to_mix;
        p_input->fifo = fifo;

364
365
366
        vlc_mutex_unlock( &p_aout->mixer_lock );
    }

367
    /* We don't care if someone changes the start date behind our back after
368
369
     * this. We'll deal with that when pushing the buffer, and compensate
     * with the next incoming buffer. */
370
    vlc_mutex_lock( &p_aout->input_fifos_lock );
371
    start_date = aout_FifoNextStart( p_aout, &p_input->fifo );
372
    vlc_mutex_unlock( &p_aout->input_fifos_lock );
373
374
375
376
377
378

    if ( start_date != 0 && start_date < mdate() )
    {
        /* The decoder is _very_ late. This can only happen if the user
         * pauses the stream (or if the decoder is buggy, which cannot
         * happen :). */
gbazin's avatar
   
gbazin committed
379
380
        msg_Warn( p_aout, "computed PTS is out of range ("I64Fd"), "
                  "clearing out", mdate() - start_date );
381
        vlc_mutex_lock( &p_aout->input_fifos_lock );
382
        aout_FifoSet( p_aout, &p_input->fifo, 0 );
gbazin's avatar
   
gbazin committed
383
        p_input->p_first_byte_to_mix = NULL;
384
        vlc_mutex_unlock( &p_aout->input_fifos_lock );
gbazin's avatar
   
gbazin committed
385
386
        if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE )
            msg_Warn( p_aout, "timing screwed, stopping resampling" );
gbazin's avatar
   
gbazin committed
387
        p_input->i_resampling_type = AOUT_RESAMPLING_NONE;
Christophe Massiot's avatar
Christophe Massiot committed
388
        if ( p_input->i_nb_resamplers != 0 )
389
390
        {
            p_input->pp_resamplers[0]->input.i_rate = p_input->input.i_rate;
gbazin's avatar
   
gbazin committed
391
            p_input->pp_resamplers[0]->b_continuity = VLC_FALSE;
392
        }
393
        start_date = 0;
gbazin's avatar
   
gbazin committed
394
    }
395

396
    if ( p_buffer->start_date < mdate() + AOUT_MIN_PREPARE_TIME )
397
398
399
    {
        /* The decoder gives us f*cked up PTS. It's its business, but we
         * can't present it anyway, so drop the buffer. */
gbazin's avatar
   
gbazin committed
400
        msg_Warn( p_aout, "PTS is out of range ("I64Fd"), dropping buffer",
401
402
                  mdate() - p_buffer->start_date );
        aout_BufferFree( p_buffer );
gbazin's avatar
   
gbazin committed
403
404
405
406
407
408
        p_input->i_resampling_type = AOUT_RESAMPLING_NONE;
        if ( p_input->i_nb_resamplers != 0 )
        {
            p_input->pp_resamplers[0]->input.i_rate = p_input->input.i_rate;
            p_input->pp_resamplers[0]->b_continuity = VLC_FALSE;
        }
409
        return 0;
410
411
    }

412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
    /* If the audio drift is too big then it's not worth trying to resample
     * the audio. */
    if ( start_date != 0 &&
         ( start_date < p_buffer->start_date - 3 * AOUT_PTS_TOLERANCE ) )
    {
        msg_Warn( p_aout, "audio drift is too big ("I64Fd"), clearing out",
                  start_date - p_buffer->start_date );
        vlc_mutex_lock( &p_aout->input_fifos_lock );
        aout_FifoSet( p_aout, &p_input->fifo, 0 );
        p_input->p_first_byte_to_mix = NULL;
        vlc_mutex_unlock( &p_aout->input_fifos_lock );
        if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE )
            msg_Warn( p_aout, "timing screwed, stopping resampling" );
        p_input->i_resampling_type = AOUT_RESAMPLING_NONE;
        if ( p_input->i_nb_resamplers != 0 )
        {
            p_input->pp_resamplers[0]->input.i_rate = p_input->input.i_rate;
            p_input->pp_resamplers[0]->b_continuity = VLC_FALSE;
        }
        start_date = 0;
    }
    else if ( start_date != 0 &&
              ( start_date > p_buffer->start_date + 3 * AOUT_PTS_TOLERANCE ) )
    {
        msg_Warn( p_aout, "audio drift is too big ("I64Fd"), dropping buffer",
                  start_date - p_buffer->start_date );
        aout_BufferFree( p_buffer );
        return 0;
    }

442
443
    if ( start_date == 0 ) start_date = p_buffer->start_date;

444
    /* Run pre-filters. */
zorglub's avatar
zorglub committed
445

446
447
448
    aout_FiltersPlay( p_aout, p_input->pp_filters, p_input->i_nb_filters,
                      &p_buffer );

gbazin's avatar
   
gbazin committed
449
450
451
452
    /* Run the resampler if needed.
     * We first need to calculate the output rate of this resampler. */
    if ( ( p_input->i_resampling_type == AOUT_RESAMPLING_NONE ) &&
         ( start_date < p_buffer->start_date - AOUT_PTS_TOLERANCE
453
           || start_date > p_buffer->start_date + AOUT_PTS_TOLERANCE ) &&
Christophe Massiot's avatar
Christophe Massiot committed
454
         p_input->i_nb_resamplers > 0 )
455
456
457
458
459
460
461
462
463
464
    {
        /* Can happen in several circumstances :
         * 1. A problem at the input (clock drift)
         * 2. A small pause triggered by the user
         * 3. Some delay in the output stage, causing a loss of lip
         *    synchronization
         * Solution : resample the buffer to avoid a scratch.
         */
        mtime_t drift = p_buffer->start_date - start_date;

gbazin's avatar
   
gbazin committed
465
        p_input->i_resamp_start_date = mdate();
466
        p_input->i_resamp_start_drift = (int)drift;
gbazin's avatar
   
gbazin committed
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485

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

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

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

        if( p_input->i_resampling_type == AOUT_RESAMPLING_UP )
486
        {
487
            p_input->pp_resamplers[0]->input.i_rate += 2; /* Hz */
488
        }
gbazin's avatar
   
gbazin committed
489
        else
490
        {
491
            p_input->pp_resamplers[0]->input.i_rate -= 2; /* Hz */
492
493
        }

gbazin's avatar
   
gbazin committed
494
495
496
497
498
499
        /* Check if everything is back to normal, in which case we can stop the
         * resampling */
        if( p_input->pp_resamplers[0]->input.i_rate ==
              p_input->input.i_rate )
        {
            p_input->i_resampling_type = AOUT_RESAMPLING_NONE;
500
501
502
503
            msg_Warn( p_aout, "resampling stopped after "I64Fi" usec "
                      "(drift: "I64Fi")",
                      mdate() - p_input->i_resamp_start_date,
                      p_buffer->start_date - start_date);
gbazin's avatar
   
gbazin committed
504
        }
505
        else if( abs( (int)(p_buffer->start_date - start_date) ) <
gbazin's avatar
   
gbazin committed
506
                 abs( p_input->i_resamp_start_drift ) / 2 )
507
        {
gbazin's avatar
   
gbazin committed
508
509
510
511
512
513
514
515
516
            /* if we reduced the drift from half, then it is time to switch
             * back the resampling direction. */
            if( p_input->i_resampling_type == AOUT_RESAMPLING_UP )
                p_input->i_resampling_type = AOUT_RESAMPLING_DOWN;
            else
                p_input->i_resampling_type = AOUT_RESAMPLING_UP;
            p_input->i_resamp_start_drift = 0;
        }
        else if( p_input->i_resamp_start_drift &&
517
                 ( abs( (int)(p_buffer->start_date - start_date) ) >
gbazin's avatar
   
gbazin committed
518
                   abs( p_input->i_resamp_start_drift ) * 3 / 2 ) )
gbazin's avatar
   
gbazin committed
519
520
521
522
523
        {
            /* If the drift is increasing and not decreasing, than something
             * is bad. We'd better stop the resampling right now. */
            msg_Warn( p_aout, "timing screwed, stopping resampling" );
            p_input->i_resampling_type = AOUT_RESAMPLING_NONE;
524
525
            p_input->pp_resamplers[0]->input.i_rate = p_input->input.i_rate;
        }
526
    }
527

gbazin's avatar
   
gbazin committed
528
529
530
    /* Adding the start date will be managed by aout_FifoPush(). */
    p_buffer->end_date = start_date +
        (p_buffer->end_date - p_buffer->start_date);
531
    p_buffer->start_date = start_date;
gbazin's avatar
   
gbazin committed
532

gbazin's avatar
   
gbazin committed
533
    /* Actually run the resampler now. */
gbazin's avatar
   
gbazin committed
534
    if ( p_input->i_nb_resamplers > 0 )
gbazin's avatar
   
gbazin committed
535
536
537
538
539
540
    {
        aout_FiltersPlay( p_aout, p_input->pp_resamplers,
                          p_input->i_nb_resamplers,
                          &p_buffer );
    }

541
    vlc_mutex_lock( &p_aout->input_fifos_lock );
542
    aout_FifoPush( p_aout, &p_input->fifo, p_buffer );
543
    vlc_mutex_unlock( &p_aout->input_fifos_lock );
544
545

    return 0;
546
}
547

548
static int ChangeFiltersString( aout_instance_t * p_aout,
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
                                 char *psz_name, vlc_bool_t b_add )
{
    vlc_value_t val;
    char *psz_parser;

    var_Get( p_aout, "audio-filter", &val );

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

    psz_parser = strstr( val.psz_string, psz_name );

    if( b_add )
    {
        if( !psz_parser )
        {
            psz_parser = val.psz_string;
            asprintf( &val.psz_string, (*val.psz_string) ? "%s,%s" : "%s%s",
                      val.psz_string, psz_name );
            free( psz_parser );
        }
569
570
571
572
        else
        {
            return 0;
        }
573
574
575
576
577
578
579
580
581
582
583
584
    }
    else
    {
        if( psz_parser )
        {
            memmove( psz_parser, psz_parser + strlen(psz_name) +
                     (*(psz_parser + strlen(psz_name)) == ',' ? 1 : 0 ),
                     strlen(psz_parser + strlen(psz_name)) + 1 );
        }
        else
        {
            free( val.psz_string );
585
            return 0;
586
587
588
589
590
        }
    }

    var_Set( p_aout, "audio-filter", val );
    free( val.psz_string );
591
    return 1;
592
593
}

594
595
596
597
598
static int VisualizationCallback( vlc_object_t *p_this, char const *psz_cmd,
                       vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    aout_instance_t *p_aout = (aout_instance_t *)p_this;
    char *psz_mode = newval.psz_string;
599
600
    vlc_value_t val;
    int i;
601
602
603

    if( !psz_mode || !*psz_mode )
    {
604
605
        ChangeFiltersString( p_aout, "goom", VLC_FALSE );
        ChangeFiltersString( p_aout, "visual", VLC_FALSE );
606
        ChangeFiltersString( p_aout, "galaktos", VLC_FALSE );
607
608
609
    }
    else
    {
610
        if( !strcmp( "goom", psz_mode ) )
611
        {
612
613
            ChangeFiltersString( p_aout, "visual", VLC_FALSE );
            ChangeFiltersString( p_aout, "goom", VLC_TRUE );
614
615
616
617
618
619
620
            ChangeFiltersString( p_aout, "galaktos", VLC_FALSE );
        }
        else if( !strcmp( "galaktos", psz_mode ) )
        {
            ChangeFiltersString( p_aout, "visual", VLC_FALSE );
            ChangeFiltersString( p_aout, "goom", VLC_FALSE );
            ChangeFiltersString( p_aout, "galaktos", VLC_TRUE );
621
622
623
        }
        else
        {
624
625
626
627
            val.psz_string = psz_mode;
            var_Create( p_aout, "effect-list", VLC_VAR_STRING );
            var_Set( p_aout, "effect-list", val );

628
629
            ChangeFiltersString( p_aout, "goom", VLC_FALSE );
            ChangeFiltersString( p_aout, "visual", VLC_TRUE );
630
            ChangeFiltersString( p_aout, "galaktos", VLC_FALSE );
631
        }
632
633
634
635
636
637
638
639
640
641
    }

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

    return VLC_SUCCESS;
}
642

643
644
645
646
647
648
649
static int EqualizerCallback( vlc_object_t *p_this, char const *psz_cmd,
                       vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    aout_instance_t *p_aout = (aout_instance_t *)p_this;
    char *psz_mode = newval.psz_string;
    vlc_value_t val;
    int i;
650
    int i_ret;
651
652
653

    if( !psz_mode || !*psz_mode )
    {
654
        i_ret = ChangeFiltersString( p_aout, "equalizer", VLC_FALSE );
655
656
657
658
659
660
    }
    else
    {
        val.psz_string = psz_mode;
        var_Create( p_aout, "equalizer-preset", VLC_VAR_STRING );
        var_Set( p_aout, "equalizer-preset", val );
661
662
        i_ret = ChangeFiltersString( p_aout, "equalizer", VLC_TRUE );

663
    }
gbazin's avatar
   
gbazin committed
664

665
    /* That sucks */
666
    if( i_ret == 1 )
667
    {
668
669
670
671
        for( i = 0; i < p_aout->i_nb_inputs; i++ )
        {
            p_aout->pp_inputs[i]->b_restart = VLC_TRUE;
        }
672
673
674
675
    }

    return VLC_SUCCESS;
}
Boris Dorès's avatar
Boris Dorès committed
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707

static aout_filter_t * allocateUserChannelMixer( aout_instance_t * p_aout,
                                     audio_sample_format_t * p_input_format,
                                     audio_sample_format_t * p_output_format )
{
    aout_filter_t * p_channel_mixer;

    /* Retreive user preferred channel mixer */
    char * psz_name = config_GetPsz( p_aout, "audio-channel-mixer" );

    /* Not specified => let the main pipeline do the mixing */
    if ( ! psz_name ) return NULL;

    /* Debug information */
    aout_FormatsPrint( p_aout, "channel mixer", p_input_format,
                       p_output_format );

    /* Create a VLC object */
    p_channel_mixer = vlc_object_create( p_aout, sizeof(aout_filter_t) );
    if( p_channel_mixer == NULL )
    {
        msg_Err( p_aout, "cannot add user channel mixer %s", psz_name );
        return NULL;
    }
    vlc_object_attach( p_channel_mixer , p_aout );

    /* Attach the suitable module */
    memcpy( &p_channel_mixer->input, p_input_format,
                    sizeof(audio_sample_format_t) );
    memcpy( &p_channel_mixer->output, p_output_format,
                    sizeof(audio_sample_format_t) );
    p_channel_mixer->p_module =
gbazin's avatar
   
gbazin committed
708
        module_Need( p_channel_mixer,"audio filter", psz_name, VLC_TRUE );
Boris Dorès's avatar
Boris Dorès committed
709
710
711
712
713
714
715
716
717
718
719
720
    if( p_channel_mixer->p_module== NULL )
    {
        msg_Err( p_aout, "cannot add user channel mixer %s", psz_name );
        vlc_object_detach( p_channel_mixer );
        vlc_object_destroy( p_channel_mixer );
        return NULL;
    }
    p_channel_mixer->b_continuity = VLC_FALSE;

    /* Ok */
    return p_channel_mixer;
}