summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2013-03-23 13:05:32 +0100
committerwm4 <wm4@nowhere>2013-04-13 04:21:27 +0200
commitc866583e1e31e6f648f2346fb9c5394d8d080587 (patch)
tree7cabc2948b1e8b6c12a13de77e6017265b00750e
parent5a958921a738f2cd928f8339872b74a3c299ff0e (diff)
downloadmpv-c866583e1e31e6f648f2346fb9c5394d8d080587.tar.bz2
mpv-c866583e1e31e6f648f2346fb9c5394d8d080587.tar.xz
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.
-rw-r--r--audio/filter/af.c133
-rw-r--r--audio/filter/af.h1
-rw-r--r--audio/filter/af_format.c13
-rw-r--r--audio/filter/af_lavrresample.c9
4 files changed, 111 insertions, 45 deletions
diff --git a/audio/filter/af.c b/audio/filter/af.c
index b7bfbd3145..2ca8d9a289 100644
--- a/audio/filter/af.c
+++ b/audio/filter/af.c
@@ -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);
diff --git a/audio/filter/af.h b/audio/filter/af.h
index e4a329abea..50719c4d1b 100644
--- a/audio/filter/af.h
+++ b/audio/filter/af.h
@@ -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
diff --git a/audio/filter/af_format.c b/audio/filter/af_format.c
index 37d29c1f80..720cff0bf0 100644
--- a/audio/filter/af_format.c
+++ b/audio/filter/af_format.c
@@ -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) {
diff --git a/audio/filter/af_lavrresample.c b/audio/filter/af_lavrresample.c
index aed60f7078..b95c3e5f69 100644
--- a/audio/filter/af_lavrresample.c
+++ b/audio/filter/af_lavrresample.c
@@ -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,
};