diff options
Diffstat (limited to 'audio/filter/af.c')
-rw-r--r-- | audio/filter/af.c | 260 |
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) { |