1
0
mirror of https://github.com/mpv-player/mpv synced 2025-04-01 23:00:41 +00:00

af: use af_lavrresample for format conversions, if possible

Refactor to remove the duplicated format filter insertion code. Allow
other format converting filters to be inserted on format mismatches.
af_info.test_conversion checks whether conversion between two formats
would work with the given filter; do this to avoid having to insert
multiple conversion filters at once and such things. (Although this
isn't ideal: what if we want to avoid af_format for some conversions?
What if we want to split af_format in endian-swapping filters etc.?)

Prefer af_lavrresample for conversions that it supports natively,
otherwise let af_format handle the full conversion.
This commit is contained in:
wm4 2013-03-23 13:05:32 +01:00
parent 5a958921a7
commit c866583e1e
4 changed files with 111 additions and 45 deletions

View File

@ -52,7 +52,6 @@ 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,
@ -77,6 +76,8 @@ static struct af_info* filter_list[] = {
#ifdef CONFIG_LIBBS2B
&af_info_bs2b,
#endif
// Must come last, because it's the fallback format conversion filter
&af_info_format,
NULL
};
@ -292,6 +293,74 @@ static void af_print_filter_chain(struct af_stream *s)
mp_msg(MSGT_AFILTER, MSGL_V, "\n");
}
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.
// If af is NULL, in is the output format of the stream.
// 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 ? *p_af : NULL;
struct mp_audio actual;
if (af) {
actual = af->prev ? *af->prev->data : s->input;
} else {
actual = *s->last->data;
}
if (actual.format == in.format)
return AF_FALSE;
const char *filter = af_find_conversion_filter(actual.format, in.format);
if (!filter)
return AF_ERROR;
struct af_instance *new;
if (af) {
new = af_prepend(s, af, filter);
new->auto_inserted = true;
} else {
struct af_info *last = s->last->info;
if (last->test_conversion &&
last->test_conversion(actual.format, in.format))
{
new = s->last;
} else {
new = af_append(s, s->last, filter);
new->auto_inserted = true;
}
}
if (new == NULL)
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 (AF_OK != (rv = new->control(new, AF_CONTROL_REINIT, &actual)))
return rv == AF_FALSE ? AF_ERROR : rv;
if (p_af)
*p_af = new;
return AF_OK;
}
// Warning:
// A failed af_reinit() leaves the audio chain behind in a useless, broken
// state (for example, format filters that were tentatively inserted stay
@ -321,12 +390,14 @@ int af_reinit(struct af_stream *s)
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;
int progress = 0;
// Insert channels filter
if ((af->prev ? af->prev->data->nch : s->input.nch) != in.nch) {
struct af_instance *new = NULL;
// Create channels filter
if (NULL == (new = af_prepend(s, af, "channels")))
return AF_ERROR;
new->auto_inserted = true;
// Set number of output channels
if (AF_OK !=
(rv = new->control(new, AF_CONTROL_CHANNELS, &in.nch)))
@ -335,33 +406,19 @@ int af_reinit(struct af_stream *s)
in = new->prev ? (*new->prev->data) : s->input;
if (AF_OK != (rv = new->control(new, AF_CONTROL_REINIT, &in)))
return rv;
progress = 1;
}
// 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;
new->auto_inserted = true;
// 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
in = new->prev ? (*new->prev->data) : s->input;
if (AF_OK != (rv = new->control(new, AF_CONTROL_REINIT, &in)))
return rv;
}
if (!new) { // Should _never_ happen
if ((rv = af_fix_format_conversion(s, &af, in)) < 0)
return rv;
if (rv == AF_OK)
progress = 1;
if (!progress) { // 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,
@ -417,14 +474,15 @@ void af_uninit(struct af_stream *s)
*/
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"))
struct af_instance *af = NULL;
if (af_is_conversion_filter(s->last))
af = af_prepend(s, s->last, "channels");
else
af = af_append(s, s->last, "channels");
af->auto_inserted = true;
// Init the new filter
if (!af ||
(AF_OK != af->control(af, AF_CONTROL_CHANNELS, &(s->output.nch))))
@ -433,21 +491,12 @@ static int fixup_output_format(struct af_stream *s)
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");
af->auto_inserted = true;
} 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))
return AF_ERROR;
if (s->output.format != AF_FORMAT_UNKNOWN) {
struct af_instance *af = NULL;
if (af_fix_format_conversion(s, &af, s->output) == AF_OK) {
if (AF_OK != af_reinit(s))
return AF_ERROR;
}
}
// Re init again just in case
@ -545,12 +594,12 @@ int af_init(struct af_stream *s)
if (!af) {
char *resampler = "lavrresample";
if ((AF_INIT_TYPE_MASK & s->cfg.force) == AF_INIT_SLOW) {
if (!strcmp(s->first->info->name, "format"))
if (af_is_conversion_filter(s->first))
af = af_append(s, s->first, resampler);
else
af = af_prepend(s, s->first, resampler);
} else {
if (!strcmp(s->last->info->name, "format"))
if (af_is_conversion_filter(s->last))
af = af_prepend(s, s->last, resampler);
else
af = af_append(s, s->last, resampler);
@ -590,7 +639,7 @@ struct af_instance *af_add(struct af_stream *s, char *name)
if (!s || !s->first || !name)
return NULL;
// Insert the filter somewhere nice
if (!strcmp(s->first->info->name, "format"))
if (af_is_conversion_filter(s->first))
new = af_append(s, s->first, name);
else
new = af_prepend(s, s->first, name);

View File

@ -60,6 +60,7 @@ struct af_info {
const char *comment;
const int flags;
int (*open)(struct af_instance *vf);
bool (*test_conversion)(int src_format, int dst_format);
};
// Linked list of audio filters

View File

@ -75,6 +75,14 @@ static int check_format(int format)
return AF_ERROR;
}
static bool test_conversion(int src_format, int dst_format)
{
// This is the fallback conversion filter, so this filter is always
// inserted on format mismatches if no other filter can handle it.
// Initializing the filter might still fail.
return true;
}
// Initialization and runtime control
static int control(struct af_instance* af, int cmd, void* arg)
{
@ -147,7 +155,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
mp_msg(MSGT_AFILTER, MSGL_ERR, "[format] %s is not a valid format\n", (char *)arg);
return AF_ERROR;
}
if(AF_OK != af->control(af,AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET,&format))
if(AF_OK != af->control(af, AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET,&format))
return AF_ERROR;
return AF_OK;
}
@ -301,7 +309,8 @@ struct af_info af_info_format = {
"Anders",
"",
AF_FLAGS_REENTRANT,
af_open
af_open,
.test_conversion = test_conversion,
};
static inline uint32_t load24bit(void* data, int pos) {

View File

@ -104,6 +104,12 @@ static bool needs_lavrctx_reconfigure(struct af_resample *s,
}
static bool test_conversion(int src_format, int dst_format)
{
return af_to_avformat(src_format) != AV_SAMPLE_FMT_NONE &&
af_to_avformat(dst_format) != AV_SAMPLE_FMT_NONE;
}
#define ctx_opt_set_int(a,b) av_opt_set_int(s->avrctx, (a), (b), 0)
#define ctx_opt_set_dbl(a,b) av_opt_set_double(s->avrctx, (a), (b), 0)
@ -304,5 +310,6 @@ struct af_info af_info_lavrresample = {
"Stefano Pigozzi (based on Michael Niedermayer's lavcresample)",
"",
AF_FLAGS_REENTRANT,
af_open
af_open,
.test_conversion = test_conversion,
};