mirror of https://github.com/mpv-player/mpv
audio: cleanup audio filter format negotiation
The algorithm and functionality is the same, but the code becomes much simpler and easier to follow. The assumption that there is only 1 conversion filter (lavrresample) helps with the simplification, but the main change is to use the same code for format/channels/rate. Get rid of the different AF_CONTROL_SET_* controls, and change the af->data parameters directly. (af->data is badly named, but essentially is a placeholder for the output format.) Also, instead of trying to use the af_reinit() loop to init inserted conversion filters or filters with changed output formats, do it inline, and move the common code to a filter_reinit() function. This gets rid of the awful retry variable. In general, this should not change any runtime behavior.
This commit is contained in:
parent
065bb635d9
commit
2eac58eaa9
|
@ -266,103 +266,79 @@ 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));
|
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)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
*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_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)
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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_SET_RESAMPLE_RATE, &in.rate) == AF_OK) {
|
|
||||||
*p_af = prev;
|
|
||||||
return AF_OK;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void reset_formats(struct af_stream *s)
|
static void reset_formats(struct af_stream *s)
|
||||||
{
|
{
|
||||||
|
struct mp_audio none = {0};
|
||||||
for (struct af_instance *af = s->first; af; af = af->next) {
|
for (struct af_instance *af = s->first; af; af = af->next) {
|
||||||
af->control(af, AF_CONTROL_SET_RESAMPLE_RATE, &(int){0});
|
if (af != s->first && af != s->last)
|
||||||
af->control(af, AF_CONTROL_SET_CHANNELS, &(struct mp_chmap){0});
|
mp_audio_copy_config(af->data, &none);
|
||||||
af->control(af, AF_CONTROL_SET_FORMAT, &(int){0});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int filter_reinit(struct af_instance *af)
|
||||||
|
{
|
||||||
|
struct af_instance *prev = af->prev;
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 filter_reinit_with_conversion(struct af_stream *s, struct af_instance *af)
|
||||||
|
{
|
||||||
|
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->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->data, &in);
|
||||||
|
if (filter_reinit(af->prev) != AF_OK)
|
||||||
|
return AF_ERROR;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
// Return AF_OK on success or AF_ERROR on failure.
|
// Return AF_OK on success or AF_ERROR on failure.
|
||||||
// Warning:
|
// Warning:
|
||||||
// A failed af_reinit() leaves the audio chain behind in a useless, broken
|
// A failed af_reinit() leaves the audio chain behind in a useless, broken
|
||||||
|
@ -378,52 +354,18 @@ static int af_reinit(struct af_stream *s)
|
||||||
// Start with the second filter, as the first filter is the special input
|
// Start with the second filter, as the first filter is the special input
|
||||||
// filter which needs no initialization.
|
// filter which needs no initialization.
|
||||||
struct af_instance *af = s->first->next;
|
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) {
|
while (af) {
|
||||||
if (retry >= max_retry)
|
int rv = filter_reinit_with_conversion(s, af);
|
||||||
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;
|
|
||||||
|
|
||||||
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) {
|
switch (rv) {
|
||||||
case AF_OK:
|
case AF_OK:
|
||||||
if (!mp_audio_config_valid(af->data))
|
|
||||||
goto error;
|
|
||||||
af->fmt_out = *af->data;
|
|
||||||
af = af->next;
|
af = af->next;
|
||||||
break;
|
break;
|
||||||
case AF_FALSE: { // Configuration filter is needed
|
case AF_FALSE: {
|
||||||
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;
|
|
||||||
}
|
|
||||||
// If the format conversion is (probably) caused by spdif, then
|
// If the format conversion is (probably) caused by spdif, then
|
||||||
// (as a feature) drop the filter, instead of failing hard.
|
// (as a feature) drop the filter, instead of failing hard.
|
||||||
int fmt_in1 = af->prev->data->format;
|
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)) {
|
if (af_fmt_is_valid(fmt_in1) && af_fmt_is_valid(fmt_in2)) {
|
||||||
bool spd1 = af_fmt_is_spdif(fmt_in1);
|
bool spd1 = af_fmt_is_spdif(fmt_in1);
|
||||||
bool spd2 = af_fmt_is_spdif(fmt_in2);
|
bool spd2 = af_fmt_is_spdif(fmt_in2);
|
||||||
|
@ -434,7 +376,6 @@ static int af_reinit(struct af_stream *s)
|
||||||
struct af_instance *aft = af->prev;
|
struct af_instance *aft = af->prev;
|
||||||
af_remove(s, af);
|
af_remove(s, af);
|
||||||
af = aft->next;
|
af = aft->next;
|
||||||
retry++;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -452,8 +393,6 @@ static int af_reinit(struct af_stream *s)
|
||||||
af->info->name, rv);
|
af->info->name, rv);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (af && !af->auto_inserted)
|
|
||||||
retry = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set previously unset fields in s->output to those of the filter chain
|
/* Set previously unset fields in s->output to those of the filter chain
|
||||||
|
|
|
@ -112,9 +112,6 @@ struct af_stream {
|
||||||
enum af_control {
|
enum af_control {
|
||||||
AF_CONTROL_REINIT = 1,
|
AF_CONTROL_REINIT = 1,
|
||||||
AF_CONTROL_RESET,
|
AF_CONTROL_RESET,
|
||||||
AF_CONTROL_SET_RESAMPLE_RATE,
|
|
||||||
AF_CONTROL_SET_FORMAT,
|
|
||||||
AF_CONTROL_SET_CHANNELS,
|
|
||||||
AF_CONTROL_SET_VOLUME,
|
AF_CONTROL_SET_VOLUME,
|
||||||
AF_CONTROL_GET_VOLUME,
|
AF_CONTROL_GET_VOLUME,
|
||||||
AF_CONTROL_SET_PAN_LEVEL,
|
AF_CONTROL_SET_PAN_LEVEL,
|
||||||
|
@ -160,6 +157,4 @@ int af_test_output(struct af_instance *af, struct mp_audio *out);
|
||||||
int af_from_ms(int n, float *in, int *out, int rate, float mi, float ma);
|
int af_from_ms(int n, float *in, int *out, int rate, float mi, float ma);
|
||||||
float af_softclip(float a);
|
float af_softclip(float a);
|
||||||
|
|
||||||
bool af_lavrresample_test_conversion(int src_format, int dst_format);
|
|
||||||
|
|
||||||
#endif /* MPLAYER_AF_H */
|
#endif /* MPLAYER_AF_H */
|
||||||
|
|
|
@ -173,12 +173,6 @@ static int check_output_conversion(int mp_format)
|
||||||
return af_to_avformat(mp_format);
|
return af_to_avformat(mp_format);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool af_lavrresample_test_conversion(int src_format, int dst_format)
|
|
||||||
{
|
|
||||||
return af_to_avformat(src_format) != AV_SAMPLE_FMT_NONE &&
|
|
||||||
check_output_conversion(dst_format) != AV_SAMPLE_FMT_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct mp_chmap fudge_pairs[][2] = {
|
static struct mp_chmap fudge_pairs[][2] = {
|
||||||
{MP_CHMAP2(BL, BR), MP_CHMAP2(SL, SR)},
|
{MP_CHMAP2(BL, BR), MP_CHMAP2(SL, SR)},
|
||||||
{MP_CHMAP2(SL, SR), MP_CHMAP2(BL, BR)},
|
{MP_CHMAP2(SL, SR), MP_CHMAP2(BL, BR)},
|
||||||
|
@ -407,21 +401,6 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||||
r = configure_lavrr(af, in, out, true);
|
r = configure_lavrr(af, in, out, true);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
case AF_CONTROL_SET_FORMAT: {
|
|
||||||
int format = *(int *)arg;
|
|
||||||
if (format && check_output_conversion(format) == AV_SAMPLE_FMT_NONE)
|
|
||||||
return AF_FALSE;
|
|
||||||
|
|
||||||
mp_audio_set_format(af->data, format);
|
|
||||||
return AF_OK;
|
|
||||||
}
|
|
||||||
case AF_CONTROL_SET_CHANNELS: {
|
|
||||||
mp_audio_set_channels(af->data, (struct mp_chmap *)arg);
|
|
||||||
return AF_OK;
|
|
||||||
}
|
|
||||||
case AF_CONTROL_SET_RESAMPLE_RATE:
|
|
||||||
af->data->rate = *(int *)arg;
|
|
||||||
return AF_OK;
|
|
||||||
case AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE: {
|
case AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE: {
|
||||||
s->playback_speed = *(double *)arg;
|
s->playback_speed = *(double *)arg;
|
||||||
return AF_OK;
|
return AF_OK;
|
||||||
|
|
Loading…
Reference in New Issue