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.c260
1 files changed, 131 insertions, 129 deletions
diff --git a/audio/filter/af.c b/audio/filter/af.c
index 21b0982692..a132965295 100644
--- a/audio/filter/af.c
+++ b/audio/filter/af.c
@@ -31,7 +31,6 @@
#include "af.h"
// Static list of filters
-extern const struct af_info af_info_delay;
extern const struct af_info af_info_channels;
extern const struct af_info af_info_format;
extern const struct af_info af_info_volume;
@@ -46,7 +45,6 @@ extern const struct af_info af_info_lavfi;
extern const struct af_info af_info_rubberband;
static const struct af_info *const filter_list[] = {
- &af_info_delay,
&af_info_channels,
&af_info_format,
&af_info_volume,
@@ -256,6 +254,8 @@ static void af_print_filter_chain(struct af_stream *s, struct af_instance *at,
mp_snprintf_cat(b, sizeof(b), "\"%s\" ", af->label);
if (af->data)
mp_snprintf_cat(b, sizeof(b), "%s", mp_audio_config_to_str(af->data));
+ if (af->auto_inserted)
+ mp_snprintf_cat(b, sizeof(b), " [a]");
if (af == at)
mp_snprintf_cat(b, sizeof(b), " <-");
MP_MSG(s, msg_level, "%s\n", b);
@@ -266,164 +266,156 @@ static void af_print_filter_chain(struct af_stream *s, struct af_instance *at,
MP_MSG(s, msg_level, " [ao] %s\n", mp_audio_config_to_str(&s->output));
}
-// 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 (or is closer)
-// 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)
+static void reset_formats(struct af_stream *s)
{
- 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;
- int dstfmt = in.format;
- char *filter = "lavrresample";
- if (!af_lavrresample_test_conversion(actual.format, dstfmt))
- return AF_ERROR;
- if (strcmp(filter, prev->info->name) == 0) {
- if (prev->control(prev, AF_CONTROL_SET_FORMAT, &dstfmt) == AF_OK) {
- *p_af = prev;
- return AF_OK;
- }
- return AF_ERROR;
- }
- struct af_instance *new = af_prepend(s, af, filter, NULL);
- if (new == NULL)
- return AF_ERROR;
- new->auto_inserted = true;
- if (AF_OK != (rv = new->control(new, AF_CONTROL_SET_FORMAT, &dstfmt))) {
- af_remove(s, new);
- return rv;
+ struct mp_audio none = {0};
+ for (struct af_instance *af = s->first; af; af = af->next) {
+ if (af != s->first && af != s->last)
+ mp_audio_copy_config(af->data, &none);
}
- *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)
+static int filter_reinit(struct af_instance *af)
{
- 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_SET_CHANNELS, &in.channels) == AF_OK) {
- *p_af = prev;
- return AF_OK;
- }
- char *filter = "lavrresample";
- struct af_instance *new = af_prepend(s, af, filter, NULL);
- if (new == NULL)
+ assert(prev);
+
+ // Check if this is the first filter
+ struct mp_audio in = *prev->data;
+ // Reset just in case...
+ mp_audio_set_null_data(&in);
+
+ if (!mp_audio_config_valid(&in))
return AF_ERROR;
- new->auto_inserted = true;
- if (AF_OK != (rv = new->control(new, AF_CONTROL_SET_CHANNELS, &in.channels)))
- return rv;
- *p_af = new;
- return AF_OK;
+
+ af->fmt_in = in;
+ int rv = af->control(af, AF_CONTROL_REINIT, &in);
+ if (rv == AF_OK && !mp_audio_config_equals(&in, prev->data))
+ rv = AF_FALSE; // conversion filter needed
+ if (rv == AF_FALSE)
+ af->fmt_in = in;
+
+ if (rv == AF_OK) {
+ if (!mp_audio_config_valid(af->data))
+ return AF_ERROR;
+ af->fmt_out = *af->data;
+ }
+
+ return rv;
}
-static int af_fix_rate(struct af_stream *s, struct af_instance **p_af,
- struct mp_audio in)
+static int filter_reinit_with_conversion(struct af_stream *s, struct af_instance *af)
{
- 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_SET_RESAMPLE_RATE, &in.rate) == AF_OK) {
- *p_af = prev;
- return AF_OK;
+ int rv = filter_reinit(af);
+
+ // Conversion filter is needed
+ if (rv == AF_FALSE) {
+ // First try if we can change the output format of the previous
+ // filter to the input format the current filter is expecting.
+ struct mp_audio in = af->fmt_in;
+ if (af->prev != s->first && !mp_audio_config_equals(af->prev->data, &in)) {
+ // This should have been successful (because it succeeded
+ // before), even if just reverting to the old output format.
+ mp_audio_copy_config(af->prev->data, &in);
+ rv = filter_reinit(af->prev);
+ if (rv != AF_OK)
+ return rv;
+ }
+ if (!mp_audio_config_equals(af->prev->data, &in)) {
+ // Retry with conversion filter added.
+ struct af_instance *new =
+ af_prepend(s, af, "lavrresample", NULL);
+ if (!new)
+ return AF_ERROR;
+ new->auto_inserted = true;
+ mp_audio_copy_config(new->data, &in);
+ rv = filter_reinit(new);
+ if (rv != AF_OK)
+ af_remove(s, new);
+ }
+ if (rv == AF_OK)
+ rv = filter_reinit(af);
}
- char *filter = "lavrresample";
- struct af_instance *new = af_prepend(s, af, filter, NULL);
- if (new == NULL)
- return AF_ERROR;
- new->auto_inserted = true;
- if (AF_OK != (rv = new->control(new, AF_CONTROL_SET_RESAMPLE_RATE, &in.rate)))
- return rv;
- *p_af = new;
- return AF_OK;
+
+ return rv;
}
-static void reset_formats(struct af_stream *s)
+static int af_find_output_conversion(struct af_stream *s, struct mp_audio *cfg)
{
- for (struct af_instance *af = s->first; af; af = af->next) {
- af->control(af, AF_CONTROL_SET_RESAMPLE_RATE, &(int){0});
- af->control(af, AF_CONTROL_SET_CHANNELS, &(struct mp_chmap){0});
- af->control(af, AF_CONTROL_SET_FORMAT, &(int){0});
+ assert(mp_audio_config_valid(&s->output));
+ assert(s->initialized > 0);
+
+ if (mp_chmap_equals_reordered(&s->input.channels, &s->output.channels))
+ return AF_ERROR;
+
+ // Heuristic to detect point of conversion. If it looks like something
+ // more complicated is going on, better bail out.
+ // We expect that the last filter converts channels.
+ struct af_instance *conv = s->last->prev;
+ if (!conv->auto_inserted)
+ return AF_ERROR;
+ if (!(mp_chmap_equals_reordered(&conv->fmt_in.channels, &s->input.channels) &&
+ mp_chmap_equals_reordered(&conv->fmt_out.channels, &s->output.channels)))
+ return AF_ERROR;
+ // Also, should be the only one which does auto conversion.
+ for (struct af_instance *af = s->first->next; af != s->last; af = af->next)
+ {
+ if (af != conv && af->auto_inserted &&
+ !mp_chmap_equals_reordered(&af->fmt_in.channels, &af->fmt_out.channels))
+ return AF_ERROR;
}
+ // And not if it's the only filter.
+ if (conv->prev == s->first && conv->next == s->last)
+ return AF_ERROR;
+
+ *cfg = s->output;
+ return AF_OK;
}
// Return AF_OK on success or AF_ERROR on failure.
-// Warning:
-// A failed af_reinit() leaves the audio chain behind in a useless, broken
-// state (for example, format filters that were tentatively inserted stay
-// inserted).
-// In that case, you should always rebuild the filter chain, or abort.
-static int af_reinit(struct af_stream *s)
-{
+static int af_do_reinit(struct af_stream *s, bool second_pass)
+{
+ struct mp_audio convert_early = {0};
+ if (second_pass) {
+ // If a channel conversion happens, and it is done by an auto-inserted
+ // filter, then insert a filter to convert it early. Otherwise, do
+ // nothing and return immediately.
+ if (af_find_output_conversion(s, &convert_early) != AF_OK)
+ return AF_OK;
+ }
+
remove_auto_inserted_filters(s);
af_chain_forget_frames(s);
reset_formats(s);
s->first->fmt_in = s->first->fmt_out = s->input;
+
+ if (mp_audio_config_valid(&convert_early)) {
+ struct af_instance *new = af_prepend(s, s->first, "lavrresample", NULL);
+ if (!new)
+ return AF_ERROR;
+ new->auto_inserted = true;
+ mp_audio_copy_config(new->data, &convert_early);
+ int rv = filter_reinit(new);
+ if (rv != AF_DETACH && rv != AF_OK)
+ return AF_ERROR;
+ MP_VERBOSE(s, "Moving up output conversion.\n");
+ }
+
// 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;
- // Up to 4 retries per filter (channel, rate, format conversions)
- int max_retry = 4;
- 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...
- mp_audio_set_null_data(&in);
-
- if (!mp_audio_config_valid(&in))
- goto error;
+ int rv = filter_reinit_with_conversion(s, af);
- af->fmt_in = in;
- int rv = af->control(af, AF_CONTROL_REINIT, &in);
- if (rv == AF_OK && !mp_audio_config_equals(&in, af->prev->data))
- rv = AF_FALSE; // conversion filter needed
switch (rv) {
case AF_OK:
- if (!mp_audio_config_valid(af->data))
- goto error;
- af->fmt_out = *af->data;
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;
- }
+ case AF_FALSE: {
// If the format conversion is (probably) caused by spdif, then
// (as a feature) drop the filter, instead of failing hard.
int fmt_in1 = af->prev->data->format;
- int fmt_in2 = in.format;
+ int fmt_in2 = af->fmt_in.format;
if (af_fmt_is_valid(fmt_in1) && af_fmt_is_valid(fmt_in2)) {
bool spd1 = af_fmt_is_spdif(fmt_in1);
bool spd2 = af_fmt_is_spdif(fmt_in2);
@@ -434,7 +426,6 @@ static int af_reinit(struct af_stream *s)
struct af_instance *aft = af->prev;
af_remove(s, af);
af = aft->next;
- retry++;
continue;
}
}
@@ -452,8 +443,6 @@ static int af_reinit(struct af_stream *s)
af->info->name, rv);
goto error;
}
- if (af && !af->auto_inserted)
- retry = 0;
}
/* Set previously unset fields in s->output to those of the filter chain
@@ -477,6 +466,19 @@ error:
return AF_ERROR;
}
+static int af_reinit(struct af_stream *s)
+{
+ int r = af_do_reinit(s, false);
+ if (r == AF_OK && mp_audio_config_valid(&s->output)) {
+ r = af_do_reinit(s, true);
+ if (r != AF_OK) {
+ MP_ERR(s, "Failed second pass filter negotiation.\n");
+ r = af_do_reinit(s, false);
+ }
+ }
+ return r;
+}
+
// Uninit and remove all filters
void af_uninit(struct af_stream *s)
{