summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2013-03-29 22:52:15 +0100
committerwm4 <wm4@nowhere>2013-04-13 04:21:29 +0200
commitfd6302631a537f198ba60ae29c8818111881b0b1 (patch)
tree9e84562f72cf5c9ccea553bdcb1287b383d8abab
parentd2c20f4cfbfe01bdf63c2a76f20d39212598440e (diff)
downloadmpv-fd6302631a537f198ba60ae29c8818111881b0b1.tar.bz2
mpv-fd6302631a537f198ba60ae29c8818111881b0b1.tar.xz
af: streamline format negotiation
Add dummy input and output filters to remove special cases in the format negotiation code (af_fix_format_conversion() etc.). The output of the filter chain is now negotiated in exactly the same way as normal filters. Negotiate setting the sample rate in the same way as other audio parameters. As a side effect, the resampler is inserted at the start of the filter chain instead of the end, but that shouldn't matter much, especially since conversion and channel mixing are conflated into the same filter (due to libavresample's API).
-rw-r--r--audio/decode/dec_audio.c10
-rw-r--r--audio/filter/af.c359
-rw-r--r--audio/filter/af.h8
3 files changed, 210 insertions, 167 deletions
diff --git a/audio/decode/dec_audio.c b/audio/decode/dec_audio.c
index 9d0e51629c..c7ae3155ed 100644
--- a/audio/decode/dec_audio.c
+++ b/audio/decode/dec_audio.c
@@ -183,7 +183,7 @@ void uninit_audio(sh_audio_t *sh_audio)
if (sh_audio->afilter) {
mp_msg(MSGT_DECAUDIO, MSGL_V, "Uninit audio filters...\n");
af_uninit(sh_audio->afilter);
- free(sh_audio->afilter);
+ af_destroy(sh_audio->afilter);
sh_audio->afilter = NULL;
}
if (sh_audio->initialized) {
@@ -202,10 +202,8 @@ int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
int *out_samplerate, int *out_channels, int *out_format)
{
struct af_stream *afs = sh_audio->afilter;
- if (!afs) {
- afs = calloc(1, sizeof(struct af_stream));
- afs->opts = sh_audio->opts;
- }
+ if (!afs)
+ afs = af_new(sh_audio->opts);
// input format: same as codec's output format:
afs->input.rate = in_samplerate;
afs->input.nch = sh_audio->channels;
@@ -230,7 +228,7 @@ int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
// let's autoprobe it!
if (0 != af_init(afs)) {
sh_audio->afilter = NULL;
- free(afs);
+ af_destroy(afs);
return 0; // failed :(
}
diff --git a/audio/filter/af.c b/audio/filter/af.c
index 47288401f0..51dbd3fa77 100644
--- a/audio/filter/af.c
+++ b/audio/filter/af.c
@@ -81,6 +81,61 @@ static struct af_info* filter_list[] = {
NULL
};
+static bool af_config_equals(struct mp_audio *a, struct mp_audio *b)
+{
+ return a->format == b->format
+ && a->bps == b->bps
+ && a->nch == b->nch
+ && a->rate == b->rate;
+}
+
+static void af_copy_unset_fields(struct mp_audio *dst, struct mp_audio *src)
+{
+ if (dst->format == AF_FORMAT_UNKNOWN) {
+ dst->format = src->format;
+ dst->bps = src->bps;
+ }
+ if (dst->nch == 0)
+ dst->nch = src->nch;
+ if (dst->rate == 0)
+ dst->rate = src->rate;
+}
+
+static int input_control(struct af_instance* af, int cmd, void* arg)
+{
+ switch (cmd) {
+ case AF_CONTROL_REINIT:
+ assert(arg == &((struct af_stream *)af->setup)->input);
+ return AF_OK;
+ }
+ return AF_UNKNOWN;
+}
+
+static int output_control(struct af_instance* af, int cmd, void* arg)
+{
+ struct af_stream *s = af->setup;
+ struct mp_audio *output = &s->output;
+ struct mp_audio *filter_output = &s->filter_output;
+
+ switch (cmd) {
+ case AF_CONTROL_REINIT: {
+ struct mp_audio *in = arg;
+ struct mp_audio orig_in = *in;
+
+ *filter_output = *output;
+ af_copy_unset_fields(filter_output, in);
+ *in = *filter_output;
+ return af_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
+ }
+ }
+ return AF_UNKNOWN;
+}
+
+static struct mp_audio *dummy_play(struct af_instance* af, struct mp_audio* data)
+{
+ return data;
+}
+
/* Find a filter in the static list of filters using it's name. This
function is used internally */
static struct af_info *af_find(char *name)
@@ -175,21 +230,19 @@ static struct af_instance *af_prepend(struct af_stream *s,
struct af_instance *af,
const char *name)
{
+ if (!af)
+ af = s->last;
+ if (af == s->first)
+ af = s->first->next;
// Create the new filter and make sure it is OK
struct af_instance *new = af_create(s, name);
if (!new)
return NULL;
// Update pointers
new->next = af;
- if (af) {
- new->prev = af->prev;
- af->prev = new;
- } else
- s->last = new;
- if (new->prev)
- new->prev->next = new;
- else
- s->first = new;
+ new->prev = af->prev;
+ af->prev = new;
+ new->prev->next = new;
return new;
}
@@ -200,21 +253,19 @@ static struct af_instance *af_append(struct af_stream *s,
struct af_instance *af,
const char *name)
{
+ if (!af)
+ af = s->first;
+ if (af == s->last)
+ af = s->last->prev;
// Create the new filter and make sure it is OK
struct af_instance *new = af_create(s, name);
if (!new)
return NULL;
// Update pointers
new->prev = af;
- if (af) {
- new->next = af->next;
- af->next = new;
- } else
- s->first = new;
- if (new->next)
- new->next->prev = new;
- else
- s->last = new;
+ new->next = af->next;
+ af->next = new;
+ new->next->prev = new;
return new;
}
@@ -224,6 +275,9 @@ void af_remove(struct af_stream *s, struct af_instance *af)
if (!af)
return;
+ if (af == s->first || af == s->last)
+ return;
+
// Print friendly message
mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Removing filter %s \n",
af->info->name);
@@ -232,25 +286,18 @@ void af_remove(struct af_stream *s, struct af_instance *af)
af->control(af, AF_CONTROL_PRE_DESTROY, 0);
// Detach pointers
- if (af->prev)
- af->prev->next = af->next;
- else
- s->first = af->next;
- if (af->next)
- af->next->prev = af->prev;
- else
- s->last = af->prev;
+ af->prev->next = af->next;
+ af->next->prev = af->prev;
- // Uninitialize af and free memory
af->uninit(af);
free(af);
}
-static void remove_auto_inserted_filters(struct af_stream *s, bool dummy_only)
+static void remove_auto_inserted_filters(struct af_stream *s)
{
repeat:
for (struct af_instance *af = s->first; af; af = af->next) {
- if ((af->auto_inserted && !dummy_only) || af->info == &af_info_dummy) {
+ if (af->auto_inserted) {
af_remove(s, af);
goto repeat;
}
@@ -266,24 +313,22 @@ static void print_fmt(struct mp_audio *d)
mp_msg(MSGT_AFILTER, MSGL_V, "(?)");
}
-static void af_print_filter_chain(struct af_stream *s)
+static void af_print_filter_chain(struct af_stream *s, struct af_instance *at)
{
mp_msg(MSGT_AFILTER, MSGL_V, "Audio filter chain:\n");
- mp_msg(MSGT_AFILTER, MSGL_V, " [in] ");
- print_fmt(&s->input);
- mp_msg(MSGT_AFILTER, MSGL_V, "\n");
-
struct af_instance *af = s->first;
while (af) {
mp_msg(MSGT_AFILTER, MSGL_V, " [%s] ", af->info->name);
print_fmt(af->data);
+ if (af == at)
+ mp_msg(MSGT_AFILTER, MSGL_V, " <-");
mp_msg(MSGT_AFILTER, MSGL_V, "\n");
af = af->next;
}
- mp_msg(MSGT_AFILTER, MSGL_V, " [out] ");
+ mp_msg(MSGT_AFILTER, MSGL_V, " [ao] ");
print_fmt(&s->output);
mp_msg(MSGT_AFILTER, MSGL_V, "\n");
}
@@ -305,7 +350,6 @@ 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.
-// 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
@@ -315,41 +359,25 @@ static int af_fix_format_conversion(struct af_stream *s,
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;
- }
+ 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;
+ if (prev->control(prev, AF_CONTROL_FORMAT_FMT, &in.format) == AF_OK) {
+ *p_af = prev;
+ return AF_OK;
+ }
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;
- }
- }
+ struct af_instance *new = af_prepend(s, af, filter);
if (new == NULL)
return AF_ERROR;
- // Set output bits per sample
- in.format |= af_bits2fmt(in.bps * 8);
+ new->auto_inserted = true;
if (AF_OK != (rv = new->control(new, AF_CONTROL_FORMAT_FMT, &in.format)))
return rv;
- if (p_af)
- *p_af = new;
+ *p_af = new;
return AF_OK;
}
@@ -358,34 +386,47 @@ 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 ? *p_af : NULL;
- struct mp_audio actual;
- if (af) {
- actual = af->prev ? *af->prev->data : s->input;
- } else {
- actual = *s->last->data;
- }
+ struct af_instance *af = *p_af;
+ struct af_instance *prev = af->prev;
+ struct mp_audio actual = *prev->data;
if (actual.nch == in.nch)
return AF_FALSE;
- const char *filter = "lavrresample";
- struct af_instance *new;
- if (af) {
- new = af_prepend(s, af, filter);
- new->auto_inserted = true;
- } else {
- if (strcmp(s->last->info->name, filter) == 0) {
- new = s->last;
- } else {
- new = af_append(s, s->last, filter);
- new->auto_inserted = true;
- }
+ if (prev->control(prev, AF_CONTROL_CHANNELS, &in.nch) == AF_OK) {
+ *p_af = prev;
+ return AF_OK;
}
+ const char *filter = "lavrresample";
+ struct af_instance *new = af_prepend(s, af, filter);
if (new == NULL)
return AF_ERROR;
+ new->auto_inserted = true;
if (AF_OK != (rv = new->control(new, AF_CONTROL_CHANNELS, &in.nch)))
return rv;
- if (p_af)
- *p_af = new;
+ *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_RESAMPLE_RATE, &in.rate) == AF_OK) {
+ *p_af = prev;
+ return AF_OK;
+ }
+ const char *filter = "lavrresample";
+ struct af_instance *new = af_prepend(s, af, filter);
+ if (new == NULL)
+ return AF_ERROR;
+ new->auto_inserted = true;
+ if (AF_OK != (rv = new->control(new, AF_CONTROL_RESAMPLE_RATE, &in.rate)))
+ return rv;
+ *p_af = new;
return AF_OK;
}
@@ -394,20 +435,20 @@ static int af_fix_channels(struct af_stream *s, struct af_instance **p_af,
// state (for example, format filters that were tentatively inserted stay
// inserted).
// In that case, you should always rebuild the filter chain, or abort.
-// Also, note that for complete reinit, fixup_output_format() must be called
-// after this function.
+// Also, note that for complete reinit, fixup_output_format() may have to be
+// called after this function.
int af_reinit(struct af_stream *s)
{
- remove_auto_inserted_filters(s, true);
-
- struct af_instance *af = s->first;
+ // 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 retry = 0;
while (af && retry < 5) {
if (retry >= 5)
goto negotiate_error;
// Check if this is the first filter
- struct mp_audio in = af->prev ? *(af->prev->data) : s->input;
+ struct mp_audio in = *af->prev->data;
// Reset just in case...
in.audio = NULL;
in.len = 0;
@@ -418,85 +459,107 @@ int af_reinit(struct af_stream *s)
af = af->next;
break;
case AF_FALSE: { // Configuration filter is needed
- int progress = 0;
- if (af_fix_channels(s, &af, in) == AF_OK)
- progress = 1;
- if (af_fix_format_conversion(s, &af, in) == AF_OK)
- progress = 1;
- if (progress) {
+ // Set output bits per sample (unknown why this would be needed)
+ in.format |= af_bits2fmt(in.bps * 8);
+
+ 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;
}
goto negotiate_error;
}
case AF_DETACH: { // Filter is redundant and wants to be unloaded
- struct af_instance *aft = af->prev;
+ struct af_instance *aft = af->prev; // never NULL
af_remove(s, af);
- if (aft)
- af = aft->next;
- else
- af = s->first; // Restart configuration
+ af = aft->next;
break;
}
default:
mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Reinitialization did not "
"work, audio filter '%s' returned error code %i\n",
af->info->name, rv);
+ af_print_filter_chain(s, af);
return AF_ERROR;
}
+ retry = 0;
}
- // At least one filter must exist in the chain.
- if (!s->last) {
- af = af_append(s, NULL, "dummy");
- if (!af)
- return AF_ERROR;
- af->control(af, AF_CONTROL_REINIT, &s->input);
- }
-
- af_print_filter_chain(s);
+ af_print_filter_chain(s, NULL);
return AF_OK;
negotiate_error:
mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Unable to correct audio format. "
"This error should never occur, please send a bug report.\n");
+ af_print_filter_chain(s, af);
return AF_ERROR;
}
// Uninit and remove all filters
void af_uninit(struct af_stream *s)
{
- while (s->first)
- af_remove(s, s->first);
+ while (s->first->next && s->first->next != s->last)
+ af_remove(s, s->first->next);
+}
+
+struct af_stream *af_new(struct MPOpts *opts)
+{
+ struct af_stream *s = talloc_zero(NULL, struct af_stream);
+ static struct af_info in = { .name = "in" };
+ s->first = talloc(s, struct af_instance);
+ *s->first = (struct af_instance) {
+ .info = &in,
+ .control = input_control,
+ .play = dummy_play,
+ .setup = s,
+ .data = &s->input,
+ .mul = 1.0,
+ };
+ static struct af_info out = { .name = "out" };
+ s->last = talloc(s, struct af_instance);
+ *s->last = (struct af_instance) {
+ .info = &out,
+ .control = output_control,
+ .play = dummy_play,
+ .setup = s,
+ .data = &s->filter_output,
+ .mul = 1.0,
+ };
+ s->first->next = s->last;
+ s->last->prev = s->first;
+ return s;
}
-/**
- * Extend the filter chain so we get the required output format at the end.
+void af_destroy(struct af_stream *s)
+{
+ af_uninit(s);
+ talloc_free(s);
+}
+
+/*
+ * Set previously unset fields in s->output to those of the filter chain
+ * output. This is used to make the output format fixed, and even if you insert
+ * new filters or change the input format, the output format won't change.
* \return AF_ERROR on error, AF_OK if successful.
*/
static int fixup_output_format(struct af_stream *s)
{
- if (s->output.nch != 0)
- af_fix_channels(s, NULL, s->output);
-
- if (s->output.format != AF_FORMAT_UNKNOWN)
- af_fix_format_conversion(s, NULL, s->output);
-
if (AF_OK != af_reinit(s))
return AF_ERROR;
- if (s->output.format == AF_FORMAT_UNKNOWN)
- s->output.format = s->last->data->format;
- if (!s->output.nch)
- s->output.nch = s->last->data->nch;
- if (!s->output.rate)
- s->output.rate = s->last->data->rate;
- if ((s->last->data->format != s->output.format) ||
- (s->last->data->nch != s->output.nch) ||
- (s->last->data->rate != s->output.rate))
- return AF_ERROR;
- return AF_OK;
+ af_copy_unset_fields(&s->output, &s->filter_output);
+ return af_config_equals(&s->output, &s->filter_output) ? AF_OK : AF_ERROR;
}
/* Initialize the stream "s". This function creates a new filter list
@@ -521,46 +584,22 @@ int af_init(struct af_stream *s)
s->input.len = s->output.len = 0;
// Check if this is the first call
- if (!s->first) {
+ if (s->first->next == s->last) {
// Add all filters in the list (if there are any)
if (s->cfg.list) {
while (s->cfg.list[i]) {
- if (!af_append(s, s->last, s->cfg.list[i++]))
+ if (!af_prepend(s, s->last, s->cfg.list[i++]))
return -1;
}
}
}
- remove_auto_inserted_filters(s, false);
+ remove_auto_inserted_filters(s);
// Init filters
if (AF_OK != af_reinit(s))
return -1;
- // Check output format
- struct af_instance *af = NULL; // New filter
- // Check output frequency if not OK fix with resample
- if (s->output.rate && s->last->data->rate != s->output.rate) {
- // try to find a filter that can change samplrate
- af = af_control_any_rev(s, AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET,
- &(s->output.rate));
- if (!af) {
- char *resampler = "lavrresample";
- if (af_is_conversion_filter(s->first))
- af = af_append(s, s->first, resampler);
- else
- af = af_prepend(s, s->first, resampler);
- // Init the new filter
- if (!af)
- return -1;
- af->auto_inserted = true;
- if (af->control(af, AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET,
- &(s->output.rate)) != AF_OK)
- return -1;
- }
- if (AF_OK != af_reinit(s))
- return -1;
- }
if (AF_OK != fixup_output_format(s)) {
// Something is stuffed audio out will not work
mp_msg(
@@ -584,10 +623,10 @@ struct af_instance *af_add(struct af_stream *s, char *name)
if (!s || !s->first || !name)
return NULL;
// Insert the filter somewhere nice
- if (af_is_conversion_filter(s->first))
- new = af_append(s, s->first, name);
+ if (af_is_conversion_filter(s->first->next))
+ new = af_append(s, s->first->next, name);
else
- new = af_prepend(s, s->first, name);
+ new = af_prepend(s, s->first->next, name);
if (!new)
return NULL;
diff --git a/audio/filter/af.h b/audio/filter/af.h
index bebd21e942..623509c9df 100644
--- a/audio/filter/af.h
+++ b/audio/filter/af.h
@@ -91,9 +91,12 @@ struct af_stream {
// The first and last filter in the list
struct af_instance *first;
struct af_instance *last;
- // Storage for input and output data formats
+ // The user sets the input format (what the decoder outputs), and sets some
+ // or all fields in output to the output format the AO accepts.
+ // See fixup_output_format().
struct mp_audio input;
struct mp_audio output;
+ struct mp_audio filter_output;
// Configuration for this stream
struct af_cfg cfg;
struct MPOpts *opts;
@@ -123,6 +126,9 @@ struct af_stream {
* \param s filter chain
*/
+struct af_stream *af_new(struct MPOpts *opts);
+void af_destroy(struct af_stream *s);
+
/**
* \brief Initialize the stream "s".
* \return 0 on success, -1 on failure