Commit 036c9c52 authored by Boris Dorès's avatar Boris Dorès

- the creation of the audio filter pipeline when specifying user audio

  channel mixers was broken (again) in 0.8.1 and 0.8.2; hopefully this
  will fix it for good, by allowing audio filters to request a specific
  format rather than imposing them an arbitrary one
- various other small fixes in the audio output core
- option to force detection of Dolby Surround
- simple module to decode Dolby Surround
- increase spatialization with two center speakers and fix channel
  order for 7.1 streams in the headphone channel mixer
parent 880f95fd
......@@ -1015,7 +1015,7 @@ dnl VLC_ADD_PLUGINS([externrun])
VLC_ADD_PLUGINS([i420_yuy2 i422_yuy2 i420_ymga])
VLC_ADD_PLUGINS([aout_file linear_resampler bandlimited_resampler])
VLC_ADD_PLUGINS([float32_mixer spdif_mixer simple_channel_mixer])
VLC_ADD_PLUGINS([headphone_channel_mixer normvol equalizer])
VLC_ADD_PLUGINS([dolby_surround_decoder headphone_channel_mixer normvol equalizer])
VLC_ADD_PLUGINS([fixed32tofloat32 float32tos16 float32tos8 float32tou16 float32tou8 a52tospdif dtstospdif s16tofloat32 s16tofloat32swab s8tofloat32 u8tofloat32 audio_format])
fi
......
......@@ -109,6 +109,8 @@ $Id$
* dmo: a DirectMediaObject decoder that uses DirectMedia to decode video (WMV3)
* dolby_surround_decoder: simple decoder for dolby surround encoded streams
* dshow: DirectShow access plugin for encoding cards under Windows
* dts: DTS basic parser/packetizer
......
SOURCES_trivial_channel_mixer = trivial.c
SOURCES_simple_channel_mixer = simple.c
SOURCES_headphone_channel_mixer = headphone.c
SOURCES_dolby_surround_decoder = dolby.c
/*****************************************************************************
* dolby.c : simple decoder for dolby surround encoded streams
*****************************************************************************
* Copyright (C) 2005 the VideoLAN team
* $Id: headphone.c 11664 2005-07-09 06:17:09Z courmisch $
*
* Authors: Boris Dors <babal@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.
*
* 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> /* malloc(), free() */
#include <string.h>
#include <vlc/vlc.h>
#include "audio_output.h"
#include "aout_internal.h"
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int Create ( vlc_object_t * );
static void Destroy ( vlc_object_t * );
static void DoWork ( aout_instance_t *, aout_filter_t *, aout_buffer_t *,
aout_buffer_t * );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( N_("Simple decoder for dolby surround encoded streams") );
set_shortname( _("Dolby surround decoder") );
set_category( CAT_INPUT );
set_subcategory( SUBCAT_INPUT_ACODEC );
set_capability( "audio filter", 5 );
set_callbacks( Create, Destroy );
vlc_module_end();
/*****************************************************************************
* Internal data structures
*****************************************************************************/
struct aout_filter_sys_t
{
int i_left;
int i_center;
int i_right;
int i_rear_left;
int i_rear_center;
int i_rear_right;
};
/* our internal channel order (WG-4 order) */
static const uint32_t pi_channels[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT,
AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, AOUT_CHAN_REARCENTER,
AOUT_CHAN_CENTER, AOUT_CHAN_LFE, 0 };
/*****************************************************************************
* Create: allocate headphone downmixer
*****************************************************************************/
static int Create( vlc_object_t *p_this )
{
int i = 0;
int i_offset = 0;
aout_filter_t * p_filter = (aout_filter_t *)p_this;
/* Validate audio filter format */
if ( p_filter->input.i_original_channels
!= (AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT|AOUT_CHAN_DOLBYSTEREO )
|| aout_FormatNbChannels( &p_filter->output ) <= 2
|| p_filter->output.i_physical_channels
!= ( p_filter->output.i_original_channels & AOUT_CHAN_PHYSMASK )
|| p_filter->input.i_physical_channels
!= ( p_filter->input.i_original_channels & AOUT_CHAN_PHYSMASK ))
{
return VLC_EGENERIC;
}
if ( p_filter->input.i_rate != p_filter->output.i_rate )
{
return VLC_EGENERIC;
}
if ( p_filter->input.i_format != VLC_FOURCC('f','l','3','2')
|| p_filter->output.i_format != VLC_FOURCC('f','l','3','2') )
{
return VLC_EGENERIC;
}
/* Allocate the memory needed to store the module's structure */
p_filter->p_sys = malloc( sizeof(struct aout_filter_sys_t) );
if ( p_filter->p_sys == NULL )
{
msg_Err( p_filter, "Out of memory" );
return VLC_EGENERIC;
}
p_filter->p_sys->i_left = -1;
p_filter->p_sys->i_center = -1;
p_filter->p_sys->i_right = -1;
p_filter->p_sys->i_rear_left = -1;
p_filter->p_sys->i_rear_center = -1;
p_filter->p_sys->i_rear_right = -1;
while ( pi_channels[i] )
{
if ( p_filter->output.i_physical_channels & pi_channels[i] )
{
switch ( pi_channels[i] )
{
case AOUT_CHAN_LEFT:
p_filter->p_sys->i_left = i_offset;
break;
case AOUT_CHAN_CENTER:
p_filter->p_sys->i_center = i_offset;
break;
case AOUT_CHAN_RIGHT:
p_filter->p_sys->i_right = i_offset;
break;
case AOUT_CHAN_REARLEFT:
p_filter->p_sys->i_rear_left = i_offset;
break;
case AOUT_CHAN_REARCENTER:
p_filter->p_sys->i_rear_center = i_offset;
break;
case AOUT_CHAN_REARRIGHT:
p_filter->p_sys->i_rear_right = i_offset;
break;
}
++i_offset;
}
++i;
}
p_filter->pf_do_work = DoWork;
p_filter->b_in_place = 0;
return VLC_SUCCESS;
}
/*****************************************************************************
* Destroy: deallocate resources associated with headphone downmixer
*****************************************************************************/
static void Destroy( vlc_object_t *p_this )
{
aout_filter_t * p_filter = (aout_filter_t *)p_this;
if ( p_filter->p_sys != NULL )
{
free ( p_filter->p_sys );
p_filter->p_sys = NULL;
}
}
/*****************************************************************************
* DoWork: convert a buffer
*****************************************************************************/
static void DoWork( aout_instance_t * p_aout, aout_filter_t * p_filter,
aout_buffer_t * p_in_buf, aout_buffer_t * p_out_buf )
{
float * p_in = (float*) p_in_buf->p_buffer;
float * p_out = (float*) p_out_buf->p_buffer;
size_t i_nb_samples = p_in_buf->i_nb_samples;
size_t i_nb_channels = aout_FormatNbChannels( &p_filter->output );
p_out_buf->i_nb_samples = i_nb_samples;
p_out_buf->i_nb_bytes = sizeof(float) * i_nb_samples
* aout_FormatNbChannels( &p_filter->output );
memset ( p_out , 0 , p_out_buf->i_nb_bytes );
if ( p_filter->p_sys != NULL )
{
struct aout_filter_sys_t * p_sys = p_filter->p_sys;
size_t i_nb_rear = 0;
size_t i;
if ( p_sys->i_rear_left >= 0 )
{
++i_nb_rear;
}
if ( p_sys->i_rear_center >= 0 )
{
++i_nb_rear;
}
if ( p_sys->i_rear_right >= 0 )
{
++i_nb_rear;
}
for ( i = 0; i < i_nb_samples; ++i )
{
float f_left = p_in[ i * 2 ];
float f_right = p_in[ i * 2 + 1 ];
float f_rear = ( f_left - f_right ) / i_nb_rear;
if ( p_sys->i_center >= 0 )
{
float f_center = f_left + f_right;
f_left -= f_center / 2;
f_right -= f_center / 2;
p_out[ i * i_nb_channels + p_sys->i_center ] = f_center;
}
if ( p_sys->i_left >= 0 )
{
p_out[ i * i_nb_channels + p_sys->i_left ] = f_left;
}
if ( p_sys->i_right >= 0 )
{
p_out[ i * i_nb_channels + p_sys->i_right ] = f_right;
}
if ( p_sys->i_rear_left >= 0 )
{
p_out[ i * i_nb_channels + p_sys->i_rear_left ] = f_rear;
}
if ( p_sys->i_rear_center >= 0 )
{
p_out[ i * i_nb_channels + p_sys->i_rear_center ] = f_rear;
}
if ( p_sys->i_rear_right >= 0 )
{
p_out[ i * i_nb_channels + p_sys->i_rear_right ] = f_rear;
}
}
}
}
......@@ -2,7 +2,7 @@
* headphone.c : headphone virtual spatialization channel mixer module
* -> gives the feeling of a real room with a simple headphone
*****************************************************************************
* Copyright (C) 2002 the VideoLAN team
* Copyright (C) 2002-2005 the VideoLAN team
* $Id$
*
* Authors: Boris Dors <babal@via.ecp.fr>
......@@ -47,11 +47,11 @@ static void DoWork ( aout_instance_t *, aout_filter_t *, aout_buffer_t *,
*****************************************************************************/
#define MODULE_DESCRIPTION N_ ( \
"This effect gives you the feeling that you are standing in a room " \
"with a complete 5.1 speaker set when using only a headphone, " \
"with a complete 7.1 speaker set when using only a headphone, " \
"providing a more realistic sound experience. It should also be " \
"more comfortable and less tiring when listening to music for " \
"long periods of time.\nIt works with any source format from mono " \
"to 5.1.")
"to 7.1.")
#define HEADPHONE_DIM_TEXT N_("Characteristic dimension")
#define HEADPHONE_DIM_LONGTEXT N_( \
......@@ -184,6 +184,10 @@ static int Init ( aout_filter_t * p_filter , struct aout_filter_sys_t * p_data
/* Number of elementary operations */
p_data->i_nb_atomic_operations = i_nb_channels * 2;
if ( i_physical_channels & AOUT_CHAN_CENTER )
{
p_data->i_nb_atomic_operations += 2;
}
p_data->p_atomic_operations = malloc ( sizeof(struct atomic_operation_t)
* p_data->i_nb_atomic_operations );
if ( p_data->p_atomic_operations == NULL )
......@@ -212,59 +216,64 @@ static int Init ( aout_filter_t * p_filter , struct aout_filter_sys_t * p_data
i_next_atomic_operation += 2;
i_source_channel_offset++;
}
if ( i_physical_channels & AOUT_CHAN_REARLEFT )
if ( i_physical_channels & AOUT_CHAN_MIDDLELEFT )
{
ComputeChannelOperations ( p_data , i_rate
, i_next_atomic_operation , i_source_channel_offset
, -d_x , d_z_rear , 1.5 / i_nb_channels );
, -d_x , 0 , 1.5 / i_nb_channels );
i_next_atomic_operation += 2;
i_source_channel_offset++;
}
if ( i_physical_channels & AOUT_CHAN_REARRIGHT )
if ( i_physical_channels & AOUT_CHAN_MIDDLERIGHT )
{
ComputeChannelOperations ( p_data , i_rate
, i_next_atomic_operation , i_source_channel_offset
, d_x , d_z_rear , 1.5 / i_nb_channels );
, d_x , 0 , 1.5 / i_nb_channels );
i_next_atomic_operation += 2;
i_source_channel_offset++;
}
if ( i_physical_channels & AOUT_CHAN_REARCENTER )
if ( i_physical_channels & AOUT_CHAN_REARLEFT )
{
ComputeChannelOperations ( p_data , i_rate
, i_next_atomic_operation , i_source_channel_offset
, 0 , -d_z , 1.5 / i_nb_channels );
, -d_x , d_z_rear , 1.5 / i_nb_channels );
i_next_atomic_operation += 2;
i_source_channel_offset++;
}
if ( i_physical_channels & AOUT_CHAN_CENTER )
if ( i_physical_channels & AOUT_CHAN_REARRIGHT )
{
ComputeChannelOperations ( p_data , i_rate
, i_next_atomic_operation , i_source_channel_offset
, 0 , d_z , 1.5 / i_nb_channels );
, d_x , d_z_rear , 1.5 / i_nb_channels );
i_next_atomic_operation += 2;
i_source_channel_offset++;
}
if ( i_physical_channels & AOUT_CHAN_LFE )
if ( i_physical_channels & AOUT_CHAN_REARCENTER )
{
ComputeChannelOperations ( p_data , i_rate
, i_next_atomic_operation , i_source_channel_offset
, 0 , d_z_rear , 5.0 / i_nb_channels );
, 0 , -d_z , 1.5 / i_nb_channels );
i_next_atomic_operation += 2;
i_source_channel_offset++;
}
if ( i_physical_channels & AOUT_CHAN_MIDDLELEFT )
if ( i_physical_channels & AOUT_CHAN_CENTER )
{
/* having two center channels increases the spatialization effect */
ComputeChannelOperations ( p_data , i_rate
, i_next_atomic_operation , i_source_channel_offset
, -d_x , 0 , 1.5 / i_nb_channels );
, d_x / 5.0 , d_z , 0.75 / i_nb_channels );
i_next_atomic_operation += 2;
ComputeChannelOperations ( p_data , i_rate
, i_next_atomic_operation , i_source_channel_offset
, -d_x / 5.0 , d_z , 0.75 / i_nb_channels );
i_next_atomic_operation += 2;
i_source_channel_offset++;
}
if ( i_physical_channels & AOUT_CHAN_MIDDLERIGHT )
if ( i_physical_channels & AOUT_CHAN_LFE )
{
ComputeChannelOperations ( p_data , i_rate
, i_next_atomic_operation , i_source_channel_offset
, d_x , 0 , 1.5 / i_nb_channels );
, 0 , d_z_rear , 5.0 / i_nb_channels );
i_next_atomic_operation += 2;
i_source_channel_offset++;
}
......@@ -301,23 +310,54 @@ static int Init ( aout_filter_t * p_filter , struct aout_filter_sys_t * p_data
static int Create( vlc_object_t *p_this )
{
aout_filter_t * p_filter = (aout_filter_t *)p_this;
vlc_bool_t b_fit = VLC_TRUE;
/* Activate this filter only with stereo devices */
if ( p_filter->output.i_physical_channels != (AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT)
|| p_filter->output.i_physical_channels
!= ( p_filter->output.i_original_channels & AOUT_CHAN_PHYSMASK )
|| p_filter->input.i_physical_channels
!= ( p_filter->input.i_original_channels & AOUT_CHAN_PHYSMASK ) )
{
msg_Dbg( p_filter, "Filter discarded (incompatible format)" );
return VLC_EGENERIC;
}
if ( p_filter->output.i_physical_channels != ( AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT )
|| p_filter->input.i_format != p_filter->output.i_format
|| p_filter->input.i_rate != p_filter->output.i_rate
|| (p_filter->input.i_format != VLC_FOURCC('f','l','3','2')
&& p_filter->input.i_format != VLC_FOURCC('f','i','3','2')) )
/* Request a specific format if not already compatible */
if ( p_filter->input.i_format != VLC_FOURCC('f','l','3','2')
|| p_filter->output.i_format != VLC_FOURCC('f','l','3','2') )
{
msg_Dbg( p_filter, "Filter discarded (invalid format)" );
return -1;
b_fit = VLC_FALSE;
p_filter->input.i_format = VLC_FOURCC('f','l','3','2');
p_filter->output.i_format = VLC_FOURCC('f','l','3','2');
}
if ( p_filter->input.i_rate != p_filter->output.i_rate )
{
b_fit = VLC_FALSE;
p_filter->input.i_rate = p_filter->output.i_rate;
}
if ( p_filter->input.i_physical_channels == (AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT)
&& ( p_filter->input.i_original_channels & AOUT_CHAN_DOLBYSTEREO ) )
{
b_fit = VLC_FALSE;
p_filter->input.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
AOUT_CHAN_CENTER |
AOUT_CHAN_REARLEFT |
AOUT_CHAN_REARRIGHT;
p_filter->input.i_original_channels = p_filter->input.i_physical_channels;
}
if ( ! b_fit )
{
msg_Dbg( p_filter, "Requesting specific format" );
return VLC_EGENERIC;
}
/* Allocate the memory needed to store the module's structure */
p_filter->p_sys = malloc( sizeof(struct aout_filter_sys_t) );
if ( p_filter->p_sys == NULL )
{
msg_Err( p_filter, "out of memory" );
return -1;
msg_Err( p_filter, "Out of memory" );
return VLC_EGENERIC;
}
p_filter->p_sys->i_overflow_buffer_size = 0;
p_filter->p_sys->p_overflow_buffer = NULL;
......@@ -329,13 +369,13 @@ static int Create( vlc_object_t *p_this )
, p_filter->input.i_physical_channels
, p_filter->input.i_rate ) < 0 )
{
return -1;
return VLC_EGENERIC;
}
p_filter->pf_do_work = DoWork;
p_filter->b_in_place = 0;
return 0;
return VLC_SUCCESS;
}
/*****************************************************************************
......
......@@ -136,16 +136,26 @@ static int Open( vlc_object_t *p_this )
{
aout_filter_t *p_filter = (aout_filter_t *)p_this;
aout_filter_sys_t *p_sys;
vlc_bool_t b_fit = VLC_TRUE;
if( p_filter->input.i_format != VLC_FOURCC('f','l','3','2' ) ||
p_filter->output.i_format != VLC_FOURCC('f','l','3','2') )
{
b_fit = VLC_FALSE;
p_filter->input.i_format = VLC_FOURCC('f','l','3','2');
p_filter->output.i_format = VLC_FOURCC('f','l','3','2');
msg_Warn( p_filter, "Bad input or output format" );
return VLC_EGENERIC;
}
if ( !AOUT_FMTS_SIMILAR( &p_filter->input, &p_filter->output ) )
{
b_fit = VLC_FALSE;
memcpy( &p_filter->output, &p_filter->input,
sizeof(audio_sample_format_t) );
msg_Warn( p_filter, "input and output formats are not similar" );
}
if ( ! b_fit )
{
return VLC_EGENERIC;
}
......
......@@ -98,6 +98,7 @@ vlc_module_end();
static int Open( vlc_object_t *p_this )
{
aout_filter_t *p_filter = (aout_filter_t*)p_this;
vlc_bool_t b_fit = VLC_TRUE;
int i_channels;
aout_filter_sys_t *p_sys = p_filter->p_sys =
malloc( sizeof( aout_filter_sys_t ) );
......@@ -105,13 +106,22 @@ static int Open( vlc_object_t *p_this )
if( p_filter->input.i_format != VLC_FOURCC('f','l','3','2' ) ||
p_filter->output.i_format != VLC_FOURCC('f','l','3','2') )
{
msg_Warn( p_filter, "Bad input or output format" );
return VLC_EGENERIC;
b_fit = VLC_FALSE;
p_filter->input.i_format = VLC_FOURCC('f','l','3','2');
p_filter->output.i_format = VLC_FOURCC('f','l','3','2');
msg_Warn( p_filter, "Bad input or output format" );
}
if ( !AOUT_FMTS_SIMILAR( &p_filter->input, &p_filter->output ) )
{
b_fit = VLC_FALSE;
memcpy( &p_filter->output, &p_filter->input,
sizeof(audio_sample_format_t) );
msg_Warn( p_filter, "input and output formats are not similar" );
}
if ( ! b_fit )
{
return VLC_EGENERIC;
}
......
......@@ -366,9 +366,9 @@ static VLCExtended *_o_sharedInstance = nil;
/* en-/disable headphone virtualisation */
if ([o_ckb_hdphnVirt state] == NSOnState)
{
[self changeAFiltersString: "headphone" onOrOff: VLC_TRUE ];
[self changeAFiltersString: "headphone_channel_mixer" onOrOff: VLC_TRUE ];
}else{
[self changeAFiltersString: "headphone" onOrOff: VLC_FALSE ];
[self changeAFiltersString: "headphone_channel_mixer" onOrOff: VLC_FALSE ];
}
}
......
......@@ -819,7 +819,7 @@ void ExtraPanel::OnHeadphone( wxCommandEvent &event )
{
aout_instance_t *p_aout= (aout_instance_t *)vlc_object_find(p_intf,
VLC_OBJECT_AOUT, FIND_ANYWHERE);
ChangeFiltersString( p_intf , p_aout, "headphone",
ChangeFiltersString( p_intf , p_aout, "headphone_channel_mixer",
event.IsChecked() ? VLC_TRUE : VLC_FALSE );
if( p_aout != NULL )
vlc_object_release( p_aout );
......
......@@ -116,36 +116,49 @@ static int SplitConversion( const audio_sample_format_t * p_input_format,
return 2;
}
static void ReleaseFilter( aout_filter_t * p_filter )
{
module_Unneed( p_filter, p_filter->p_module );
vlc_object_detach( p_filter );
vlc_object_destroy( p_filter );
}
/*****************************************************************************
* aout_FiltersCreatePipeline: create a filters pipeline to transform a sample
* format to another
*****************************************************************************
* TODO : allow the user to add/remove specific filters
* pi_nb_filters must be initialized before calling this function
*****************************************************************************/
int aout_FiltersCreatePipeline( aout_instance_t * p_aout,
aout_filter_t ** pp_filters,
aout_filter_t ** pp_filters_start,
int * pi_nb_filters,
const audio_sample_format_t * p_input_format,
const audio_sample_format_t * p_output_format )
{
aout_filter_t** pp_filters = pp_filters_start + *pi_nb_filters;
audio_sample_format_t temp_format;
int i_nb_conversions;
if ( AOUT_FMTS_IDENTICAL( p_input_format, p_output_format ) )
{
msg_Dbg( p_aout, "no need for any filter" );
*pi_nb_filters = 0;
return 0;
}
aout_FormatsPrint( p_aout, "filter(s)", p_input_format, p_output_format );
if( *pi_nb_filters + 1 > AOUT_MAX_FILTERS )
{
msg_Err( p_aout, "max filter reached (%d)", AOUT_MAX_FILTERS );
return -1;
}
/* Try to find a filter to do the whole conversion. */
pp_filters[0] = FindFilter( p_aout, p_input_format, p_output_format );
if ( pp_filters[0] != NULL )
{
msg_Dbg( p_aout, "found a filter for the whole conversion" );
*pi_nb_filters = 1;
++*pi_nb_filters;
return 0;
}
......@@ -177,6 +190,12 @@ int aout_FiltersCreatePipeline( aout_instance_t * p_aout,
/* We have the first stage of the conversion. Find a filter for
* the rest. */
if( *pi_nb_filters + 2 > AOUT_MAX_FILTERS )
{
ReleaseFilter( pp_filters[0] );
msg_Err( p_aout, "max filter reached (%d)", AOUT_MAX_FILTERS );
return -1;
}
pp_filters[1] = FindFilter( p_aout, &pp_filters[0]->output,
p_output_format );
if ( pp_filters[1] == NULL )
......@@ -186,10 +205,16 @@ int aout_FiltersCreatePipeline( aout_instance_t * p_aout,
p_output_format, &temp_format );
if ( !i_nb_conversions )
{
vlc_object_detach( pp_filters[0] );
vlc_object_destroy( pp_filters[0] );
ReleaseFilter( pp_filters[0] );
msg_Err( p_aout,
"couldn't find a filter for the second part of the conversion" );
return -1;
}
if( *pi_nb_filters + 3 > AOUT_MAX_FILTERS )
{
ReleaseFilter( pp_filters[0] );
msg_Err( p_aout, "max filter reached (%d)", AOUT_MAX_FILTERS );
return -1;
}
pp_filters[1] = FindFilter( p_aout, &pp_filters[0]->output,