]> git.sesse.net Git - vlc/blob - src/audio_output/filters.c
aout: handle filters creation/deletion from decoder, fix memory leak
[vlc] / src / audio_output / filters.c
1 /*****************************************************************************
2  * filters.c : audio output filters management
3  *****************************************************************************
4  * Copyright (C) 2002-2007 VLC authors and VideoLAN
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 it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * 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 <string.h>
32 #include <assert.h>
33
34 #include <vlc_common.h>
35 #include <vlc_dialog.h>
36 #include <vlc_modules.h>
37 #include <vlc_aout.h>
38 #include <vlc_filter.h>
39 #include <vlc_vout.h>                  /* for vout_Request */
40
41 #include <libvlc.h>
42 #include "aout_internal.h"
43
44 /*****************************************************************************
45  * FindFilter: find an audio filter for a specific transformation
46  *****************************************************************************/
47 static filter_t * FindFilter( vlc_object_t *obj,
48                               const audio_sample_format_t *infmt,
49                               const audio_sample_format_t *outfmt )
50 {
51     static const char typename[] = "audio converter";
52     const char *type = "audio converter", *name = NULL;
53     filter_t * p_filter;
54
55     p_filter = vlc_custom_create( obj, sizeof(*p_filter), typename );
56
57     if ( p_filter == NULL ) return NULL;
58
59     p_filter->fmt_in.audio = *infmt;
60     p_filter->fmt_in.i_codec = infmt->i_format;
61     p_filter->fmt_out.audio = *outfmt;
62     p_filter->fmt_out.i_codec = outfmt->i_format;
63
64     if( infmt->i_format == outfmt->i_format
65      && infmt->i_physical_channels == outfmt->i_physical_channels
66      && infmt->i_original_channels == outfmt->i_original_channels )
67     {
68         assert( infmt->i_rate != outfmt->i_rate );
69         type = "audio resampler";
70         name = "$audio-resampler";
71     }
72
73     p_filter->p_module = module_need( p_filter, type, name, false );
74     if ( p_filter->p_module == NULL )
75     {
76         vlc_object_release( p_filter );
77         return NULL;
78     }
79
80     assert( p_filter->pf_audio_filter );
81     return p_filter;
82 }
83
84 /**
85  * Splits audio format conversion in two simpler conversions
86  * @return 0 on successful split, -1 if the input and output formats are too
87  * similar to split the conversion.
88  */
89 static int SplitConversion( const audio_sample_format_t *restrict infmt,
90                             const audio_sample_format_t *restrict outfmt,
91                             audio_sample_format_t *midfmt )
92 {
93     *midfmt = *outfmt;
94
95     /* Lastly: resample (after format conversion and remixing) */
96     if( infmt->i_rate != outfmt->i_rate )
97         midfmt->i_rate = infmt->i_rate;
98     else
99     /* Penultimately: remix channels (after format conversion) */
100     if( infmt->i_physical_channels != outfmt->i_physical_channels
101      || infmt->i_original_channels != outfmt->i_original_channels )
102     {
103         midfmt->i_physical_channels = infmt->i_physical_channels;
104         midfmt->i_original_channels = infmt->i_original_channels;
105     }
106     else
107     /* Second: convert linear to S16N as intermediate format */
108     if( AOUT_FMT_LINEAR( infmt ) )
109     {
110         /* All conversion from linear to S16N must be supported directly. */
111         if( outfmt->i_format == VLC_CODEC_S16N )
112             return -1;
113         midfmt->i_format = VLC_CODEC_S16N;
114     }
115     else
116     /* First: convert non-linear to FI32 as intermediate format */
117     {
118         if( outfmt->i_format == VLC_CODEC_FI32 )
119             return -1;
120         midfmt->i_format = VLC_CODEC_FI32;
121     }
122
123     assert( !AOUT_FMTS_IDENTICAL( infmt, midfmt ) );
124     aout_FormatPrepare( midfmt );
125     return 0;
126 }
127
128 #undef aout_FiltersCreatePipeline
129 /**
130  * Allocates audio format conversion filters
131  * @param obj parent VLC object for new filters
132  * @param filters table of filters [IN/OUT]
133  * @param nb_filters pointer to the number of filters in the table [IN/OUT]
134  * @param max_filters size of filters table [IN]
135  * @param infmt input audio format
136  * @param outfmt output audio format
137  * @return 0 on success, -1 on failure
138  */
139 int aout_FiltersCreatePipeline( vlc_object_t *obj, filter_t **filters,
140                                 unsigned *nb_filters, unsigned max_filters,
141                                 const audio_sample_format_t *restrict infmt,
142                                 const audio_sample_format_t *restrict outfmt )
143 {
144     audio_sample_format_t curfmt = *outfmt;
145     unsigned i = 0;
146
147     max_filters -= *nb_filters;
148     filters += *nb_filters;
149     aout_FormatsPrint( obj, "filter(s)", infmt, outfmt );
150
151     while( !AOUT_FMTS_IDENTICAL( infmt, &curfmt ) )
152     {
153         if( i >= max_filters )
154         {
155             msg_Err( obj, "maximum of %u filters reached", max_filters );
156             dialog_Fatal( obj, _("Audio filtering failed"),
157                           _("The maximum number of filters (%u) was reached."),
158                           max_filters );
159             goto rollback;
160         }
161
162         /* Make room and prepend a filter */
163         memmove( filters + 1, filters, i * sizeof( *filters ) );
164
165         *filters = FindFilter( obj, infmt, &curfmt );
166         if( *filters != NULL )
167         {
168             i++;
169             break; /* done! */
170         }
171
172         audio_sample_format_t midfmt;
173         /* Split the conversion */
174         if( SplitConversion( infmt, &curfmt, &midfmt ) )
175         {
176             msg_Err( obj, "conversion pipeline failed: %4.4s -> %4.4s",
177                      (const char *)&infmt->i_format,
178                      (const char *)&outfmt->i_format );
179             goto rollback;
180         }
181
182         *filters = FindFilter( obj, &midfmt, &curfmt );
183         if( *filters == NULL )
184         {
185             msg_Err( obj, "cannot find filter for simple conversion" );
186             goto rollback;
187         }
188         curfmt = midfmt;
189         i++;
190     }
191
192     msg_Dbg( obj, "conversion pipeline completed" );
193     *nb_filters += i;
194     return 0;
195
196 rollback:
197     aout_FiltersDestroyPipeline( filters, i );
198     return -1;
199 }
200
201 /**
202  * Destroys a chain of audio filters.
203  */
204 void aout_FiltersDestroyPipeline( filter_t *const *filters, unsigned n )
205 {
206     for( unsigned i = 0; i < n; i++ )
207     {
208         filter_t *p_filter = filters[i];
209
210         module_unneed( p_filter, p_filter->p_module );
211         vlc_object_release( p_filter );
212     }
213 }
214
215 static inline bool ChangeFiltersString (vlc_object_t *aout, const char *var,
216                                         const char *filter, bool add)
217 {
218     return aout_ChangeFilterString (aout, aout, var, filter, add);
219 }
220
221 /**
222  * Filters an audio buffer through a chain of filters.
223  */
224 void aout_FiltersPlay( filter_t *const *pp_filters,
225                        unsigned i_nb_filters, block_t ** pp_block )
226 {
227     block_t *p_block = *pp_block;
228
229     /* TODO: use filter chain */
230     for( unsigned i = 0; (i < i_nb_filters) && (p_block != NULL); i++ )
231     {
232         filter_t * p_filter = pp_filters[i];
233
234         /* Please note that p_block->i_nb_samples & i_buffer
235          * shall be set by the filter plug-in. */
236         p_block = p_filter->pf_audio_filter( p_filter, p_block );
237     }
238     *pp_block = p_block;
239 }
240
241 /** Callback for visualization selection */
242 static int VisualizationCallback (vlc_object_t *obj, char const *var,
243                                   vlc_value_t oldval, vlc_value_t newval,
244                                   void *data)
245 {
246     audio_output_t *aout = (audio_output_t *)obj;
247     const char *mode = newval.psz_string;
248
249     if (!*mode)
250     {
251         ChangeFiltersString (obj, "audio-visual", "goom", false);
252         ChangeFiltersString (obj, "audio-visual", "visual", false);
253         ChangeFiltersString (obj, "audio-visual", "projectm", false);
254         ChangeFiltersString (obj, "audio-visual", "vsxu", false);
255     }
256     else if (!strcmp ("goom", mode))
257     {
258         ChangeFiltersString (obj, "audio-visual", "visual", false );
259         ChangeFiltersString (obj, "audio-visual", "goom", true );
260         ChangeFiltersString (obj, "audio-visual", "projectm", false );
261         ChangeFiltersString (obj, "audio-visual", "vsxu", false);
262     }
263     else if (!strcmp ("projectm", mode))
264     {
265         ChangeFiltersString (obj, "audio-visual", "visual", false);
266         ChangeFiltersString (obj, "audio-visual", "goom", false);
267         ChangeFiltersString (obj, "audio-visual", "projectm", true);
268         ChangeFiltersString (obj, "audio-visual", "vsxu", false);
269     }
270     else if (!strcmp ("vsxu", mode))
271     {
272         ChangeFiltersString (obj, "audio-visual", "visual", false);
273         ChangeFiltersString (obj, "audio-visual", "goom", false);
274         ChangeFiltersString (obj, "audio-visual", "projectm", false);
275         ChangeFiltersString (obj, "audio-visual", "vsxu", true);
276     }
277     else
278     {
279         var_Create (obj, "effect-list", VLC_VAR_STRING);
280         var_SetString (obj, "effect-list", mode);
281
282         ChangeFiltersString (obj, "audio-visual", "goom", false);
283         ChangeFiltersString (obj, "audio-visual", "visual", true);
284         ChangeFiltersString (obj, "audio-visual", "projectm", false);
285     }
286
287     aout_InputRequestRestart (aout);
288     (void) var; (void) oldval; (void) data;
289     return VLC_SUCCESS;
290 }
291
292 static int EqualizerCallback (vlc_object_t *obj, char const *var,
293                               vlc_value_t oldval, vlc_value_t newval,
294                               void *data)
295 {
296     audio_output_t *aout = (audio_output_t *)obj;
297     char *mode = newval.psz_string;
298     bool ret;
299
300     if (!*mode)
301         ret = ChangeFiltersString (obj, "audio-filter", "equalizer", false);
302     else
303     {
304         var_Create (obj, "equalizer-preset", VLC_VAR_STRING);
305         var_SetString (obj, "equalizer-preset", mode);
306         ret = ChangeFiltersString (obj, "audio-filter", "equalizer", true);
307     }
308
309     /* That sucks */
310     if (ret)
311         aout_InputRequestRestart (aout);
312     (void) var; (void) oldval; (void) data;
313     return VLC_SUCCESS;
314 }
315
316 static vout_thread_t *RequestVout (void *data, vout_thread_t *vout,
317                                    video_format_t *fmt, bool recycle)
318 {
319     audio_output_t *aout = data;
320     vout_configuration_t cfg = {
321         .vout       = vout,
322         .input      = NULL,
323         .change_fmt = true,
324         .fmt        = fmt,
325         .dpb_size   = 1,
326     };
327
328     (void) recycle;
329     return vout_Request (aout, &cfg);
330 }
331
332 vout_thread_t *aout_filter_RequestVout (filter_t *filter, vout_thread_t *vout,
333                                         video_format_t *fmt)
334 {
335     /* NOTE: This only works from audio output.
336      * If you want to use visualization filters from another place, you will
337      * need to add a new pf_aout_request_vout callback or store a pointer
338      * to aout_request_vout_t inside filter_t (i.e. a level of indirection). */
339     aout_owner_t *owner = aout_owner ((audio_output_t *)filter->p_parent);
340     aout_request_vout_t *req = &owner->request_vout;
341
342     return req->pf_request_vout (req->p_private, vout, fmt,
343                                  owner->recycle_vout);
344 }
345
346 static filter_t *CreateFilter (vlc_object_t *parent, const char *name,
347                                const audio_sample_format_t *restrict infmt,
348                                const audio_sample_format_t *restrict outfmt,
349                                bool visu)
350 {
351     filter_t *filter = vlc_custom_create (parent, sizeof (*filter),
352                                           "audio filter");
353     if (unlikely(filter == NULL))
354         return NULL;
355
356     /*filter->p_owner = NOT NEEDED;*/
357     filter->fmt_in.i_codec = infmt->i_format;
358     filter->fmt_in.audio = *infmt;
359     filter->fmt_out.i_codec = outfmt->i_format;
360     filter->fmt_out.audio = *outfmt;
361
362     if (!visu)
363     {
364         filter->p_module = module_need (filter, "audio filter", name, true);
365         if (filter->p_module != NULL)
366             return filter;
367
368         /* If probing failed, formats shall not have been modified. */
369         assert (AOUT_FMTS_IDENTICAL(&filter->fmt_in.audio, infmt));
370         assert (AOUT_FMTS_IDENTICAL(&filter->fmt_out.audio, outfmt));
371     }
372
373     filter->p_module = module_need (filter, "visualization2", name, true);
374     if (filter->p_module != NULL)
375         return filter;
376
377     vlc_object_release (filter);
378     return NULL;
379 }
380
381 /**
382  * Sets up the audio filters.
383  */
384 int aout_FiltersNew (audio_output_t *aout,
385                      const audio_sample_format_t *restrict infmt,
386                      const audio_sample_format_t *restrict outfmt,
387                      const aout_request_vout_t *request_vout)
388 {
389     aout_owner_t *owner = aout_owner (aout);
390
391     /* Prepare format structure */
392     aout_FormatPrint (aout, "input", infmt);
393     audio_sample_format_t input_format = *infmt;
394     audio_sample_format_t output_format = *outfmt;
395
396     /* Now add user filters */
397     owner->nb_filters = 0;
398     owner->rate_filter = NULL;
399     owner->resampler = NULL;
400
401     if (!AOUT_FMT_LINEAR(outfmt))
402         return 0;
403
404     var_AddCallback (aout, "visual", VisualizationCallback, NULL);
405     var_AddCallback (aout, "equalizer", EqualizerCallback, NULL);
406
407     const char *scaletempo =
408         var_InheritBool (aout, "audio-time-stretch") ? "scaletempo" : NULL;
409     char *filters = var_InheritString (aout, "audio-filter");
410     char *visual = var_InheritString (aout, "audio-visual");
411
412     if (request_vout != NULL)
413         owner->request_vout = *request_vout;
414     else
415     {
416         owner->request_vout.pf_request_vout = RequestVout;
417         owner->request_vout.p_private = aout;
418     }
419     owner->recycle_vout = (visual != NULL) && *visual;
420
421     /* parse user filter lists */
422     const char *list[AOUT_MAX_FILTERS];
423     unsigned n = 0;
424
425     if (scaletempo != NULL)
426         list[n++] = scaletempo;
427     if (filters != NULL)
428     {
429         char *p = filters, *name;
430         while ((name = strsep (&p, " :")) != NULL && n < AOUT_MAX_FILTERS)
431             list[n++] = name;
432     }
433     if (visual != NULL && n < AOUT_MAX_FILTERS)
434         list[n++] = visual;
435
436     for (unsigned i = 0; i < n; i++)
437     {
438         const char *name = list[i];
439
440         if (owner->nb_filters >= AOUT_MAX_FILTERS)
441         {
442             msg_Err (aout, "maximum of %u filters reached", AOUT_MAX_FILTERS);
443             msg_Err (aout, "cannot add user filter %s (skipped)", name);
444             break;
445         }
446
447         filter_t *filter = CreateFilter (VLC_OBJECT(aout), name,
448                                          &input_format, &output_format,
449                                          name == visual);
450         if (filter == NULL)
451         {
452             msg_Err (aout, "cannot add user filter %s (skipped)", name);
453             continue;
454         }
455
456         /* convert to the filter input format if necessary */
457         if (aout_FiltersCreatePipeline (VLC_OBJECT(aout), owner->filters,
458                                         &owner->nb_filters,
459                                         AOUT_MAX_FILTERS - 1,
460                                         &input_format, &filter->fmt_in.audio))
461         {
462             msg_Err (aout, "cannot add user filter %s (skipped)", name);
463             module_unneed (filter, filter->p_module);
464             vlc_object_release (filter);
465             continue;
466         }
467
468         assert (owner->nb_filters < AOUT_MAX_FILTERS);
469         owner->filters[owner->nb_filters++] = filter;
470         input_format = filter->fmt_out.audio;
471
472         if (name == scaletempo)
473             owner->rate_filter = filter;
474     }
475     free (visual);
476     free (filters);
477
478     /* convert to the output format (minus resampling) if necessary */
479     output_format.i_rate = input_format.i_rate;
480     if (aout_FiltersCreatePipeline (VLC_OBJECT(aout), owner->filters,
481                                     &owner->nb_filters, AOUT_MAX_FILTERS,
482                                     &input_format, &output_format))
483     {
484         msg_Err (aout, "cannot setup filtering pipeline");
485         goto error;
486     }
487     input_format = output_format;
488
489     /* insert the resampler */
490     output_format.i_rate = outfmt->i_rate;
491     assert (AOUT_FMTS_IDENTICAL(&output_format, outfmt));
492
493     unsigned rate_bak = input_format.i_rate;
494     if (output_format.i_rate == input_format.i_rate)
495         /* For historical reasons, a different rate is required to probe
496          * resampling filters. */
497         input_format.i_rate++;
498     owner->resampler = FindFilter (VLC_OBJECT(aout), &input_format,
499                                    &output_format);
500     if (owner->resampler == NULL)
501     {
502         msg_Err (aout, "cannot setup a resampler");
503         goto error;
504     }
505     owner->resampler->fmt_in.audio.i_rate = rate_bak;
506     if (owner->rate_filter == NULL)
507         owner->rate_filter = owner->resampler;
508
509     return 0;
510
511 error:
512     aout_FiltersDestroyPipeline (owner->filters, owner->nb_filters);
513     var_DelCallback (aout, "equalizer", EqualizerCallback, NULL);
514     var_DelCallback (aout, "visual", VisualizationCallback, NULL);
515     return -1;
516 }
517
518 /**
519  * Destroys the audio filters.
520  */
521 void aout_FiltersDelete (audio_output_t *aout)
522 {
523     aout_owner_t *owner = aout_owner (aout);
524
525     if (owner->resampler != NULL)
526         aout_FiltersDestroyPipeline (&owner->resampler, 1);
527     aout_FiltersDestroyPipeline (owner->filters, owner->nb_filters);
528     var_DelCallback (aout, "equalizer", EqualizerCallback, NULL);
529     var_DelCallback (aout, "visual", VisualizationCallback, NULL);
530
531     /* XXX We need to update recycle_vout before calling
532      * aout_FiltersDestroyPipeline().
533      * FIXME There may be a race condition if audio-visual is updated between
534      * aout_FiltersDestroy() and the next aout_FiltersNew().
535      */
536     char *visual = var_InheritString (aout, "audio-visual");
537     owner->recycle_vout = (visual != NULL) && *visual;
538     free (visual);
539 }