diff options
Diffstat (limited to 'audio/filter')
-rw-r--r-- | audio/filter/af.c | 1078 | ||||
-rw-r--r-- | audio/filter/af.h | 63 | ||||
-rw-r--r-- | audio/filter/af_bs2b.c | 8 | ||||
-rw-r--r-- | audio/filter/af_center.c | 5 | ||||
-rw-r--r-- | audio/filter/af_channels.c | 47 | ||||
-rw-r--r-- | audio/filter/af_delay.c | 5 | ||||
-rw-r--r-- | audio/filter/af_drc.c | 13 | ||||
-rw-r--r-- | audio/filter/af_dummy.c | 4 | ||||
-rw-r--r-- | audio/filter/af_equalizer.c | 30 | ||||
-rw-r--r-- | audio/filter/af_export.c | 6 | ||||
-rw-r--r-- | audio/filter/af_extrastereo.c | 17 | ||||
-rw-r--r-- | audio/filter/af_force.c | 146 | ||||
-rw-r--r-- | audio/filter/af_format.c | 32 | ||||
-rw-r--r-- | audio/filter/af_hrtf.c | 11 | ||||
-rw-r--r-- | audio/filter/af_karaoke.c | 6 | ||||
-rw-r--r-- | audio/filter/af_ladspa.c | 38 | ||||
-rw-r--r-- | audio/filter/af_lavcac3enc.c | 29 | ||||
-rw-r--r-- | audio/filter/af_lavrresample.c | 171 | ||||
-rw-r--r-- | audio/filter/af_pan.c | 24 | ||||
-rw-r--r-- | audio/filter/af_scaletempo.c | 14 | ||||
-rw-r--r-- | audio/filter/af_sinesuppress.c | 19 | ||||
-rw-r--r-- | audio/filter/af_sub.c | 5 | ||||
-rw-r--r-- | audio/filter/af_surround.c | 12 | ||||
-rw-r--r-- | audio/filter/af_sweep.c | 8 | ||||
-rw-r--r-- | audio/filter/af_tools.c | 4 | ||||
-rw-r--r-- | audio/filter/af_volume.c | 33 | ||||
-rw-r--r-- | audio/filter/control.h | 127 |
27 files changed, 988 insertions, 967 deletions
diff --git a/audio/filter/af.c b/audio/filter/af.c index ad43e5fca7..137e7cc407 100644 --- a/audio/filter/af.c +++ b/audio/filter/af.c @@ -20,6 +20,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <assert.h> #include "af.h" @@ -28,6 +29,7 @@ extern struct af_info af_info_dummy; extern struct af_info af_info_delay; extern struct af_info af_info_channels; extern struct af_info af_info_format; +extern struct af_info af_info_force; extern struct af_info af_info_volume; extern struct af_info af_info_equalizer; extern struct af_info af_info_pan; @@ -47,232 +49,389 @@ extern struct af_info af_info_karaoke; extern struct af_info af_info_scaletempo; extern struct af_info af_info_bs2b; -static struct af_info* filter_list[]={ - &af_info_dummy, - &af_info_delay, - &af_info_channels, - &af_info_format, - &af_info_volume, - &af_info_equalizer, - &af_info_pan, - &af_info_surround, - &af_info_sub, +static struct af_info* filter_list[] = { + &af_info_dummy, + &af_info_delay, + &af_info_channels, + &af_info_force, + &af_info_volume, + &af_info_equalizer, + &af_info_pan, + &af_info_surround, + &af_info_sub, #ifdef HAVE_SYS_MMAN_H - &af_info_export, + &af_info_export, #endif - &af_info_drc, - &af_info_extrastereo, - &af_info_lavcac3enc, - &af_info_lavrresample, - &af_info_sweep, - &af_info_hrtf, + &af_info_drc, + &af_info_extrastereo, + &af_info_lavcac3enc, + &af_info_lavrresample, + &af_info_sweep, + &af_info_hrtf, #ifdef CONFIG_LADSPA - &af_info_ladspa, + &af_info_ladspa, #endif - &af_info_center, - &af_info_sinesuppress, - &af_info_karaoke, - &af_info_scaletempo, + &af_info_center, + &af_info_sinesuppress, + &af_info_karaoke, + &af_info_scaletempo, #ifdef CONFIG_LIBBS2B - &af_info_bs2b, + &af_info_bs2b, #endif - NULL + // Must come last, because it's the fallback format conversion filter + &af_info_format, + NULL }; -// CPU speed -int* af_cpu_speed = NULL; +static bool af_config_equals(struct mp_audio *a, struct mp_audio *b) +{ + return a->format == b->format + && mp_chmap_equals(&a->channels, &b->channels) + && a->rate == b->rate; +} + +static void af_copy_unset_fields(struct mp_audio *dst, struct mp_audio *src) +{ + if (dst->format == AF_FORMAT_UNKNOWN) + mp_audio_set_format(dst, src->format); + if (dst->nch == 0) + mp_audio_set_channels(dst, &src->channels); + if (dst->rate == 0) + dst->rate = src->rate; +} + +static int input_control(struct af_instance* af, int cmd, void* arg) +{ + switch (cmd) { + case AF_CONTROL_REINIT: + assert(arg == &((struct af_stream *)af->setup)->input); + return AF_OK; + } + return AF_UNKNOWN; +} + +static int output_control(struct af_instance* af, int cmd, void* arg) +{ + struct af_stream *s = af->setup; + struct mp_audio *output = &s->output; + struct mp_audio *filter_output = &s->filter_output; + + switch (cmd) { + case AF_CONTROL_REINIT: { + struct mp_audio *in = arg; + struct mp_audio orig_in = *in; + + *filter_output = *output; + af_copy_unset_fields(filter_output, in); + *in = *filter_output; + return af_config_equals(in, &orig_in) ? AF_OK : AF_FALSE; + } + } + return AF_UNKNOWN; +} + +static struct mp_audio *dummy_play(struct af_instance* af, struct mp_audio* data) +{ + return data; +} /* Find a filter in the static list of filters using it's name. This function is used internally */ -static struct af_info* af_find(char*name) +static struct af_info *af_find(char *name) { - int i=0; - while(filter_list[i]){ - if(!strcmp(filter_list[i]->name,name)) - return filter_list[i]; - i++; - } - mp_msg(MSGT_AFILTER, MSGL_ERR, "Couldn't find audio filter '%s'\n",name); - return NULL; + int i = 0; + while (filter_list[i]) { + if (!strcmp(filter_list[i]->name, name)) + return filter_list[i]; + i++; + } + mp_msg(MSGT_AFILTER, MSGL_ERR, "Couldn't find audio filter '%s'\n", name); + return NULL; } /* Find filter in the dynamic filter list using it's name This function is used for finding already initialized filters */ -struct af_instance* af_get(struct af_stream* s, char* name) -{ - struct af_instance* af=s->first; - // Find the filter - while(af != NULL){ - if(!strcmp(af->info->name,name)) - return af; - af=af->next; - } - return NULL; -} - -/*/ Function for creating a new filter of type name. The name may - contain the commandline parameters for the filter */ -static struct af_instance* af_create(struct af_stream* s, const char* name_with_cmd) -{ - char* name = strdup(name_with_cmd); - char* cmdline = name; - - // Allocate space for the new filter and reset all pointers - struct af_instance* new=malloc(sizeof(struct af_instance)); - if (!name || !new) { - mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Could not allocate memory\n"); - goto err_out; - } - memset(new,0,sizeof(struct af_instance)); - - // Check for commandline parameters - char *skip = strstr(cmdline, "="); - if (skip) { - *skip = '\0'; // for name - cmdline = skip + 1; - } else { - cmdline = NULL; - } - - // Find filter from name - if(NULL == (new->info=af_find(name))) - goto err_out; - - /* Make sure that the filter is not already in the list if it is - non-reentrant */ - if(new->info->flags & AF_FLAGS_NOT_REENTRANT){ - if(af_get(s,name)){ - mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] There can only be one instance of" - " the filter '%s' in each stream\n",name); - goto err_out; +struct af_instance *af_get(struct af_stream *s, char *name) +{ + struct af_instance *af = s->first; + // Find the filter + while (af != NULL) { + if (!strcmp(af->info->name, name)) + return af; + af = af->next; } - } + return NULL; +} - mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Adding filter %s \n",name); +/* Function for creating a new filter of type name.The name may +contain the commandline parameters for the filter */ +static struct af_instance *af_create(struct af_stream *s, + const char *name_with_cmd) +{ + char *name = strdup(name_with_cmd); + char *cmdline = name; - // Initialize the new filter - if(AF_OK == new->info->open(new) && - AF_ERROR < new->control(new,AF_CONTROL_POST_CREATE,&s->cfg)){ - if(cmdline){ - if(AF_ERROR>=new->control(new,AF_CONTROL_COMMAND_LINE,cmdline)) + // Allocate space for the new filter and reset all pointers + struct af_instance *new = malloc(sizeof(struct af_instance)); + if (!name || !new) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Could not allocate memory\n"); goto err_out; } - free(name); - return new; - } + memset(new, 0, sizeof(struct af_instance)); + + // Check for commandline parameters + char *skip = strstr(cmdline, "="); + if (skip) { + *skip = '\0'; // for name + cmdline = skip + 1; + } else { + cmdline = NULL; + } + + // Find filter from name + if (NULL == (new->info = af_find(name))) + goto err_out; + + /* Make sure that the filter is not already in the list if it is + non-reentrant */ + if (new->info->flags & AF_FLAGS_NOT_REENTRANT) { + if (af_get(s, name)) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] There can only be one " + "instance of the filter '%s' in each stream\n", name); + goto err_out; + } + } + + mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Adding filter %s \n", name); + + // Initialize the new filter + if (AF_OK == new->info->open(new)) { + if (cmdline) { + if (AF_ERROR >= new->control(new, AF_CONTROL_COMMAND_LINE, cmdline)) + goto err_out; + } + free(name); + return new; + } err_out: - free(new); - mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Couldn't create or open audio filter '%s'\n", - name); - free(name); - return NULL; + free(new); + mp_msg(MSGT_AFILTER, MSGL_ERR, + "[libaf] Couldn't create or open audio filter '%s'\n", name); + free(name); + return NULL; } /* Create and insert a new filter of type name before the filter in the argument. This function can be called during runtime, the return value is the new filter */ -static struct af_instance* af_prepend(struct af_stream* s, struct af_instance* af, const char* name) +static struct af_instance *af_prepend(struct af_stream *s, + struct af_instance *af, + const char *name) { - // Create the new filter and make sure it is OK - struct af_instance* new=af_create(s,name); - if(!new) - return NULL; - // Update pointers - new->next=af; - if(af){ - new->prev=af->prev; - af->prev=new; - } - else - s->last=new; - if(new->prev) - new->prev->next=new; - else - s->first=new; - return new; + if (!af) + af = s->last; + if (af == s->first) + af = s->first->next; + // Create the new filter and make sure it is OK + struct af_instance *new = af_create(s, name); + if (!new) + return NULL; + // Update pointers + new->next = af; + new->prev = af->prev; + af->prev = new; + new->prev->next = new; + return new; } /* Create and insert a new filter of type name after the filter in the argument. This function can be called during runtime, the return value is the new filter */ -static struct af_instance* af_append(struct af_stream* s, struct af_instance* af, const char* name) +static struct af_instance *af_append(struct af_stream *s, + struct af_instance *af, + const char *name) { - // Create the new filter and make sure it is OK - struct af_instance* new=af_create(s,name); - if(!new) - return NULL; - // Update pointers - new->prev=af; - if(af){ - new->next=af->next; - af->next=new; - } - else - s->first=new; - if(new->next) - new->next->prev=new; - else - s->last=new; - return new; + if (!af) + af = s->first; + if (af == s->last) + af = s->last->prev; + // Create the new filter and make sure it is OK + struct af_instance *new = af_create(s, name); + if (!new) + return NULL; + // Update pointers + new->prev = af; + new->next = af->next; + af->next = new; + new->next->prev = new; + return new; } // Uninit and remove the filter "af" -void af_remove(struct af_stream* s, struct af_instance* af) +void af_remove(struct af_stream *s, struct af_instance *af) { - if(!af) return; + if (!af) + return; + + if (af == s->first || af == s->last) + return; - // Print friendly message - mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Removing filter %s \n",af->info->name); + // Print friendly message + mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Removing filter %s \n", + af->info->name); - // Notify filter before changing anything - af->control(af,AF_CONTROL_PRE_DESTROY,0); + // Notify filter before changing anything + af->control(af, AF_CONTROL_PRE_DESTROY, 0); - // Detach pointers - if(af->prev) - af->prev->next=af->next; - else - s->first=af->next; - if(af->next) - af->next->prev=af->prev; - else - s->last=af->prev; + // Detach pointers + af->prev->next = af->next; + af->next->prev = af->prev; - // Uninitialize af and free memory - af->uninit(af); - free(af); + af->uninit(af); + free(af); } -static void print_fmt(struct mp_audio *d) +static void remove_auto_inserted_filters(struct af_stream *s) { - if (d) { - mp_msg(MSGT_AFILTER, MSGL_V, "%dHz/%dch/%s", d->rate, d->nch, - af_fmt2str_short(d->format)); - } else { - mp_msg(MSGT_AFILTER, MSGL_V, "(?)"); +repeat: + for (struct af_instance *af = s->first; af; af = af->next) { + if (af->auto_inserted) { + af_remove(s, af); + goto repeat; + } } } -static void af_print_filter_chain(struct af_stream* s) +static void af_print_filter_chain(struct af_stream *s, struct af_instance *at, + int msg_level) { - mp_msg(MSGT_AFILTER, MSGL_V, "Audio filter chain:\n"); - - mp_msg(MSGT_AFILTER, MSGL_V, " [in] "); - print_fmt(&s->input); - mp_msg(MSGT_AFILTER, MSGL_V, "\n"); + mp_msg(MSGT_AFILTER, msg_level, "Audio filter chain:\n"); struct af_instance *af = s->first; while (af) { - mp_msg(MSGT_AFILTER, MSGL_V, " [%s] ", af->info->name); - print_fmt(af->data); - mp_msg(MSGT_AFILTER, MSGL_V, "\n"); + mp_msg(MSGT_AFILTER, msg_level, " [%s] ", af->info->name); + if (af->data) { + char *info = mp_audio_config_to_str(af->data); + mp_msg(MSGT_AFILTER, msg_level, "%s", info); + talloc_free(info); + } + if (af == at) + mp_msg(MSGT_AFILTER, msg_level, " <-"); + mp_msg(MSGT_AFILTER, msg_level, "\n"); af = af->next; } - mp_msg(MSGT_AFILTER, MSGL_V, " [out] "); - print_fmt(&s->output); - mp_msg(MSGT_AFILTER, MSGL_V, "\n"); + mp_msg(MSGT_AFILTER, msg_level, " [ao] "); + char *info = mp_audio_config_to_str(&s->output); + mp_msg(MSGT_AFILTER, msg_level, "%s\n", info); + talloc_free(info); +} + +static int af_count_filters(struct af_stream *s) +{ + int count = 0; + for (struct af_instance *af = s->first; af; af = af->next) + count++; + return count; +} + +static const char *af_find_conversion_filter(int srcfmt, int dstfmt) +{ + for (int n = 0; filter_list[n]; n++) { + struct af_info *af = filter_list[n]; + if (af->test_conversion && af->test_conversion(srcfmt, dstfmt)) + return af->name; + } + return NULL; +} + +static bool af_is_conversion_filter(struct af_instance *af) +{ + return af && af->info->test_conversion != NULL; +} + +// in is what af can take as input - insert a conversion filter if the actual +// input format doesn't match what af expects. +// Returns: +// AF_OK: must call af_reinit() or equivalent, format matches +// AF_FALSE: nothing was changed, format matches +// else: error +static int af_fix_format_conversion(struct af_stream *s, + struct af_instance **p_af, + struct mp_audio in) +{ + int rv; + struct af_instance *af = *p_af; + struct af_instance *prev = af->prev; + struct mp_audio actual = *prev->data; + if (actual.format == in.format) + return AF_FALSE; + if (prev->control(prev, AF_CONTROL_FORMAT_FMT, &in.format) == AF_OK) { + *p_af = prev; + return AF_OK; + } + const char *filter = af_find_conversion_filter(actual.format, in.format); + if (!filter) + return AF_ERROR; + struct af_instance *new = af_prepend(s, af, filter); + if (new == NULL) + return AF_ERROR; + new->auto_inserted = true; + if (AF_OK != (rv = new->control(new, AF_CONTROL_FORMAT_FMT, &in.format))) + return rv; + *p_af = new; + return AF_OK; +} + +// same as af_fix_format_conversion - only wrt. channels +static int af_fix_channels(struct af_stream *s, struct af_instance **p_af, + struct mp_audio in) +{ + int rv; + struct af_instance *af = *p_af; + struct af_instance *prev = af->prev; + struct mp_audio actual = *prev->data; + if (mp_chmap_equals(&actual.channels, &in.channels)) + return AF_FALSE; + if (prev->control(prev, AF_CONTROL_CHANNELS, &in.channels) == AF_OK) { + *p_af = prev; + return AF_OK; + } + const char *filter = "lavrresample"; + struct af_instance *new = af_prepend(s, af, filter); + if (new == NULL) + return AF_ERROR; + new->auto_inserted = true; + if (AF_OK != (rv = new->control(new, AF_CONTROL_CHANNELS, &in.channels))) + return rv; + *p_af = new; + return AF_OK; +} + +static int af_fix_rate(struct af_stream *s, struct af_instance **p_af, + struct mp_audio in) +{ + int rv; + struct af_instance *af = *p_af; + struct af_instance *prev = af->prev; + struct mp_audio actual = *prev->data; + if (actual.rate == in.rate) + return AF_FALSE; + if (prev->control(prev, AF_CONTROL_RESAMPLE_RATE, &in.rate) == AF_OK) { + *p_af = prev; + return AF_OK; + } + const char *filter = "lavrresample"; + struct af_instance *new = af_prepend(s, af, filter); + if (new == NULL) + return AF_ERROR; + new->auto_inserted = true; + if (AF_OK != (rv = new->control(new, AF_CONTROL_RESAMPLE_RATE, &in.rate))) + return rv; + *p_af = new; + return AF_OK; } // Warning: @@ -280,186 +439,128 @@ static void af_print_filter_chain(struct af_stream* s) // state (for example, format filters that were tentatively inserted stay // inserted). // In that case, you should always rebuild the filter chain, or abort. -int af_reinit(struct af_stream* s, struct af_instance* af) -{ - do{ - struct mp_audio in; // Format of the input to current filter - int rv=0; // Return value - - // Check if there are any filters left in the list - if(NULL == af){ - if(!(af=af_append(s,s->first,"dummy"))) - return AF_UNKNOWN; - else - return AF_ERROR; +// Also, note that for complete reinit, fixup_output_format() may have to be +// called after this function. +int af_reinit(struct af_stream *s) +{ + // Start with the second filter, as the first filter is the special input + // filter which needs no initialization. + struct af_instance *af = s->first->next; + int max_retry = af_count_filters(s) * 4; // up to 4 retries per filter + int retry = 0; + while (af) { + if (retry >= max_retry) + goto negotiate_error; + + // Check if this is the first filter + struct mp_audio in = *af->prev->data; + // Reset just in case... + in.audio = NULL; + in.len = 0; + + int rv = af->control(af, AF_CONTROL_REINIT, &in); + switch (rv) { + case AF_OK: + af = af->next; + break; + case AF_FALSE: { // Configuration filter is needed + if (af_fix_channels(s, &af, in) == AF_OK) { + retry++; + continue; + } + if (af_fix_rate(s, &af, in) == AF_OK) { + retry++; + continue; + } + // Do this last, to prevent "format->lavrresample" being added to + // the filter chain when output formats not supported by + // af_lavrresample are in use. + if (af_fix_format_conversion(s, &af, in) == AF_OK) { + retry++; + continue; + } + goto negotiate_error; + } + case AF_DETACH: { // Filter is redundant and wants to be unloaded + struct af_instance *aft = af->prev; // never NULL + af_remove(s, af); + af = aft->next; + break; + } + default: + mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Reinitialization did not " + "work, audio filter '%s' returned error code %i\n", + af->info->name, rv); + af_print_filter_chain(s, af, MSGL_ERR); + return AF_ERROR; + } } - // Check if this is the first filter - if(!af->prev) - memcpy(&in,&(s->input),sizeof(struct mp_audio)); - else - memcpy(&in,af->prev->data,sizeof(struct mp_audio)); - // Reset just in case... - in.audio=NULL; - in.len=0; - - rv = af->control(af,AF_CONTROL_REINIT,&in); - switch(rv){ - case AF_OK: - af = af->next; - break; - case AF_FALSE:{ // Configuration filter is needed - // Do auto insertion only if force is not specified - if((AF_INIT_TYPE_MASK & s->cfg.force) != AF_INIT_FORCE){ - struct af_instance* new = NULL; - // Insert channels filter - if((af->prev?af->prev->data->nch:s->input.nch) != in.nch){ - // Create channels filter - if(NULL == (new = af_prepend(s,af,"channels"))) - return AF_ERROR; - // Set number of output channels - if(AF_OK != (rv = new->control(new,AF_CONTROL_CHANNELS,&in.nch))) - return rv; - // Initialize channels filter - if(!new->prev) - memcpy(&in,&(s->input),sizeof(struct mp_audio)); - else - memcpy(&in,new->prev->data,sizeof(struct mp_audio)); - if(AF_OK != (rv = new->control(new,AF_CONTROL_REINIT,&in))) - return rv; - } - // Insert format filter - if((af->prev?af->prev->data->format:s->input.format) != in.format){ - // Create format filter - if(NULL == (new = af_prepend(s,af,"format"))) - return AF_ERROR; - // Set output bits per sample - in.format |= af_bits2fmt(in.bps*8); - if(AF_OK != (rv = new->control(new,AF_CONTROL_FORMAT_FMT,&in.format))) - return rv; - // Initialize format filter - if(!new->prev) - memcpy(&in,&(s->input),sizeof(struct mp_audio)); - else - memcpy(&in,new->prev->data,sizeof(struct mp_audio)); - if(AF_OK != (rv = new->control(new,AF_CONTROL_REINIT,&in))) - return rv; - } - if(!new){ // Should _never_ happen - mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Unable to correct audio format. " - "This error should never occur, please send a bug report.\n"); - return AF_ERROR; - } - af=new->next; - } - else { - mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Automatic filter insertion disabled " - "but formats do not match. Giving up.\n"); - return AF_ERROR; - } - break; - } - case AF_DETACH:{ // Filter is redundant and wants to be unloaded - // Do auto remove only if force is not specified - if((AF_INIT_TYPE_MASK & s->cfg.force) != AF_INIT_FORCE){ - struct af_instance* aft=af->prev; - af_remove(s,af); - if(aft) - af=aft->next; - else - af=s->first; // Restart configuration - } - break; - } - default: - mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Reinitialization did not work, audio" - " filter '%s' returned error code %i\n",af->info->name,rv); - return AF_ERROR; - } - }while(af); + af_print_filter_chain(s, NULL, MSGL_V); - af_print_filter_chain(s); + return AF_OK; - return AF_OK; +negotiate_error: + mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Unable to correct audio format. " + "This error should never occur, please send a bug report.\n"); + af_print_filter_chain(s, af, MSGL_ERR); + return AF_ERROR; } // Uninit and remove all filters -void af_uninit(struct af_stream* s) +void af_uninit(struct af_stream *s) { - while(s->first) - af_remove(s,s->first); + while (s->first->next && s->first->next != s->last) + af_remove(s, s->first->next); } -/** - * Extend the filter chain so we get the required output format at the end. - * \return AF_ERROR on error, AF_OK if successful. - */ -static int fixup_output_format(struct af_stream* s) -{ - struct af_instance* af = NULL; - // Check number of output channels fix if not OK - // If needed always inserted last -> easy to screw up other filters - if(s->output.nch && s->last->data->nch!=s->output.nch){ - if(!strcmp(s->last->info->name,"format")) - af = af_prepend(s,s->last,"channels"); - else - af = af_append(s,s->last,"channels"); - // Init the new filter - if(!af || (AF_OK != af->control(af,AF_CONTROL_CHANNELS,&(s->output.nch)))) - return AF_ERROR; - if(AF_OK != af_reinit(s,af)) - return AF_ERROR; - } - - // Check output format fix if not OK - if(s->output.format != AF_FORMAT_UNKNOWN && - s->last->data->format != s->output.format){ - if(strcmp(s->last->info->name,"format")) - af = af_append(s,s->last,"format"); - else - af = s->last; - // Init the new filter - s->output.format |= af_bits2fmt(s->output.bps*8); - if(!af || (AF_OK != af->control(af,AF_CONTROL_FORMAT_FMT,&(s->output.format)))) - return AF_ERROR; - if(AF_OK != af_reinit(s,af)) - return AF_ERROR; - } +struct af_stream *af_new(struct MPOpts *opts) +{ + struct af_stream *s = talloc_zero(NULL, struct af_stream); + static struct af_info in = { .name = "in" }; + s->first = talloc(s, struct af_instance); + *s->first = (struct af_instance) { + .info = &in, + .control = input_control, + .play = dummy_play, + .setup = s, + .data = &s->input, + .mul = 1.0, + }; + static struct af_info out = { .name = "out" }; + s->last = talloc(s, struct af_instance); + *s->last = (struct af_instance) { + .info = &out, + .control = output_control, + .play = dummy_play, + .setup = s, + .data = &s->filter_output, + .mul = 1.0, + }; + s->first->next = s->last; + s->last->prev = s->first; + return s; +} - // Re init again just in case - if(AF_OK != af_reinit(s,s->first)) - return AF_ERROR; - - if (s->output.format == AF_FORMAT_UNKNOWN) - s->output.format = s->last->data->format; - if (!s->output.nch) s->output.nch = s->last->data->nch; - if (!s->output.rate) s->output.rate = s->last->data->rate; - if((s->last->data->format != s->output.format) || - (s->last->data->nch != s->output.nch) || - (s->last->data->rate != s->output.rate)) { - return AF_ERROR; - } - return AF_OK; +void af_destroy(struct af_stream *s) +{ + af_uninit(s); + talloc_free(s); } -/** - * Automatic downmix to stereo in case the codec does not implement it. +/* + * Set previously unset fields in s->output to those of the filter chain + * output. This is used to make the output format fixed, and even if you insert + * new filters or change the input format, the output format won't change. + * \return AF_ERROR on error, AF_OK if successful. */ -static void af_downmix(struct af_stream* s) -{ - static const char * const downmix_strs[AF_NCH + 1] = { - /* FL FR RL RR FC LF AL AR */ - [3] = "pan=2:" "0.6:0:" "0:0.6:" "0.4:0.4", - [4] = "pan=2:" "0.6:0:" "0:0.6:" "0.4:0:" "0:0.4", - [5] = "pan=2:" "0.5:0:" "0:0.5:" "0.2:0:" "0:0.2:" "0.3:0.3", - [6] = "pan=2:" "0.4:0:" "0:0.4:" "0.2:0:" "0:0.2:" "0.3:0.3:" "0.1:0.1", - [7] = "pan=2:" "0.4:0:" "0:0.4:" "0.2:0:" "0:0.2:" "0.3:0.3:" "0.1:0:" "0:0.1", - [8] = "pan=2:" "0.4:0:" "0:0.4:" "0.15:0:" "0:0.15:" "0.25:0.25:" "0.1:0.1:" "0.1:0:" "0:0.1", - }; - const char *af_pan_str = downmix_strs[s->input.nch]; +static int fixup_output_format(struct af_stream *s) +{ + if (AF_OK != af_reinit(s)) + return AF_ERROR; - if (af_pan_str) - af_append(s, s->first, af_pan_str); + af_copy_unset_fields(&s->output, &s->filter_output); + |