summaryrefslogtreecommitdiffstats
path: root/audio/filter/af.c
diff options
context:
space:
mode:
Diffstat (limited to 'audio/filter/af.c')
-rw-r--r--audio/filter/af.c1078
1 files changed, 567 insertions, 511 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);
+ return af_config_equals(&s->output, &s->filter_output) ? AF_OK : AF_ERROR;
}
/* Initialize the stream "s". This function creates a new filter list
@@ -471,221 +572,176 @@ static void af_downmix(struct af_stream* s)
If one of the prefered output parameters is 0 the one that needs
no conversion is used (i.e. the output format in the last filter).
The return value is 0 if success and -1 if failure */
-int af_init(struct af_stream* s)
-{
- struct MPOpts *opts = s->opts;
- int i=0;
-
- // Sanity check
- if(!s) return -1;
-
- // Precaution in case caller is misbehaving
- s->input.audio = s->output.audio = NULL;
- s->input.len = s->output.len = 0;
-
- // Figure out how fast the machine is
- if(AF_INIT_AUTO == (AF_INIT_TYPE_MASK & s->cfg.force))
- s->cfg.force = (s->cfg.force & ~AF_INIT_TYPE_MASK) | AF_INIT_TYPE;
-
- // Check if this is the first call
- if(!s->first){
- // Append a downmix pan filter at the beginning of the chain if needed
- if (s->input.nch != opts->audio_output_channels
- && opts->audio_output_channels == 2)
- af_downmix(s);
- // Add all filters in the list (if there are any)
- if (s->cfg.list) {
- while(s->cfg.list[i]){
- if(!af_append(s,s->last,s->cfg.list[i++]))
- return -1;
- }
- }
- }
-
- // If we do not have any filters otherwise
- // add dummy to make automatic format conversion work
- if (!s->first && !af_append(s, s->first, "dummy"))
- return -1;
-
- // Init filters
- if(AF_OK != af_reinit(s,s->first))
- return -1;
-
- // make sure the chain is not empty and valid (e.g. because of AF_DETACH)
- if (!s->first)
- if (!af_append(s,s->first,"dummy") || AF_OK != af_reinit(s,s->first))
- return -1;
-
- // Check output format
- if((AF_INIT_TYPE_MASK & s->cfg.force) != AF_INIT_FORCE){
- struct af_instance* af = NULL; // New filter
- // Check output frequency if not OK fix with resample
- if(s->output.rate && s->last->data->rate!=s->output.rate){
- // try to find a filter that can change samplrate
- af = af_control_any_rev(s, AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET,
- &(s->output.rate));
- if (!af) {
- char *resampler = "lavrresample";
- if((AF_INIT_TYPE_MASK & s->cfg.force) == AF_INIT_SLOW){
- if(!strcmp(s->first->info->name,"format"))
- af = af_append(s,s->first,resampler);
- else
- af = af_prepend(s,s->first,resampler);
- }
- else{
- if(!strcmp(s->last->info->name,"format"))
- af = af_prepend(s,s->last,resampler);
- else
- af = af_append(s,s->last,resampler);
- }
- // Init the new filter
- if(!af || (AF_OK != af->control(af,AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET,
- &(s->output.rate))))
- return -1;
- }
- if(AF_OK != af_reinit(s,af))
- return -1;
+int af_init(struct af_stream *s)
+{
+ int i = 0;
+
+ // Sanity check
+ if (!s)
+ return -1;
+
+ // Precaution in case caller is misbehaving
+ s->input.audio = s->output.audio = NULL;
+ s->input.len = s->output.len = 0;
+
+ // Check if this is the first call
+ if (s->first->next == s->last) {
+ // Add all filters in the list (if there are any)
+ if (s->cfg.list) {
+ while (s->cfg.list[i]) {
+ if (!af_prepend(s, s->last, s->cfg.list[i++]))
+ return -1;
+ }
+ }
}
+
+ remove_auto_inserted_filters(s);
+
+ // Init filters
+ if (AF_OK != af_reinit(s))
+ return -1;
+
if (AF_OK != fixup_output_format(s)) {
- // Something is stuffed audio out will not work
- mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Unable to setup filter system can not"
- " meet sound-card demands, please send a bug report. \n");
- af_uninit(s);
- return -1;
+ // Something is stuffed audio out will not work
+ mp_msg(
+ MSGT_AFILTER, MSGL_ERR,
+ "[libaf] Unable to setup filter system can not"
+ " meet sound-card demands, please send a bug report. \n");
+ af_uninit(s);
+ return -1;
}
- }
- return 0;
+ return 0;
}
/* Add filter during execution. This function adds the filter "name"
to the stream s. The filter will be inserted somewhere nice in the
list of filters. The return value is a pointer to the new filter,
If the filter couldn't be added the return value is NULL. */
-struct af_instance* af_add(struct af_stream* s, char* name){
- struct af_instance* new;
- // Sanity check
- if(!s || !s->first || !name)
- return NULL;
- // Insert the filter somewhere nice
- if(!strcmp(s->first->info->name,"format"))
- new = af_append(s, s->first, name);
- else
- new = af_prepend(s, s->first, name);
- if(!new)
- return NULL;
-
- // Reinitalize the filter list
- if(AF_OK != af_reinit(s, s->first) ||
- AF_OK != fixup_output_format(s)){
- while (s->first)
- af_remove(s, s->first);
- af_init(s);
- return NULL;
- }
- return new;
+struct af_instance *af_add(struct af_stream *s, char *name)
+{
+ struct af_instance *new;
+ // Sanity check
+ if (!s || !s->first || !name)
+ return NULL;
+ // Insert the filter somewhere nice
+ if (af_is_conversion_filter(s->first->next))
+ new = af_append(s, s->first->next, name);
+ else
+ new = af_prepend(s, s->first->next, name);
+ if (!new)
+ return NULL;
+
+ // Reinitalize the filter list
+ if (AF_OK != af_reinit(s) ||
+ AF_OK != fixup_output_format(s)) {
+ while (s->first)
+ af_remove(s, s->first);
+ af_init(s);
+ return NULL;
+ }
+ return new;
}
// Filter data chunk through the filters in the list
-struct mp_audio* af_play(struct af_stream* s, struct mp_audio* data)
+struct mp_audio *af_play(struct af_stream *s, struct mp_audio *data)
{
- struct af_instance* af=s->first;
- // Iterate through all filters
- do{
- if (data->len <= 0) break;
- data=af->play(af,data);
- af=af->next;
- }while(af && data);
- return data;
+ struct af_instance *af = s->first;
+ // Iterate through all filters
+ do {
+ if