]> git.sesse.net Git - vlc/blob - src/audio_output/filters.c
i_nb_bytes -> i_buffer
[vlc] / src / audio_output / filters.c
1 /*****************************************************************************
2  * filters.c : audio output filters management
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 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <assert.h>
32
33 #include <vlc_common.h>
34 #include <vlc_dialog.h>
35
36 #ifdef HAVE_ALLOCA_H
37 #   include <alloca.h>
38 #endif
39
40 #include <vlc_aout.h>
41 #include "aout_internal.h"
42 #include <libvlc.h>
43
44 /*****************************************************************************
45  * FindFilter: find an audio filter for a specific transformation
46  *****************************************************************************/
47 static aout_filter_t * FindFilter( aout_instance_t * p_aout,
48                              const audio_sample_format_t * p_input_format,
49                              const audio_sample_format_t * p_output_format )
50 {
51     static const char typename[] = "audio output";
52     aout_filter_t * p_filter;
53
54     p_filter = vlc_custom_create( p_aout, sizeof(*p_filter),
55                                   VLC_OBJECT_GENERIC, typename );
56
57     if ( p_filter == NULL ) return NULL;
58     vlc_object_attach( p_filter, p_aout );
59
60     memcpy( &p_filter->input, p_input_format, sizeof(audio_sample_format_t) );
61     memcpy( &p_filter->output, p_output_format,
62             sizeof(audio_sample_format_t) );
63     p_filter->p_module = module_need( p_filter, "audio filter", NULL, false );
64     if ( p_filter->p_module == NULL )
65     {
66         vlc_object_detach( p_filter );
67         vlc_object_release( p_filter );
68         return NULL;
69     }
70
71     p_filter->b_continuity = false;
72
73     return p_filter;
74 }
75
76 /*****************************************************************************
77  * SplitConversion: split a conversion in two parts
78  *****************************************************************************
79  * Returns the number of conversions required by the first part - 0 if only
80  * one conversion was asked.
81  * Beware : p_output_format can be modified during this function if the
82  * developer passed SplitConversion( toto, titi, titi, ... ). That is legal.
83  * SplitConversion( toto, titi, toto, ... ) isn't.
84  *****************************************************************************/
85 static int SplitConversion( const audio_sample_format_t * p_input_format,
86                             const audio_sample_format_t * p_output_format,
87                             audio_sample_format_t * p_middle_format )
88 {
89     bool b_format =
90              (p_input_format->i_format != p_output_format->i_format);
91     bool b_rate = (p_input_format->i_rate != p_output_format->i_rate);
92     bool b_channels =
93         (p_input_format->i_physical_channels
94           != p_output_format->i_physical_channels)
95      || (p_input_format->i_original_channels
96           != p_output_format->i_original_channels);
97     int i_nb_conversions = b_format + b_rate + b_channels;
98
99     if ( i_nb_conversions <= 1 ) return 0;
100
101     memcpy( p_middle_format, p_output_format, sizeof(audio_sample_format_t) );
102
103     if ( i_nb_conversions == 2 )
104     {
105         if ( !b_format || !b_channels )
106         {
107             p_middle_format->i_rate = p_input_format->i_rate;
108             aout_FormatPrepare( p_middle_format );
109             return 1;
110         }
111
112         /* !b_rate */
113         p_middle_format->i_physical_channels
114              = p_input_format->i_physical_channels;
115         p_middle_format->i_original_channels
116              = p_input_format->i_original_channels;
117         aout_FormatPrepare( p_middle_format );
118         return 1;
119     }
120
121     /* i_nb_conversion == 3 */
122     p_middle_format->i_rate = p_input_format->i_rate;
123     aout_FormatPrepare( p_middle_format );
124     return 2;
125 }
126
127 static void ReleaseFilter( aout_filter_t * p_filter )
128 {
129     module_unneed( p_filter, p_filter->p_module );
130     vlc_object_detach( p_filter );
131     vlc_object_release( p_filter );
132 }
133
134 /*****************************************************************************
135  * aout_FiltersCreatePipeline: create a filters pipeline to transform a sample
136  *                             format to another
137  *****************************************************************************
138  * pi_nb_filters must be initialized before calling this function
139  *****************************************************************************/
140 int aout_FiltersCreatePipeline( aout_instance_t * p_aout,
141                                 aout_filter_t ** pp_filters_start,
142                                 int * pi_nb_filters,
143                                 const audio_sample_format_t * p_input_format,
144                                 const audio_sample_format_t * p_output_format )
145 {
146     aout_filter_t** pp_filters = pp_filters_start + *pi_nb_filters;
147     audio_sample_format_t temp_format;
148     int i_nb_conversions;
149
150     if ( AOUT_FMTS_IDENTICAL( p_input_format, p_output_format ) )
151     {
152         msg_Dbg( p_aout, "no need for any filter" );
153         return 0;
154     }
155
156     aout_FormatsPrint( p_aout, "filter(s)", p_input_format, p_output_format );
157
158     if( *pi_nb_filters + 1 > AOUT_MAX_FILTERS )
159     {
160         msg_Err( p_aout, "max filter reached (%d)", AOUT_MAX_FILTERS );
161         dialog_Fatal( p_aout, _("Audio filtering failed"),
162                       _("The maximum number of filters (%d) was reached."),
163                       AOUT_MAX_FILTERS );
164         return -1;
165     }
166
167     /* Try to find a filter to do the whole conversion. */
168     pp_filters[0] = FindFilter( p_aout, p_input_format, p_output_format );
169     if ( pp_filters[0] != NULL )
170     {
171         msg_Dbg( p_aout, "found a filter for the whole conversion" );
172         ++*pi_nb_filters;
173         return 0;
174     }
175
176     /* We'll have to split the conversion. We always do the downmixing
177      * before the resampling, because the audio decoder can probably do it
178      * for us. */
179     i_nb_conversions = SplitConversion( p_input_format,
180                                         p_output_format, &temp_format );
181     if ( !i_nb_conversions )
182     {
183         /* There was only one conversion to do, and we already failed. */
184         msg_Err( p_aout, "couldn't find a filter for the conversion" );
185         return -1;
186     }
187
188     pp_filters[0] = FindFilter( p_aout, p_input_format, &temp_format );
189     if ( pp_filters[0] == NULL && i_nb_conversions == 2 )
190     {
191         /* Try with only one conversion. */
192         SplitConversion( p_input_format, &temp_format, &temp_format );
193         pp_filters[0] = FindFilter( p_aout, p_input_format, &temp_format );
194     }
195     if ( pp_filters[0] == NULL )
196     {
197         msg_Err( p_aout,
198               "couldn't find a filter for the first part of the conversion" );
199         return -1;
200     }
201
202     /* We have the first stage of the conversion. Find a filter for
203      * the rest. */
204     if( *pi_nb_filters + 2 > AOUT_MAX_FILTERS )
205     {
206         ReleaseFilter( pp_filters[0] );
207         msg_Err( p_aout, "max filter reached (%d)", AOUT_MAX_FILTERS );
208         dialog_Fatal( p_aout, _("Audio filtering failed"),
209                       _("The maximum number of filters (%d) was reached."),
210                       AOUT_MAX_FILTERS );
211         return -1;
212     }
213     pp_filters[1] = FindFilter( p_aout, &pp_filters[0]->output,
214                                 p_output_format );
215     if ( pp_filters[1] == NULL )
216     {
217         /* Try to split the conversion. */
218         i_nb_conversions = SplitConversion( &pp_filters[0]->output,
219                                            p_output_format, &temp_format );
220         if ( !i_nb_conversions )
221         {
222             ReleaseFilter( pp_filters[0] );
223             msg_Err( p_aout,
224               "couldn't find a filter for the second part of the conversion" );
225             return -1;
226         }
227         if( *pi_nb_filters + 3 > AOUT_MAX_FILTERS )
228         {
229             ReleaseFilter( pp_filters[0] );
230             msg_Err( p_aout, "max filter reached (%d)", AOUT_MAX_FILTERS );
231             dialog_Fatal( p_aout, _("Audio filtering failed"),
232                           _("The maximum number of filters (%d) was reached."),
233                           AOUT_MAX_FILTERS );
234             return -1;
235         }
236         pp_filters[1] = FindFilter( p_aout, &pp_filters[0]->output,
237                                     &temp_format );
238         pp_filters[2] = FindFilter( p_aout, &temp_format,
239                                     p_output_format );
240
241         if ( pp_filters[1] == NULL || pp_filters[2] == NULL )
242         {
243             ReleaseFilter( pp_filters[0] );
244             if ( pp_filters[1] != NULL )
245             {
246                 ReleaseFilter( pp_filters[1] );
247             }
248             if ( pp_filters[2] != NULL )
249             {
250                 ReleaseFilter( pp_filters[2] );
251             }
252             msg_Err( p_aout,
253                "couldn't find filters for the second part of the conversion" );
254             return -1;
255         }
256         *pi_nb_filters += 3;
257         msg_Dbg( p_aout, "found 3 filters for the whole conversion" );
258     }
259     else
260     {
261         *pi_nb_filters += 2;
262         msg_Dbg( p_aout, "found 2 filters for the whole conversion" );
263     }
264
265     return 0;
266 }
267
268 /*****************************************************************************
269  * aout_FiltersDestroyPipeline: deallocate a filters pipeline
270  *****************************************************************************/
271 void aout_FiltersDestroyPipeline( aout_instance_t * p_aout,
272                                   aout_filter_t ** pp_filters,
273                                   int i_nb_filters )
274 {
275     int i;
276     (void)p_aout;
277
278     for ( i = 0; i < i_nb_filters; i++ )
279     {
280         aout_filter_t *p_filter = pp_filters[i];
281
282         module_unneed( p_filter, p_filter->p_module );
283         free( p_filter->p_owner );
284         vlc_object_detach( p_filter );
285         vlc_object_release( p_filter );
286     }
287 }
288
289 /*****************************************************************************
290  * aout_FiltersHintBuffers: fill in aout_alloc_t structures to optimize
291  *                          buffer allocations
292  *****************************************************************************/
293 void aout_FiltersHintBuffers( aout_instance_t * p_aout,
294                               aout_filter_t ** pp_filters,
295                               int i_nb_filters, aout_alloc_t * p_first_alloc )
296 {
297     int i;
298
299     (void)p_aout; /* unused */
300
301     for ( i = i_nb_filters - 1; i >= 0; i-- )
302     {
303         aout_filter_t * p_filter = pp_filters[i];
304
305         int i_output_size = p_filter->output.i_bytes_per_frame
306                              * p_filter->output.i_rate * AOUT_MAX_INPUT_RATE
307                              / p_filter->output.i_frame_length;
308         int i_input_size = p_filter->input.i_bytes_per_frame
309                              * p_filter->input.i_rate * AOUT_MAX_INPUT_RATE
310                              / p_filter->input.i_frame_length;
311
312         p_first_alloc->i_bytes_per_sec = __MAX( p_first_alloc->i_bytes_per_sec,
313                                                 i_output_size );
314
315         if ( p_filter->b_in_place )
316         {
317             p_first_alloc->i_bytes_per_sec = __MAX(
318                                          p_first_alloc->i_bytes_per_sec,
319                                          i_input_size );
320             p_filter->output_alloc.b_alloc = false;
321         }
322         else
323         {
324             /* We're gonna need a buffer allocation. */
325             memcpy( &p_filter->output_alloc, p_first_alloc,
326                     sizeof(aout_alloc_t) );
327             p_first_alloc->b_alloc = true;
328             p_first_alloc->i_bytes_per_sec = i_input_size;
329         }
330     }
331 }
332
333 /*****************************************************************************
334  * aout_FiltersPlay: play a buffer
335  *****************************************************************************/
336 void aout_FiltersPlay( aout_instance_t * p_aout,
337                        aout_filter_t ** pp_filters,
338                        int i_nb_filters, aout_buffer_t ** pp_input_buffer )
339 {
340     int i;
341
342     for( i = 0; i < i_nb_filters; i++ )
343     {
344         aout_filter_t * p_filter = pp_filters[i];
345         aout_buffer_t * p_output_buffer;
346
347         /* Resamplers can produce slightly more samples than (i_in_nb *
348          * p_filter->output.i_rate / p_filter->input.i_rate) so we need
349          * slightly bigger buffers. */
350         p_output_buffer = aout_BufferAlloc( &p_filter->output_alloc,
351                               ((mtime_t)(*pp_input_buffer)->i_nb_samples + 2)
352                               * 1000000 / p_filter->input.i_rate,
353                               *pp_input_buffer );
354         if( p_output_buffer == NULL )
355             return;
356         p_output_buffer->i_pts = (*pp_input_buffer)->i_pts;
357         p_output_buffer->i_length = (*pp_input_buffer)->i_length;
358
359         /* Please note that p_output_buffer->i_nb_samples & i_buffer
360          * shall be set by the filter plug-in. */
361         if( (*pp_input_buffer)->i_nb_samples > 0 )
362         {
363             p_filter->pf_do_work( p_aout, p_filter, *pp_input_buffer,
364                                   p_output_buffer );
365         }
366         else
367         {
368             p_output_buffer->i_buffer = 0;
369             p_output_buffer->i_nb_samples = 0;
370         }
371
372         if( !p_filter->b_in_place )
373         {
374             aout_BufferFree( *pp_input_buffer );
375             *pp_input_buffer = p_output_buffer;
376         }
377     }
378 }
379
380 /*****************************************************************************
381  * aout_filter_RequestVout
382  *****************************************************************************/
383 vout_thread_t *aout_filter_RequestVout( aout_filter_t *p_filter,
384                                         vout_thread_t *p_vout, video_format_t *p_fmt )
385 {
386     if( !p_filter->request_vout.pf_request_vout )
387         return NULL;
388     return p_filter->request_vout.pf_request_vout( p_filter->request_vout.p_private,
389                                                    p_vout, p_fmt, true );
390 }
391