packetizer: hxxx: fix DirectTV extraction
[vlc.git] / src / audio_output / filters.c
blobab8de7c39f84e814a8f3026a0781b8db4d9e9e4f
1 /*****************************************************************************
2 * filters.c : audio output filters management
3 *****************************************************************************
4 * Copyright (C) 2002-2007 VLC authors and VideoLAN
5 * $Id$
7 * Authors: Christophe Massiot <massiot@via.ecp.fr>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <string.h>
32 #include <assert.h>
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 #include <vlc_input.h>
42 #include <libvlc.h>
43 #include "aout_internal.h"
45 static filter_t *CreateFilter (vlc_object_t *obj, const char *type,
46 const char *name, filter_owner_sys_t *owner,
47 const audio_sample_format_t *infmt,
48 const audio_sample_format_t *outfmt,
49 config_chain_t *cfg, bool const_fmt)
51 filter_t *filter = vlc_custom_create (obj, sizeof (*filter), type);
52 if (unlikely(filter == NULL))
53 return NULL;
55 filter->owner.sys = owner;
56 filter->p_cfg = cfg;
57 filter->fmt_in.audio = *infmt;
58 filter->fmt_in.i_codec = infmt->i_format;
59 filter->fmt_out.audio = *outfmt;
60 filter->fmt_out.i_codec = outfmt->i_format;
62 #ifndef NDEBUG
63 /* Assure that infmt/oufmt are well prepared and that channels
64 * configurations are valid*/
65 if( infmt->i_physical_channels != 0 )
66 assert( aout_FormatNbChannels( infmt ) == infmt->i_channels );
67 if( outfmt->i_physical_channels != 0 )
68 assert( aout_FormatNbChannels( outfmt ) == outfmt->i_channels );
69 #endif
71 filter->p_module = module_need (filter, type, name, false);
73 #ifndef NDEBUG
74 if (filter->p_module == NULL || const_fmt)
76 /* If probing failed, formats shall not have been modified. */
77 assert (AOUT_FMTS_IDENTICAL(&filter->fmt_in.audio, infmt));
78 assert (AOUT_FMTS_IDENTICAL(&filter->fmt_out.audio, outfmt));
80 #endif
82 if (filter->p_module == NULL)
84 vlc_object_release (filter);
85 filter = NULL;
87 else
88 assert (filter->pf_audio_filter != NULL);
89 return filter;
92 static filter_t *FindConverter (vlc_object_t *obj,
93 const audio_sample_format_t *infmt,
94 const audio_sample_format_t *outfmt)
96 return CreateFilter (obj, "audio converter", NULL, NULL, infmt, outfmt,
97 NULL, true);
100 static filter_t *FindResampler (vlc_object_t *obj,
101 const audio_sample_format_t *infmt,
102 const audio_sample_format_t *outfmt)
104 return CreateFilter (obj, "audio resampler", "$audio-resampler", NULL,
105 infmt, outfmt, NULL, true);
109 * Destroys a chain of audio filters.
111 static void aout_FiltersPipelineDestroy(filter_t *const *filters, unsigned n)
113 for( unsigned i = 0; i < n; i++ )
115 filter_t *p_filter = filters[i];
117 module_unneed( p_filter, p_filter->p_module );
118 vlc_object_release( p_filter );
122 static filter_t *TryFormat (vlc_object_t *obj, vlc_fourcc_t codec,
123 audio_sample_format_t *restrict fmt)
125 audio_sample_format_t output = *fmt;
127 assert (codec != fmt->i_format);
128 output.i_format = codec;
129 aout_FormatPrepare (&output);
131 filter_t *filter = FindConverter (obj, fmt, &output);
132 if (filter != NULL)
133 *fmt = output;
134 return filter;
138 * Allocates audio format conversion filters
139 * @param obj parent VLC object for new filters
140 * @param filters table of filters [IN/OUT]
141 * @param count pointer to the number of filters in the table [IN/OUT]
142 * @param max size of filters table [IN]
143 * @param infmt input audio format
144 * @param outfmt output audio format
145 * @return 0 on success, -1 on failure
147 static int aout_FiltersPipelineCreate(vlc_object_t *obj, filter_t **filters,
148 unsigned *count, unsigned max,
149 const audio_sample_format_t *restrict infmt,
150 const audio_sample_format_t *restrict outfmt,
151 bool headphones)
153 aout_FormatsPrint (obj, "conversion:", infmt, outfmt);
154 max -= *count;
155 filters += *count;
157 /* There is a lot of second guessing on what the conversion plugins can
158 * and cannot do. This seems hardly avoidable, the conversion problem need
159 * to be reduced somehow. */
160 audio_sample_format_t input = *infmt;
161 unsigned n = 0;
163 if (!AOUT_FMT_LINEAR(&input))
165 msg_Err(obj, "Can't convert non linear input");
166 return -1;
169 /* Remix channels */
170 if (infmt->i_physical_channels != outfmt->i_physical_channels
171 || infmt->i_chan_mode != outfmt->i_chan_mode
172 || infmt->channel_type != outfmt->channel_type)
173 { /* Remixing currently requires FL32... TODO: S16N */
174 if (input.i_format != VLC_CODEC_FL32)
176 if (n == max)
177 goto overflow;
179 filter_t *f = TryFormat (obj, VLC_CODEC_FL32, &input);
180 if (f == NULL)
182 msg_Err (obj, "cannot find %s for conversion pipeline",
183 "pre-mix converter");
184 goto error;
187 filters[n++] = f;
190 if (n == max)
191 goto overflow;
193 audio_sample_format_t output;
194 output.i_format = input.i_format;
195 output.i_rate = input.i_rate;
196 output.i_physical_channels = outfmt->i_physical_channels;
197 output.channel_type = outfmt->channel_type;
198 output.i_chan_mode = outfmt->i_chan_mode;
199 aout_FormatPrepare (&output);
201 const char *filter_type =
202 infmt->channel_type != outfmt->channel_type ?
203 "audio renderer" : "audio converter";
205 config_chain_t *cfg = NULL;
206 if (headphones)
207 config_ChainParseOptions(&cfg, "{headphones=true}");
208 filter_t *f = CreateFilter (obj, filter_type, NULL, NULL,
209 &input, &output, cfg, true);
210 if (cfg)
211 config_ChainDestroy(cfg);
213 if (f == NULL)
215 msg_Err (obj, "cannot find %s for conversion pipeline",
216 "remixer");
217 goto error;
220 input = output;
221 filters[n++] = f;
224 /* Resample */
225 if (input.i_rate != outfmt->i_rate)
226 { /* Resampling works with any linear format, but may be ugly. */
227 if (n == max)
228 goto overflow;
230 audio_sample_format_t output = input;
231 output.i_rate = outfmt->i_rate;
233 filter_t *f = FindConverter (obj, &input, &output);
234 if (f == NULL)
236 msg_Err (obj, "cannot find %s for conversion pipeline",
237 "resampler");
238 goto error;
241 input = output;
242 filters[n++] = f;
245 /* Format */
246 if (input.i_format != outfmt->i_format)
248 if (max == 0)
249 goto overflow;
251 filter_t *f = TryFormat (obj, outfmt->i_format, &input);
252 if (f == NULL)
254 msg_Err (obj, "cannot find %s for conversion pipeline",
255 "post-mix converter");
256 goto error;
258 filters[n++] = f;
261 msg_Dbg (obj, "conversion pipeline complete");
262 *count += n;
263 return 0;
265 overflow:
266 msg_Err (obj, "maximum of %u conversion filters reached", max);
267 vlc_dialog_display_error (obj, _("Audio filtering failed"),
268 _("The maximum number of filters (%u) was reached."), max);
269 error:
270 aout_FiltersPipelineDestroy (filters, n);
271 return -1;
275 * Filters an audio buffer through a chain of filters.
277 static block_t *aout_FiltersPipelinePlay(filter_t *const *filters,
278 unsigned count, block_t *block)
280 /* TODO: use filter chain */
281 for (unsigned i = 0; (i < count) && (block != NULL); i++)
283 filter_t *filter = filters[i];
285 /* Please note that p_block->i_nb_samples & i_buffer
286 * shall be set by the filter plug-in. */
287 block = filter->pf_audio_filter (filter, block);
289 return block;
294 * Drain the chain of filters.
296 static block_t *aout_FiltersPipelineDrain(filter_t *const *filters,
297 unsigned count)
299 block_t *chain = NULL;
301 for (unsigned i = 0; i < count; i++)
303 filter_t *filter = filters[i];
305 block_t *block = filter_DrainAudio (filter);
306 if (block)
308 /* If there is a drained block, filter it through the following
309 * chain of filters */
310 if (i + 1 < count)
311 block = aout_FiltersPipelinePlay (&filters[i + 1],
312 count - i - 1, block);
313 if (block)
314 block_ChainAppend (&chain, block);
318 if (chain)
319 return block_ChainGather(chain);
320 else
321 return NULL;
325 * Flush the chain of filters.
327 static void aout_FiltersPipelineFlush(filter_t *const *filters,
328 unsigned count)
330 for (unsigned i = 0; i < count; i++)
331 filter_Flush (filters[i]);
334 static void aout_FiltersPipelineChangeViewpoint(filter_t *const *filters,
335 unsigned count,
336 const vlc_viewpoint_t *vp)
338 for (unsigned i = 0; i < count; i++)
339 filter_ChangeViewpoint (filters[i], vp);
342 #define AOUT_MAX_FILTERS 10
344 struct aout_filters
346 filter_t *rate_filter; /**< The filter adjusting samples count
347 (either the scaletempo filter or a resampler) */
348 filter_t *resampler; /**< The resampler */
349 int resampling; /**< Current resampling (Hz) */
351 unsigned count; /**< Number of filters */
352 filter_t *tab[AOUT_MAX_FILTERS]; /**< Configured user filters
353 (e.g. equalization) and their conversions */
356 /** Callback for visualization selection */
357 static int VisualizationCallback (vlc_object_t *obj, const char *var,
358 vlc_value_t oldval, vlc_value_t newval,
359 void *data)
361 const char *mode = newval.psz_string;
363 if (!*mode)
364 mode = "none";
365 /* FIXME: This ugly hack enforced by visual effect-list, as is the need for
366 * separate "visual" (external) and "audio-visual" (internal) variables...
367 * The visual plugin should have one submodule per effect instead. */
368 if (strcasecmp (mode, "none") && strcasecmp (mode, "goom")
369 && strcasecmp (mode, "projectm") && strcasecmp (mode, "vsxu")
370 && strcasecmp (mode, "glspectrum"))
372 var_Create (obj, "effect-list", VLC_VAR_STRING);
373 var_SetString (obj, "effect-list", mode);
374 mode = "visual";
377 var_SetString (obj, "audio-visual", mode);
378 aout_InputRequestRestart ((audio_output_t *)obj);
379 (void) var; (void) oldval; (void) data;
380 return VLC_SUCCESS;
383 vout_thread_t *aout_filter_RequestVout (filter_t *filter, vout_thread_t *vout,
384 const video_format_t *fmt)
386 /* NOTE: This only works from aout_filters_t.
387 * If you want to use visualization filters from another place, you will
388 * need to add a new pf_aout_request_vout callback or store a pointer
389 * to aout_request_vout_t inside filter_t (i.e. a level of indirection). */
390 const aout_request_vout_t *req = filter->owner.sys;
391 char *visual = var_InheritString (filter->obj.parent, "audio-visual");
392 /* NOTE: Disable recycling to always close the filter vout because OpenGL
393 * visualizations do not use this function to ask for a context. */
394 bool recycle = false;
395 free (visual);
397 return req->pf_request_vout (req->p_private, vout, fmt, recycle);
400 static int AppendFilter(vlc_object_t *obj, const char *type, const char *name,
401 aout_filters_t *restrict filters, const void *owner,
402 audio_sample_format_t *restrict infmt,
403 const audio_sample_format_t *restrict outfmt,
404 config_chain_t *cfg)
406 const unsigned max = sizeof (filters->tab) / sizeof (filters->tab[0]);
407 if (filters->count >= max)
409 msg_Err (obj, "maximum of %u filters reached", max);
410 return -1;
413 filter_t *filter = CreateFilter (obj, type, name,
414 (void *)owner, infmt, outfmt, cfg, false);
415 if (filter == NULL)
417 msg_Err (obj, "cannot add user %s \"%s\" (skipped)", type, name);
418 return -1;
421 /* convert to the filter input format if necessary */
422 if (aout_FiltersPipelineCreate (obj, filters->tab, &filters->count,
423 max - 1, infmt, &filter->fmt_in.audio, false))
425 msg_Err (filter, "cannot add user %s \"%s\" (skipped)", type, name);
426 module_unneed (filter, filter->p_module);
427 vlc_object_release (filter);
428 return -1;
431 assert (filters->count < max);
432 filters->tab[filters->count] = filter;
433 filters->count++;
434 *infmt = filter->fmt_out.audio;
435 return 0;
438 static int AppendRemapFilter(vlc_object_t *obj, aout_filters_t *restrict filters,
439 audio_sample_format_t *restrict infmt,
440 const audio_sample_format_t *restrict outfmt,
441 const int *wg4_remap)
443 char *name;
444 config_chain_t *cfg;
446 /* The remap audio filter use a different order than wg4 */
447 static const uint8_t wg4_to_remap[] = { 0, 2, 6, 7, 3, 5, 4, 1, 8 };
448 int remap[AOUT_CHAN_MAX];
449 bool needed = false;
450 for (int i = 0; i < AOUT_CHAN_MAX; ++i)
452 if (wg4_remap[i] != i)
453 needed = true;
454 remap[i] = wg4_remap[i] >= 0 ? wg4_to_remap[wg4_remap[i]] : -1;
456 if (!needed)
457 return 0;
459 char *str;
460 int ret = asprintf(&str, "remap{channel-left=%d,channel-right=%d,"
461 "channel-middleleft=%d,channel-middleright=%d,"
462 "channel-rearleft=%d,channel-rearright=%d,"
463 "channel-rearcenter=%d,channel-center=%d,"
464 "channel-lfe=%d,normalize=false}",
465 remap[0], remap[1], remap[2], remap[3], remap[4],
466 remap[5], remap[6], remap[7], remap[8]);
467 if (ret == -1)
468 return -1;
470 free(config_ChainCreate(&name, &cfg, str));
471 if (name != NULL && cfg != NULL)
472 ret = AppendFilter(obj, "audio filter", name, filters,
473 NULL, infmt, outfmt, cfg);
474 else
475 ret = -1;
477 free(str);
478 free(name);
479 if (cfg)
480 config_ChainDestroy(cfg);
481 return ret;
484 #undef aout_FiltersNew
486 * Sets a chain of audio filters up.
487 * \param obj parent object for the filters
488 * \param infmt chain input format [IN]
489 * \param outfmt chain output format [IN]
490 * \param request_vout visualization video output request callback
491 * \param cfg a valid aout_filters_cfg_t struct or NULL.
492 * \return a filters chain or NULL on failure
494 * \note
495 * *request_vout (if not NULL) must remain valid until aout_FiltersDelete().
497 * \bug
498 * If request_vout is non NULL, obj is assumed to be an audio_output_t pointer.
500 aout_filters_t *aout_FiltersNew (vlc_object_t *obj,
501 const audio_sample_format_t *restrict infmt,
502 const audio_sample_format_t *restrict outfmt,
503 const aout_request_vout_t *request_vout,
504 const aout_filters_cfg_t *cfg)
506 aout_filters_t *filters = malloc (sizeof (*filters));
507 if (unlikely(filters == NULL))
508 return NULL;
510 filters->rate_filter = NULL;
511 filters->resampler = NULL;
512 filters->resampling = 0;
513 filters->count = 0;
515 /* Prepare format structure */
516 aout_FormatPrint (obj, "input", infmt);
517 audio_sample_format_t input_format = *infmt;
518 audio_sample_format_t output_format = *outfmt;
520 /* Callbacks (before reading values and also before return statement) */
521 if (request_vout != NULL)
522 var_AddCallback (obj, "visual", VisualizationCallback, NULL);
524 if (!AOUT_FMT_LINEAR(outfmt))
525 { /* Non-linear output: just convert formats, no filters/visu */
526 if (!AOUT_FMTS_IDENTICAL(infmt, outfmt))
528 aout_FormatsPrint (obj, "pass-through:", infmt, outfmt);
529 filters->tab[0] = FindConverter(obj, infmt, outfmt);
530 if (filters->tab[0] == NULL)
532 msg_Err (obj, "cannot setup pass-through");
533 goto error;
535 filters->count++;
537 return filters;
539 if (aout_FormatNbChannels(outfmt) == 0)
541 msg_Warn (obj, "No ouput channel mask, cannot setup filters");
542 goto error;
545 assert(output_format.channel_type == AUDIO_CHANNEL_TYPE_BITMAP);
546 if (input_format.channel_type != output_format.channel_type)
548 /* Do the channel type conversion before any filters since audio
549 * converters and filters handle only AUDIO_CHANNEL_TYPE_BITMAP */
551 /* convert to the output format (minus resampling) if necessary */
552 output_format.i_rate = input_format.i_rate;
553 if (aout_FiltersPipelineCreate (obj, filters->tab, &filters->count,
554 AOUT_MAX_FILTERS, &input_format, &output_format,
555 cfg->headphones))
557 msg_Warn (obj, "cannot setup audio renderer pipeline");
558 /* Fallback to bitmap without any conversions */
559 input_format.channel_type = AUDIO_CHANNEL_TYPE_BITMAP;
560 aout_FormatPrepare(&input_format);
562 else
563 input_format = output_format;
566 if (aout_FormatNbChannels(&input_format) == 0)
568 /* The input channel map is unknown, use the WAVE one and add a
569 * converter that will drop extra channels that are not handled by VLC
570 * */
571 msg_Info(obj, "unknown channel map, using the WAVE channel layout.");
573 assert(input_format.i_channels > 0);
574 audio_sample_format_t input_phys_format = input_format;
575 aout_SetWavePhysicalChannels(&input_phys_format);
577 filter_t *f = FindConverter (obj, &input_format, &input_phys_format);
578 if (f == NULL)
580 msg_Err (obj, "cannot find channel converter");
581 goto error;
584 input_format = input_phys_format;
585 filters->tab[filters->count++] = f;
588 assert(input_format.channel_type == AUDIO_CHANNEL_TYPE_BITMAP);
590 /* parse user filter lists */
591 if (var_InheritBool (obj, "audio-time-stretch"))
593 if (AppendFilter(obj, "audio filter", "scaletempo",
594 filters, NULL, &input_format, &output_format, NULL) == 0)
595 filters->rate_filter = filters->tab[filters->count - 1];
598 if (cfg != NULL)
600 AppendRemapFilter(obj, filters, &input_format, &output_format,
601 cfg->remap);
603 if (input_format.i_channels > 2 && cfg->headphones)
604 AppendFilter(obj, "audio filter", "binauralizer", filters, NULL,
605 &input_format, &output_format, NULL);
608 /* Now add user filters */
609 char *str = var_InheritString (obj, "audio-filter");
610 if (str != NULL)
612 char *p = str, *name;
613 while ((name = strsep (&p, " :")) != NULL)
615 AppendFilter(obj, "audio filter", name, filters,
616 NULL, &input_format, &output_format, NULL);
618 free (str);
621 if (request_vout != NULL)
623 char *visual = var_InheritString (obj, "audio-visual");
624 if (visual != NULL && strcasecmp (visual, "none"))
625 AppendFilter(obj, "visualization", visual, filters,
626 request_vout, &input_format, &output_format, NULL);
627 free (visual);
630 /* convert to the output format (minus resampling) if necessary */
631 output_format.i_rate = input_format.i_rate;
632 if (aout_FiltersPipelineCreate (obj, filters->tab, &filters->count,
633 AOUT_MAX_FILTERS, &input_format, &output_format, false))
635 msg_Err (obj, "cannot setup filtering pipeline");
636 goto error;
638 input_format = output_format;
640 /* insert the resampler */
641 output_format.i_rate = outfmt->i_rate;
642 assert (AOUT_FMTS_IDENTICAL(&output_format, outfmt));
643 filters->resampler = FindResampler (obj, &input_format,
644 &output_format);
645 if (filters->resampler == NULL && input_format.i_rate != outfmt->i_rate)
647 msg_Err (obj, "cannot setup a resampler");
648 goto error;
650 if (filters->rate_filter == NULL)
651 filters->rate_filter = filters->resampler;
653 return filters;
655 error:
656 aout_FiltersPipelineDestroy (filters->tab, filters->count);
657 if (request_vout != NULL)
658 var_DelCallback (obj, "visual", VisualizationCallback, NULL);
659 free (filters);
660 return NULL;
663 #undef aout_FiltersDelete
665 * Destroys a chain of audio filters.
666 * \param obj object used with aout_FiltersNew()
667 * \param filters chain to be destroyed
668 * \bug
669 * obj must be NULL iff request_vout was NULL in aout_FiltersNew()
670 * (this implies obj is an audio_output_t pointer if non NULL).
672 void aout_FiltersDelete (vlc_object_t *obj, aout_filters_t *filters)
674 if (filters->resampler != NULL)
675 aout_FiltersPipelineDestroy (&filters->resampler, 1);
676 aout_FiltersPipelineDestroy (filters->tab, filters->count);
677 if (obj != NULL)
678 var_DelCallback (obj, "visual", VisualizationCallback, NULL);
679 free (filters);
682 bool aout_FiltersCanResample (aout_filters_t *filters)
684 return (filters->resampler != NULL);
687 bool aout_FiltersAdjustResampling (aout_filters_t *filters, int adjust)
689 if (filters->resampler == NULL)
690 return false;
692 if (adjust)
693 filters->resampling += adjust;
694 else
695 filters->resampling = 0;
696 return filters->resampling != 0;
699 block_t *aout_FiltersPlay (aout_filters_t *filters, block_t *block, int rate)
701 int nominal_rate = 0;
703 if (rate != INPUT_RATE_DEFAULT)
705 filter_t *rate_filter = filters->rate_filter;
707 if (rate_filter == NULL)
708 goto drop; /* Without linear, non-nominal rate is impossible. */
710 /* Override input rate */
711 nominal_rate = rate_filter->fmt_in.audio.i_rate;
712 rate_filter->fmt_in.audio.i_rate =
713 (nominal_rate * INPUT_RATE_DEFAULT) / rate;
716 block = aout_FiltersPipelinePlay (filters->tab, filters->count, block);
717 if (filters->resampler != NULL)
718 { /* NOTE: the resampler needs to run even if resampling is 0.
719 * The decoder and output rates can still be different. */
720 filters->resampler->fmt_in.audio.i_rate += filters->resampling;
721 block = aout_FiltersPipelinePlay (&filters->resampler, 1, block);
722 filters->resampler->fmt_in.audio.i_rate -= filters->resampling;
725 if (nominal_rate != 0)
726 { /* Restore input rate */
727 assert (filters->rate_filter != NULL);
728 filters->rate_filter->fmt_in.audio.i_rate = nominal_rate;
730 return block;
732 drop:
733 block_Release (block);
734 return NULL;
737 block_t *aout_FiltersDrain (aout_filters_t *filters)
739 /* Drain the filters pipeline */
740 block_t *block = aout_FiltersPipelineDrain (filters->tab, filters->count);
742 if (filters->resampler != NULL)
744 block_t *chain = NULL;
746 filters->resampler->fmt_in.audio.i_rate += filters->resampling;
748 if (block)
750 /* Resample the drained block from the filters pipeline */
751 block = aout_FiltersPipelinePlay (&filters->resampler, 1, block);
752 if (block)
753 block_ChainAppend (&chain, block);
756 /* Drain the resampler filter */
757 block = aout_FiltersPipelineDrain (&filters->resampler, 1);
758 if (block)
759 block_ChainAppend (&chain, block);
761 filters->resampler->fmt_in.audio.i_rate -= filters->resampling;
763 return chain ? block_ChainGather (chain) : NULL;
765 else
766 return block;
769 void aout_FiltersFlush (aout_filters_t *filters)
771 aout_FiltersPipelineFlush (filters->tab, filters->count);
773 if (filters->resampler != NULL)
774 aout_FiltersPipelineFlush (&filters->resampler, 1);
777 void aout_FiltersChangeViewpoint (aout_filters_t *filters,
778 const vlc_viewpoint_t *vp)
780 aout_FiltersPipelineChangeViewpoint (filters->tab, filters->count, vp);