From e60b8f181dec744af25c3a52fb88f600cd1b63ea Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 22 Oct 2013 01:20:43 +0200 Subject: audio/filter: split af_format into separate filters, rename af_force af_format is the old audio conversion filter. It could do all possible conversions supported by the audio chain. However, ever since the addition of af_lavrresample, most conversions are done by libav/swresample, and af_format is used as fallback. Separate out the fallback cases and remove af_format. af_convert24 does 24 bit <-> 32 bit conversions, while af_convertsignendian does sign and endian conversions. Maybe the way the conversions are split sounds a bit odd. But the former changes the size of the audio data, while the latter is fully in-place, so there's at least different buffer management. This requires a quite complicated algorithm to make sure all these "partial" conversion filters can actually get from one format to another. E.g. s24le->s32be always requires convertsignendian and convert24, but af.c has no idea what the intermediate format should be. So I added a graph search (trying every possible format and filter) to determine required format and filter. When I wrote this, it seemed this was still better than messing everything into af_lavrresample, but maybe this is overkill and I'll change my opinion. For now, it seems nice to get rid of af_format though. The AC3->IEC61937 conversion isn't supported anymore, but I don't think this is needed anywhere. Most AOs test all formats explicitly, or use the AF_FORMAT_IS_IEC61937() macro (which includes AC3). One positive consequence of this change is that conversions always include dithering (done by libav/swresample), instead of possibly going through af_format, which doesn't do anything fancy. Rename af_force to af_format. It's essentially compatible with command line uses of af_format. We retain a compatibility alias for af_force. --- audio/filter/af.c | 110 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 92 insertions(+), 18 deletions(-) (limited to 'audio/filter/af.c') diff --git a/audio/filter/af.c b/audio/filter/af.c index 00eb2346f9..62dce8df7e 100644 --- a/audio/filter/af.c +++ b/audio/filter/af.c @@ -52,12 +52,14 @@ extern struct af_info af_info_karaoke; extern struct af_info af_info_scaletempo; extern struct af_info af_info_bs2b; extern struct af_info af_info_lavfi; +extern struct af_info af_info_convert24; +extern struct af_info af_info_convertsignendian; static struct af_info* filter_list[] = { &af_info_dummy, &af_info_delay, &af_info_channels, - &af_info_force, + &af_info_format, &af_info_volume, &af_info_equalizer, &af_info_pan, @@ -85,8 +87,9 @@ static struct af_info* filter_list[] = { #ifdef CONFIG_AF_LAVFI &af_info_lavfi, #endif - // Must come last, because it's the fallback format conversion filter - &af_info_format, + // Must come last, because they're fallback format conversion filter + &af_info_convert24, + &af_info_convertsignendian, NULL }; @@ -109,6 +112,10 @@ static bool get_desc(struct m_obj_desc *dst, int index) const struct m_obj_list af_obj_list = { .get_desc = get_desc, .description = "audio filters", + .aliases = { + {"force", "format"}, + {0} + }, .legacy_hacks = true, // many filters have custom option parsing }; @@ -285,7 +292,8 @@ static void af_remove(struct af_stream *s, struct af_instance *af) af->prev->next = af->next; af->next->prev = af->prev; - af->uninit(af); + if (af->uninit) + af->uninit(af); talloc_free(af); } @@ -334,14 +342,76 @@ static int af_count_filters(struct af_stream *s) return count; } -static char *af_find_conversion_filter(int srcfmt, int dstfmt) +// Finds the first conversion filter on the way from srcfmt to dstfmt. +// Conversions form a DAG: each node is a format/filter pair, and possible +// conversions are edges. We search the DAG for the shortest path. +// Some cases visit the same filter multiple times, but with different formats +// (like u24le->s8), so one node per format or filter separate is not enough. +// Returns the filter and dest. format for the first conversion step. +// (So we know what conversion filter with what format to insert next.) +static 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 (char *)af->name; +#define NUM_FMT 64 +#define NUM_FILT 32 +#define NUM_NODES (NUM_FMT * NUM_FILT) + for (int n = 0; filter_list[n]; n++) + assert(n < NUM_FILT); + for (int n = 0; af_fmtstr_table[n].format; n++) + assert(n < NUM_FMT); + + bool visited[NUM_NODES] = {0}; + unsigned char distance[NUM_NODES]; + short previous[NUM_NODES] = {0}; + for (int n = 0; n < NUM_NODES; n++) { + distance[n] = 255; + if (af_fmtstr_table[n % NUM_FMT].format == srcfmt) + distance[n] = 0; } - return NULL; + + while (1) { + int next = -1; + for (int n = 0; n < NUM_NODES; n++) { + if (!visited[n] && (next < 0 || (distance[n] < distance[next]))) + next = n; + } + if (next < 0 || distance[next] == 255) + return NULL; + visited[next] = true; + + int fmt = next % NUM_FMT; + if (af_fmtstr_table[fmt].format == *dstfmt) { + // Best match found + for (int cur = next; cur >= 0; cur = previous[cur] - 1) { + if (distance[cur] == 1) { + *dstfmt = af_fmtstr_table[cur % NUM_FMT].format; + return (char *)filter_list[cur / NUM_FMT]->name; + } + } + return NULL; + } + + for (int n = 0; filter_list[n]; n++) { + struct af_info *af = filter_list[n]; + if (!af->test_conversion) + continue; + for (int i = 0; af_fmtstr_table[i].format; i++) { + if (i != fmt && af->test_conversion(af_fmtstr_table[fmt].format, + af_fmtstr_table[i].format)) + { + int other = n * NUM_FMT + i; + int ndist = distance[next] + 1; + if (ndist < distance[other]) { + distance[other] = ndist; + previous[other] = next + 1; + } + } + } + } + } + assert(0); +#undef NUM_FMT +#undef NUM_FILT +#undef NODE_N } static bool af_is_conversion_filter(struct af_instance *af) @@ -352,7 +422,7 @@ static bool af_is_conversion_filter(struct af_instance *af) // 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_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, @@ -365,18 +435,21 @@ static int af_fix_format_conversion(struct af_stream *s, 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; - } - char *filter = af_find_conversion_filter(actual.format, in.format); + int dstfmt = in.format; + char *filter = af_find_conversion_filter(actual.format, &dstfmt); if (!filter) return AF_ERROR; + if (strcmp(filter, prev->info->name) == 0) { + if (prev->control(prev, AF_CONTROL_FORMAT_FMT, &dstfmt) == AF_OK) { + *p_af = prev; + return AF_OK; + } + } 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_FORMAT_FMT, &in.format))) + if (AF_OK != (rv = new->control(new, AF_CONTROL_FORMAT_FMT, &dstfmt))) return rv; *p_af = new; return AF_OK; @@ -442,7 +515,8 @@ static 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 + // Up to 7 retries per filter (channel, rate, 5x format conversions) + int max_retry = af_count_filters(s) * 7; int retry = 0; while (af) { if (retry >= max_retry) -- cgit v1.2.3