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).
This commit is contained in:
wm4 2013-03-29 22:52:15 +01:00
parent d2c20f4cfb
commit fd6302631a
3 changed files with 210 additions and 167 deletions

View File

@ -183,7 +183,7 @@ void uninit_audio(sh_audio_t *sh_audio)
if (sh_audio->afilter) { if (sh_audio->afilter) {
mp_msg(MSGT_DECAUDIO, MSGL_V, "Uninit audio filters...\n"); mp_msg(MSGT_DECAUDIO, MSGL_V, "Uninit audio filters...\n");
af_uninit(sh_audio->afilter); af_uninit(sh_audio->afilter);
free(sh_audio->afilter); af_destroy(sh_audio->afilter);
sh_audio->afilter = NULL; sh_audio->afilter = NULL;
} }
if (sh_audio->initialized) { 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) int *out_samplerate, int *out_channels, int *out_format)
{ {
struct af_stream *afs = sh_audio->afilter; struct af_stream *afs = sh_audio->afilter;
if (!afs) { if (!afs)
afs = calloc(1, sizeof(struct af_stream)); afs = af_new(sh_audio->opts);
afs->opts = sh_audio->opts;
}
// input format: same as codec's output format: // input format: same as codec's output format:
afs->input.rate = in_samplerate; afs->input.rate = in_samplerate;
afs->input.nch = sh_audio->channels; 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! // let's autoprobe it!
if (0 != af_init(afs)) { if (0 != af_init(afs)) {
sh_audio->afilter = NULL; sh_audio->afilter = NULL;
free(afs); af_destroy(afs);
return 0; // failed :( return 0; // failed :(
} }

View File

@ -81,6 +81,61 @@ static struct af_info* filter_list[] = {
NULL 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 /* Find a filter in the static list of filters using it's name. This
function is used internally */ function is used internally */
static struct af_info *af_find(char *name) 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, struct af_instance *af,
const char *name) 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 // Create the new filter and make sure it is OK
struct af_instance *new = af_create(s, name); struct af_instance *new = af_create(s, name);
if (!new) if (!new)
return NULL; return NULL;
// Update pointers // Update pointers
new->next = af; new->next = af;
if (af) {
new->prev = af->prev; new->prev = af->prev;
af->prev = new; af->prev = new;
} else
s->last = new;
if (new->prev)
new->prev->next = new; new->prev->next = new;
else
s->first = new;
return new; return new;
} }
@ -200,21 +253,19 @@ static struct af_instance *af_append(struct af_stream *s,
struct af_instance *af, struct af_instance *af,
const char *name) 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 // Create the new filter and make sure it is OK
struct af_instance *new = af_create(s, name); struct af_instance *new = af_create(s, name);
if (!new) if (!new)
return NULL; return NULL;
// Update pointers // Update pointers
new->prev = af; new->prev = af;
if (af) {
new->next = af->next; new->next = af->next;
af->next = new; af->next = new;
} else
s->first = new;
if (new->next)
new->next->prev = new; new->next->prev = new;
else
s->last = new;
return new; return new;
} }
@ -224,6 +275,9 @@ void af_remove(struct af_stream *s, struct af_instance *af)
if (!af) if (!af)
return; return;
if (af == s->first || af == s->last)
return;
// Print friendly message // Print friendly message
mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Removing filter %s \n", mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Removing filter %s \n",
af->info->name); 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); af->control(af, AF_CONTROL_PRE_DESTROY, 0);
// Detach pointers // Detach pointers
if (af->prev)
af->prev->next = af->next; af->prev->next = af->next;
else
s->first = af->next;
if (af->next)
af->next->prev = af->prev; af->next->prev = af->prev;
else
s->last = af->prev;
// Uninitialize af and free memory
af->uninit(af); af->uninit(af);
free(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: repeat:
for (struct af_instance *af = s->first; af; af = af->next) { 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); af_remove(s, af);
goto repeat; goto repeat;
} }
@ -266,24 +313,22 @@ static void print_fmt(struct mp_audio *d)
mp_msg(MSGT_AFILTER, MSGL_V, "(?)"); 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, "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; struct af_instance *af = s->first;
while (af) { while (af) {
mp_msg(MSGT_AFILTER, MSGL_V, " [%s] ", af->info->name); mp_msg(MSGT_AFILTER, MSGL_V, " [%s] ", af->info->name);
print_fmt(af->data); print_fmt(af->data);
if (af == at)
mp_msg(MSGT_AFILTER, MSGL_V, " <-");
mp_msg(MSGT_AFILTER, MSGL_V, "\n"); mp_msg(MSGT_AFILTER, MSGL_V, "\n");
af = af->next; af = af->next;
} }
mp_msg(MSGT_AFILTER, MSGL_V, " [out] "); mp_msg(MSGT_AFILTER, MSGL_V, " [ao] ");
print_fmt(&s->output); print_fmt(&s->output);
mp_msg(MSGT_AFILTER, MSGL_V, "\n"); 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 // in is what af can take as input - insert a conversion filter if the actual
// input format doesn't match what af expects. // input format doesn't match what af expects.
// If af is NULL, in is the output format of the stream.
// Returns: // Returns:
// AF_OK: must call af_reinit() or equivalent, format matches // AF_OK: must call af_reinit() or equivalent, format matches
// AF_FALSE: nothing was changed, format matches // AF_FALSE: nothing was changed, format matches
@ -315,40 +359,24 @@ static int af_fix_format_conversion(struct af_stream *s,
struct mp_audio in) struct mp_audio in)
{ {
int rv; int rv;
struct af_instance *af = p_af ? *p_af : NULL; struct af_instance *af = *p_af;
struct mp_audio actual; struct af_instance *prev = af->prev;
if (af) { struct mp_audio actual = *prev->data;
actual = af->prev ? *af->prev->data : s->input;
} else {
actual = *s->last->data;
}
if (actual.format == in.format) if (actual.format == in.format)
return AF_FALSE; 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); const char *filter = af_find_conversion_filter(actual.format, in.format);
if (!filter) if (!filter)
return AF_ERROR; return AF_ERROR;
struct af_instance *new; struct af_instance *new = af_prepend(s, af, filter);
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) if (new == NULL)
return AF_ERROR; return AF_ERROR;
// Set output bits per sample new->auto_inserted = true;
in.format |= af_bits2fmt(in.bps * 8);
if (AF_OK != (rv = new->control(new, AF_CONTROL_FORMAT_FMT, &in.format))) if (AF_OK != (rv = new->control(new, AF_CONTROL_FORMAT_FMT, &in.format)))
return rv; return rv;
if (p_af)
*p_af = new; *p_af = new;
return AF_OK; return AF_OK;
} }
@ -358,33 +386,46 @@ static int af_fix_channels(struct af_stream *s, struct af_instance **p_af,
struct mp_audio in) struct mp_audio in)
{ {
int rv; int rv;
struct af_instance *af = p_af ? *p_af : NULL; struct af_instance *af = *p_af;
struct mp_audio actual; struct af_instance *prev = af->prev;
if (af) { struct mp_audio actual = *prev->data;
actual = af->prev ? *af->prev->data : s->input;
} else {
actual = *s->last->data;
}
if (actual.nch == in.nch) if (actual.nch == in.nch)
return AF_FALSE; return AF_FALSE;
if (prev->control(prev, AF_CONTROL_CHANNELS, &in.nch) == AF_OK) {
*p_af = prev;
return AF_OK;
}
const char *filter = "lavrresample"; const char *filter = "lavrresample";
struct af_instance *new; struct af_instance *new = af_prepend(s, af, filter);
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 (new == NULL) if (new == NULL)
return AF_ERROR; return AF_ERROR;
new->auto_inserted = true;
if (AF_OK != (rv = new->control(new, AF_CONTROL_CHANNELS, &in.nch))) if (AF_OK != (rv = new->control(new, AF_CONTROL_CHANNELS, &in.nch)))
return rv; return rv;
if (p_af) *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; *p_af = new;
return AF_OK; 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 // state (for example, format filters that were tentatively inserted stay
// inserted). // inserted).
// In that case, you should always rebuild the filter chain, or abort. // In that case, you should always rebuild the filter chain, or abort.
// Also, note that for complete reinit, fixup_output_format() must be called // Also, note that for complete reinit, fixup_output_format() may have to be
// after this function. // called after this function.
int af_reinit(struct af_stream *s) int af_reinit(struct af_stream *s)
{ {
remove_auto_inserted_filters(s, true); // Start with the second filter, as the first filter is the special input
// filter which needs no initialization.
struct af_instance *af = s->first; struct af_instance *af = s->first->next;
int retry = 0; int retry = 0;
while (af && retry < 5) { while (af && retry < 5) {
if (retry >= 5) if (retry >= 5)
goto negotiate_error; goto negotiate_error;
// Check if this is the first filter // 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... // Reset just in case...
in.audio = NULL; in.audio = NULL;
in.len = 0; in.len = 0;
@ -418,85 +459,107 @@ int af_reinit(struct af_stream *s)
af = af->next; af = af->next;
break; break;
case AF_FALSE: { // Configuration filter is needed case AF_FALSE: { // Configuration filter is needed
int progress = 0; // Set output bits per sample (unknown why this would be needed)
if (af_fix_channels(s, &af, in) == AF_OK) in.format |= af_bits2fmt(in.bps * 8);
progress = 1;
if (af_fix_format_conversion(s, &af, in) == AF_OK) if (af_fix_channels(s, &af, in) == AF_OK) {
progress = 1; retry++;
if (progress) { 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++; retry++;
continue; continue;
} }
goto negotiate_error; goto negotiate_error;
} }
case AF_DETACH: { // Filter is redundant and wants to be unloaded 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); af_remove(s, af);
if (aft)
af = aft->next; af = aft->next;
else
af = s->first; // Restart configuration
break; break;
} }
default: default:
mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Reinitialization did not " mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Reinitialization did not "
"work, audio filter '%s' returned error code %i\n", "work, audio filter '%s' returned error code %i\n",
af->info->name, rv); af->info->name, rv);
af_print_filter_chain(s, af);
return AF_ERROR; return AF_ERROR;
} }
retry = 0;
} }
// At least one filter must exist in the chain. af_print_filter_chain(s, NULL);
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);
return AF_OK; return AF_OK;
negotiate_error: negotiate_error:
mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Unable to correct audio format. " mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Unable to correct audio format. "
"This error should never occur, please send a bug report.\n"); "This error should never occur, please send a bug report.\n");
af_print_filter_chain(s, af);
return AF_ERROR; return AF_ERROR;
} }
// Uninit and remove all filters // Uninit and remove all filters
void af_uninit(struct af_stream *s) void af_uninit(struct af_stream *s)
{ {
while (s->first) while (s->first->next && s->first->next != s->last)
af_remove(s, s->first); af_remove(s, s->first->next);
} }
/** struct af_stream *af_new(struct MPOpts *opts)
* Extend the filter chain so we get the required output format at the end. {
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;
}
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. * \return AF_ERROR on error, AF_OK if successful.
*/ */
static int fixup_output_format(struct af_stream *s) 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)) if (AF_OK != af_reinit(s))
return AF_ERROR; return AF_ERROR;
if (s->output.format == AF_FORMAT_UNKNOWN) af_copy_unset_fields(&s->output, &s->filter_output);
s->output.format = s->last->data->format; return af_config_equals(&s->output, &s->filter_output) ? AF_OK : AF_ERROR;
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;
} }
/* Initialize the stream "s". This function creates a new filter list /* 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; s->input.len = s->output.len = 0;
// Check if this is the first call // 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) // Add all filters in the list (if there are any)
if (s->cfg.list) { if (s->cfg.list) {
while (s->cfg.list[i]) { 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; return -1;
} }
} }
} }
remove_auto_inserted_filters(s, false); remove_auto_inserted_filters(s);
// Init filters // Init filters
if (AF_OK != af_reinit(s)) if (AF_OK != af_reinit(s))
return -1; 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)) { if (AF_OK != fixup_output_format(s)) {
// Something is stuffed audio out will not work // Something is stuffed audio out will not work
mp_msg( mp_msg(
@ -584,10 +623,10 @@ struct af_instance *af_add(struct af_stream *s, char *name)
if (!s || !s->first || !name) if (!s || !s->first || !name)
return NULL; return NULL;
// Insert the filter somewhere nice // Insert the filter somewhere nice
if (af_is_conversion_filter(s->first)) if (af_is_conversion_filter(s->first->next))
new = af_append(s, s->first, name); new = af_append(s, s->first->next, name);
else else
new = af_prepend(s, s->first, name); new = af_prepend(s, s->first->next, name);
if (!new) if (!new)
return NULL; return NULL;

View File

@ -91,9 +91,12 @@ struct af_stream {
// The first and last filter in the list // The first and last filter in the list
struct af_instance *first; struct af_instance *first;
struct af_instance *last; 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 input;
struct mp_audio output; struct mp_audio output;
struct mp_audio filter_output;
// Configuration for this stream // Configuration for this stream
struct af_cfg cfg; struct af_cfg cfg;
struct MPOpts *opts; struct MPOpts *opts;
@ -123,6 +126,9 @@ struct af_stream {
* \param s filter chain * \param s filter chain
*/ */
struct af_stream *af_new(struct MPOpts *opts);
void af_destroy(struct af_stream *s);
/** /**
* \brief Initialize the stream "s". * \brief Initialize the stream "s".
* \return 0 on success, -1 on failure * \return 0 on success, -1 on failure