1
0
mirror of https://github.com/mpv-player/mpv synced 2025-05-03 16:49:34 +00:00

audio/filter: use new option API

Make the VF/VO/AO option parser available to audio filters. No audio
filter uses this yet, but it's still a quite intrusive change.

In particular, the commands for manipulating filters at runtime
completely change. We delete the old code, and use the same
infrastructure as for video filters. (This forces complete
reinitialization of the filter chain, which hopefully isn't a problem
for any use cases. The old code forced reinitialization too, but it
could potentially allow a filter to cache things; e.g. consider loaded
ladspa plugins and such.)
This commit is contained in:
wm4 2013-07-22 14:43:58 +02:00
parent 221ef23d0d
commit 3b8dfddb4c
14 changed files with 210 additions and 280 deletions

View File

@ -186,6 +186,8 @@ input.conf and Slave Commands
+--------------------------------+----------------------------------------+ +--------------------------------+----------------------------------------+
| ``show_chapters`` | ``show_text ${chapter-list}`` | | ``show_chapters`` | ``show_text ${chapter-list}`` |
+--------------------------------+----------------------------------------+ +--------------------------------+----------------------------------------+
| ``af_switch``, ``af_add``, ... | ``af [set|add|...]`` |
+--------------------------------+----------------------------------------+
Other Other
~~~~~ ~~~~~

View File

@ -253,20 +253,10 @@ List of Input Commands
Input Commands that are Possibly Subject to Change Input Commands that are Possibly Subject to Change
-------------------------------------------------- --------------------------------------------------
``af_switch "filter1=params,filter2,..."`` ``af set|add|toggle|del|clr "filter1=params,filter2,..."``
Replace the current filter chain with the given list. Change audio filter chain. See ``vf`` command.
``af_add "filter1=params,filter2,..."`` ``vf set|add|toggle|del|clr "filter1=params,filter2,..."``
Add the given list of audio filters to the audio filter chain.
``af_del "filter1,filter2,..."``
Remove the given list of audio filters.
``af_clr``
Remove all audio filters. (Conversion filters will be re-added
automatically if needed.)
``vf set|add|toggle|del "filter1=params,filter2,..."``
Change video filter chain. Change video filter chain.
The first argument decides what happens: The first argument decides what happens:
@ -290,6 +280,10 @@ Input Commands that are Possibly Subject to Change
indexes start from the last filter, and ``-1`` denotes the last indexes start from the last filter, and ``-1`` denotes the last
filter. filter.
clr
Remove all filters. Note that like the other sub-commands, this does
not control automatically inserted filters.
You can assign labels to filter by prefixing them with ``@name:`` (where You can assign labels to filter by prefixing them with ``@name:`` (where
``name`` is a user-chosen arbitrary identifier). Labels can be used to ``name`` is a user-chosen arbitrary identifier). Labels can be used to
refer to filters by name in all of the filter chain modification commands. refer to filters by name in all of the filter chain modification commands.
@ -322,7 +316,7 @@ Undocumented commands: ``tv_start_scan``, ``tv_step_channel``, ``tv_step_norm``,
``tv_step_freq``, ``tv_set_norm``, ``dvb_set_channel``, ``radio_step_channel``, ``tv_step_freq``, ``tv_set_norm``, ``dvb_set_channel``, ``radio_step_channel``,
``radio_set_channel``, ``radio_set_freq``, ``radio_step_freq`` (all of these ``radio_set_channel``, ``radio_set_freq``, ``radio_step_freq`` (all of these
should be replaced by properties), ``stop`` (questionable use), ``get_property`` should be replaced by properties), ``stop`` (questionable use), ``get_property``
(?), ``af_cmdline``, ``vo_cmdline`` (experimental). (?), ``vo_cmdline`` (experimental).
Input Command Prefixes Input Command Prefixes
---------------------- ----------------------

View File

@ -54,8 +54,6 @@ static const struct ad_functions * const ad_drivers[] = {
NULL NULL
}; };
struct af_cfg af_cfg = {0}; // Configuration for audio filters
static int init_audio_codec(sh_audio_t *sh_audio, const char *decoder) static int init_audio_codec(sh_audio_t *sh_audio, const char *decoder)
{ {
assert(!sh_audio->initialized); assert(!sh_audio->initialized);
@ -180,7 +178,6 @@ 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_destroy(sh_audio->afilter); af_destroy(sh_audio->afilter);
sh_audio->afilter = NULL; sh_audio->afilter = NULL;
} }
@ -199,9 +196,10 @@ int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
int *out_samplerate, struct mp_chmap *out_channels, int *out_samplerate, struct mp_chmap *out_channels,
int *out_format) int *out_format)
{ {
if (!sh_audio->afilter)
sh_audio->afilter = af_new(sh_audio->opts);
struct af_stream *afs = sh_audio->afilter; struct af_stream *afs = sh_audio->afilter;
if (!afs)
afs = af_new(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;
mp_audio_set_channels(&afs->input, &sh_audio->channels); mp_audio_set_channels(&afs->input, &sh_audio->channels);
@ -212,9 +210,6 @@ int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
mp_audio_set_channels(&afs->output, out_channels); mp_audio_set_channels(&afs->output, out_channels);
mp_audio_set_format(&afs->output, *out_format); mp_audio_set_format(&afs->output, *out_format);
// filter config:
memcpy(&afs->cfg, &af_cfg, sizeof(struct af_cfg));
char *s_from = mp_audio_config_to_str(&afs->input); char *s_from = mp_audio_config_to_str(&afs->input);
char *s_to = mp_audio_config_to_str(&afs->output); char *s_to = mp_audio_config_to_str(&afs->output);
mp_tmsg(MSGT_DECAUDIO, MSGL_V, mp_tmsg(MSGT_DECAUDIO, MSGL_V,
@ -223,9 +218,9 @@ int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
talloc_free(s_to); talloc_free(s_to);
// let's autoprobe it! // let's autoprobe it!
if (0 != af_init(afs)) { if (af_init(afs) != 0) {
sh_audio->afilter = NULL;
af_destroy(afs); af_destroy(afs);
sh_audio->afilter = NULL;
return 0; // failed :( return 0; // failed :(
} }
@ -233,8 +228,6 @@ int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
*out_channels = afs->output.channels; *out_channels = afs->output.channels;
*out_format = afs->output.format; *out_format = afs->output.format;
// ok!
sh_audio->afilter = (void *) afs;
return 1; return 1;
} }

View File

@ -37,6 +37,4 @@ int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
int *out_samplerate, struct mp_chmap *out_channels, int *out_samplerate, struct mp_chmap *out_channels,
int *out_format); int *out_format);
extern struct af_cfg af_cfg;
#endif /* MPLAYER_DEC_AUDIO_H */ #endif /* MPLAYER_DEC_AUDIO_H */

View File

@ -22,6 +22,9 @@
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include "core/m_option.h"
#include "core/m_config.h"
#include "af.h" #include "af.h"
// Static list of filters // Static list of filters
@ -87,6 +90,27 @@ static struct af_info* filter_list[] = {
NULL NULL
}; };
static bool get_desc(struct m_obj_desc *dst, int index)
{
if (index >= MP_ARRAY_SIZE(filter_list) - 1)
return false;
const struct af_info *af = filter_list[index];
*dst = (struct m_obj_desc) {
.name = af->name,
.description = af->info,
.priv_size = af->priv_size,
.priv_defaults = af->priv_defaults,
.options = af->options,
.p = af,
};
return true;
}
const struct m_obj_list af_obj_list = {
.get_desc = get_desc,
.description = "audio filters",
};
static bool af_config_equals(struct mp_audio *a, struct mp_audio *b) static bool af_config_equals(struct mp_audio *a, struct mp_audio *b)
{ {
return a->format == b->format return a->format == b->format
@ -139,90 +163,58 @@ static struct mp_audio *dummy_play(struct af_instance* af, struct mp_audio* data
return 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)
{
int i = 0;
while (filter_list[i]) {
if (!strcmp(filter_list[i]->name, name))
return filter_list[i];
i++;
}
mp_msg(MSGT_AFILTER, MSGL_ERR, "Couldn't find audio filter '%s'\n", name);
return NULL;
}
/* Find filter in the dynamic filter list using it's name This
function is used for finding already initialized filters */
struct af_instance *af_get(struct af_stream *s, char *name)
{
struct af_instance *af = s->first;
// Find the filter
while (af != NULL) {
if (!strcmp(af->info->name, name))
return af;
af = af->next;
}
return NULL;
}
/* Function for creating a new filter of type name.The name may /* Function for creating a new filter of type name.The name may
contain the commandline parameters for the filter */ contain the commandline parameters for the filter */
static struct af_instance *af_create(struct af_stream *s, static struct af_instance *af_create(struct af_stream *s, char *name,
const char *name_with_cmd) char **args)
{ {
char *name = strdup(name_with_cmd); struct m_obj_desc desc;
char *cmdline = name; if (!m_obj_list_find(&desc, &af_obj_list, bstr0(name))) {
mp_tmsg(MSGT_VFILTER, MSGL_ERR,
// Allocate space for the new filter and reset all pointers "Couldn't find audio filter '%s'.\n", name);
struct af_instance *new = malloc(sizeof(struct af_instance)); return NULL;
if (!name || !new) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Could not allocate memory\n");
goto err_out;
} }
memset(new, 0, sizeof(struct af_instance)); const struct af_info *info = desc.p;
// Check for commandline parameters
char *skip = strstr(cmdline, "=");
if (skip) {
*skip = '\0'; // for name
cmdline = skip + 1;
} else {
cmdline = NULL;
}
// Find filter from name
if (NULL == (new->info = af_find(name)))
goto err_out;
/* Make sure that the filter is not already in the list if it is /* Make sure that the filter is not already in the list if it is
non-reentrant */ non-reentrant */
if (new->info->flags & AF_FLAGS_NOT_REENTRANT) { if (info->flags & AF_FLAGS_NOT_REENTRANT) {
if (af_get(s, name)) { for (struct af_instance *cur = s->first; cur; cur = cur->next) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] There can only be one " if (cur->info == info) {
"instance of the filter '%s' in each stream\n", name); mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] There can only be one "
goto err_out; "instance of the filter '%s' in each stream\n", name);
return NULL;
}
} }
} }
mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Adding filter %s \n", name); mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Adding filter %s \n", name);
struct af_instance *af = talloc_zero(NULL, struct af_instance);
*af = (struct af_instance) {
.info = info,
.mul = 1,
};
struct m_config *config = m_config_from_obj_desc(af, &desc);
if (m_config_initialize_obj(config, &desc, &af->priv, &args) < 0)
goto error;
// Initialize the new filter // Initialize the new filter
if (AF_OK == new->info->open(new)) { if (af->info->open(af) != AF_OK)
if (cmdline) { goto error;
if (AF_ERROR >= new->control(new, AF_CONTROL_COMMAND_LINE, cmdline)) if (args && af->control) {
goto err_out; // Single option string for old filters
} char *s = (char *)args; // m_config_initialize_obj did this
free(name); assert(!af->priv);
return new; if (af->control(af, AF_CONTROL_COMMAND_LINE, s) <= AF_ERROR)
goto error;
} }
err_out: return af;
free(new);
error:
mp_msg(MSGT_AFILTER, MSGL_ERR, mp_msg(MSGT_AFILTER, MSGL_ERR,
"[libaf] Couldn't create or open audio filter '%s'\n", name); "[libaf] Couldn't create or open audio filter '%s'\n", name);
free(name); talloc_free(af);
return NULL; return NULL;
} }
@ -231,14 +223,14 @@ err_out:
value is the new filter */ value is the new filter */
static struct af_instance *af_prepend(struct af_stream *s, static struct af_instance *af_prepend(struct af_stream *s,
struct af_instance *af, struct af_instance *af,
const char *name) char *name, char **args)
{ {
if (!af) if (!af)
af = s->last; af = s->last;
if (af == s->first) if (af == s->first)
af = s->first->next; 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, args);
if (!new) if (!new)
return NULL; return NULL;
// Update pointers // Update pointers
@ -254,14 +246,14 @@ static struct af_instance *af_prepend(struct af_stream *s,
value is the new filter */ value is the new filter */
static struct af_instance *af_append(struct af_stream *s, static struct af_instance *af_append(struct af_stream *s,
struct af_instance *af, struct af_instance *af,
const char *name) char *name, char **args)
{ {
if (!af) if (!af)
af = s->first; af = s->first;
if (af == s->last) if (af == s->last)
af = s->last->prev; 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, args);
if (!new) if (!new)
return NULL; return NULL;
// Update pointers // Update pointers
@ -273,7 +265,7 @@ static struct af_instance *af_append(struct af_stream *s,
} }
// Uninit and remove the filter "af" // Uninit and remove the filter "af"
void af_remove(struct af_stream *s, struct af_instance *af) static void af_remove(struct af_stream *s, struct af_instance *af)
{ {
if (!af) if (!af)
return; return;
@ -293,7 +285,7 @@ void af_remove(struct af_stream *s, struct af_instance *af)
af->next->prev = af->prev; af->next->prev = af->prev;
af->uninit(af); af->uninit(af);
free(af); talloc_free(af);
} }
static void remove_auto_inserted_filters(struct af_stream *s) static void remove_auto_inserted_filters(struct af_stream *s)
@ -341,12 +333,12 @@ static int af_count_filters(struct af_stream *s)
return count; return count;
} }
static const char *af_find_conversion_filter(int srcfmt, int dstfmt) static char *af_find_conversion_filter(int srcfmt, int dstfmt)
{ {
for (int n = 0; filter_list[n]; n++) { for (int n = 0; filter_list[n]; n++) {
struct af_info *af = filter_list[n]; struct af_info *af = filter_list[n];
if (af->test_conversion && af->test_conversion(srcfmt, dstfmt)) if (af->test_conversion && af->test_conversion(srcfmt, dstfmt))
return af->name; return (char *)af->name;
} }
return NULL; return NULL;
} }
@ -376,10 +368,10 @@ static int af_fix_format_conversion(struct af_stream *s,
*p_af = prev; *p_af = prev;
return AF_OK; return AF_OK;
} }
const char *filter = af_find_conversion_filter(actual.format, in.format); char *filter = af_find_conversion_filter(actual.format, in.format);
if (!filter) if (!filter)
return AF_ERROR; return AF_ERROR;
struct af_instance *new = af_prepend(s, af, filter); struct af_instance *new = af_prepend(s, af, filter, NULL);
if (new == NULL) if (new == NULL)
return AF_ERROR; return AF_ERROR;
new->auto_inserted = true; new->auto_inserted = true;
@ -403,8 +395,8 @@ static int af_fix_channels(struct af_stream *s, struct af_instance **p_af,
*p_af = prev; *p_af = prev;
return AF_OK; return AF_OK;
} }
const char *filter = "lavrresample"; char *filter = "lavrresample";
struct af_instance *new = af_prepend(s, af, filter); struct af_instance *new = af_prepend(s, af, filter, NULL);
if (new == NULL) if (new == NULL)
return AF_ERROR; return AF_ERROR;
new->auto_inserted = true; new->auto_inserted = true;
@ -427,8 +419,8 @@ static int af_fix_rate(struct af_stream *s, struct af_instance **p_af,
*p_af = prev; *p_af = prev;
return AF_OK; return AF_OK;
} }
const char *filter = "lavrresample"; char *filter = "lavrresample";
struct af_instance *new = af_prepend(s, af, filter); struct af_instance *new = af_prepend(s, af, filter, NULL);
if (new == NULL) if (new == NULL)
return AF_ERROR; return AF_ERROR;
new->auto_inserted = true; new->auto_inserted = true;
@ -438,6 +430,7 @@ static int af_fix_rate(struct af_stream *s, struct af_instance **p_af,
return AF_OK; return AF_OK;
} }
// Return AF_OK on success or AF_ERROR on failure.
// Warning: // Warning:
// A failed af_reinit() leaves the audio chain behind in a useless, broken // A failed af_reinit() leaves the audio chain behind in a useless, broken
// state (for example, format filters that were tentatively inserted stay // state (for example, format filters that were tentatively inserted stay
@ -445,7 +438,7 @@ static int af_fix_rate(struct af_stream *s, struct af_instance **p_af,
// 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() may have to be // Also, note that for complete reinit, fixup_output_format() may have to be
// called after this function. // called after this function.
int af_reinit(struct af_stream *s) static int af_reinit(struct af_stream *s)
{ {
// Start with the second filter, as the first filter is the special input // Start with the second filter, as the first filter is the special input
// filter which needs no initialization. // filter which needs no initialization.
@ -543,6 +536,7 @@ struct af_stream *af_new(struct MPOpts *opts)
}; };
s->first->next = s->last; s->first->next = s->last;
s->last->prev = s->first; s->last->prev = s->first;
s->opts = opts;
return s; return s;
} }
@ -578,8 +572,6 @@ static int fixup_output_format(struct af_stream *s)
The return value is 0 if success and -1 if failure */ The return value is 0 if success and -1 if failure */
int af_init(struct af_stream *s) int af_init(struct af_stream *s)
{ {
int i = 0;
// Sanity check // Sanity check
if (!s) if (!s)
return -1; return -1;
@ -591,11 +583,10 @@ int af_init(struct af_stream *s)
// Check if this is the first call // Check if this is the first call
if (s->first->next == s->last) { 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) { struct m_obj_settings *list = s->opts->af_settings;
while (s->cfg.list[i]) { for (int i = 0; list && list[i].name; i++) {
if (!af_prepend(s, s->last, s->cfg.list[i++])) if (!af_prepend(s, s->last, list[i].name, list[i].attribs))
return -1; return -1;
}
} }
} }
@ -621,7 +612,7 @@ int af_init(struct af_stream *s)
to the stream s. The filter will be inserted somewhere nice in the to the stream s. The filter will be inserted somewhere nice in the
list of filters. The return value is a pointer to the new filter, list of filters. The return value is a pointer to the new filter,
If the filter couldn't be added the return value is NULL. */ If the filter couldn't be added the return value is NULL. */
struct af_instance *af_add(struct af_stream *s, char *name) struct af_instance *af_add(struct af_stream *s, char *name, char **args)
{ {
struct af_instance *new; struct af_instance *new;
// Sanity check // Sanity check
@ -629,9 +620,9 @@ struct af_instance *af_add(struct af_stream *s, char *name)
return NULL; return NULL;
// Insert the filter somewhere nice // Insert the filter somewhere nice
if (af_is_conversion_filter(s->first->next)) if (af_is_conversion_filter(s->first->next))
new = af_append(s, s->first->next, name); new = af_append(s, s->first->next, name, args);
else else
new = af_prepend(s, s->first->next, name); new = af_prepend(s, s->first->next, name, args);
if (!new) if (!new)
return NULL; return NULL;
@ -729,21 +720,3 @@ struct af_instance *af_control_any_rev(struct af_stream *s, int cmd, void *arg)
} }
return NULL; return NULL;
} }
void af_help(void)
{
int i = 0;
mp_msg(MSGT_AFILTER, MSGL_INFO, "Available audio filters:\n");
while (filter_list[i]) {
if (filter_list[i]->comment && filter_list[i]->comment[0]) {
mp_msg(MSGT_AFILTER, MSGL_INFO, " %-15s: %s (%s)\n",
filter_list[i]->name, filter_list[i]->info,
filter_list[i]->comment);
} else {
mp_msg(MSGT_AFILTER, MSGL_INFO, " %-15s: %s\n",
filter_list[i]->name,
filter_list[i]->info);
}
i++;
}
}

View File

@ -50,15 +50,19 @@ struct af_info {
const int flags; const int flags;
int (*open)(struct af_instance *vf); int (*open)(struct af_instance *vf);
bool (*test_conversion)(int src_format, int dst_format); bool (*test_conversion)(int src_format, int dst_format);
int priv_size;
const void *priv_defaults;
const struct m_option *options;
}; };
// Linked list of audio filters // Linked list of audio filters
struct af_instance { struct af_instance {
struct af_info *info; const struct af_info *info;
int (*control)(struct af_instance *af, int cmd, void *arg); int (*control)(struct af_instance *af, int cmd, void *arg);
void (*uninit)(struct af_instance *af); void (*uninit)(struct af_instance *af);
struct mp_audio * (*play)(struct af_instance *af, struct mp_audio *data); struct mp_audio * (*play)(struct af_instance *af, struct mp_audio *data);
void *setup; // setup data for this specific instance and filter void *setup; // old field for priv structs
void *priv;
struct mp_audio *data; // configuration for outgoing data stream struct mp_audio *data; // configuration for outgoing data stream
struct af_instance *next; struct af_instance *next;
struct af_instance *prev; struct af_instance *prev;
@ -69,12 +73,6 @@ struct af_instance {
bool auto_inserted; // inserted by af.c, such as conversion filters bool auto_inserted; // inserted by af.c, such as conversion filters
}; };
// Configuration switches
struct af_cfg {
char **list; /* list of names of filters that are added to filter
list during first initialization of stream */
};
// Current audio stream // Current audio stream
struct af_stream { struct af_stream {
// The first and last filter in the list // The first and last filter in the list
@ -86,8 +84,7 @@ struct af_stream {
struct mp_audio input; struct mp_audio input;
struct mp_audio output; struct mp_audio output;
struct mp_audio filter_output; struct mp_audio filter_output;
// Configuration for this stream
struct af_cfg cfg;
struct MPOpts *opts; struct MPOpts *opts;
}; };
@ -138,13 +135,6 @@ int af_init(struct af_stream *s);
*/ */
void af_uninit(struct af_stream *s); void af_uninit(struct af_stream *s);
/**
* \brief Reinit the filter list from the given filter on downwards
* See af.c.
* \return AF_OK on success or AF_ERROR on failure
*/
int af_reinit(struct af_stream *s);
/** /**
* \brief This function adds the filter "name" to the stream s. * \brief This function adds the filter "name" to the stream s.
* \param name name of filter to add * \param name name of filter to add
@ -154,22 +144,7 @@ int af_reinit(struct af_stream *s);
* list of filters (i.e. at the beginning unless the * list of filters (i.e. at the beginning unless the
* first filter is the format filter (why??). * first filter is the format filter (why??).
*/ */
struct af_instance *af_add(struct af_stream *s, char *name); struct af_instance *af_add(struct af_stream *s, char *name, char **args);
/**
* \brief Uninit and remove the filter "af"
* \param af filter to remove
*/
void af_remove(struct af_stream *s, struct af_instance *af);
/**
* \brief find filter in chain by name
* \param name name of the filter to find
* \return first filter with right name or NULL if not found
*
* This function is used for finding already initialized filters
*/
struct af_instance *af_get(struct af_stream *s, char *name);
/** /**
* \brief filter data chunk through the filters in the list * \brief filter data chunk through the filters in the list
@ -282,9 +257,6 @@ float af_softclip(float a);
/** \} */ // end of af_filter group, but more functions of this group below /** \} */ // end of af_filter group, but more functions of this group below
/** Print a list of all available audio filters */
void af_help(void);
/** Memory reallocation macro: if a local buffer is used (i.e. if the /** Memory reallocation macro: if a local buffer is used (i.e. if the
filter doesn't operate on the incoming buffer this macro must be filter doesn't operate on the incoming buffer this macro must be
called to ensure the buffer is big enough. called to ensure the buffer is big enough.

View File

@ -113,7 +113,7 @@ static void setvolume_internal(mixer_t *mixer, float l, float r)
{ {
mp_tmsg(MSGT_GLOBAL, mixer->softvol ? MSGL_V : MSGL_WARN, mp_tmsg(MSGT_GLOBAL, mixer->softvol ? MSGL_V : MSGL_WARN,
"[Mixer] No hardware mixing, inserting volume filter.\n"); "[Mixer] No hardware mixing, inserting volume filter.\n");
if (!(af_add(mixer->afilter, "volume") if (!(af_add(mixer->afilter, "volume", NULL)
&& af_control_any_rev(mixer->afilter, && af_control_any_rev(mixer->afilter,
AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET,
db_vals))) db_vals)))
@ -220,7 +220,7 @@ void mixer_setbalance(mixer_t *mixer, float val)
if (val == 0 || mixer->ao->channels.num < 2) if (val == 0 || mixer->ao->channels.num < 2)
return; return;
if (!(af_pan_balance = af_add(mixer->afilter, "pan"))) { if (!(af_pan_balance = af_add(mixer->afilter, "pan", NULL))) {
mp_tmsg(MSGT_GLOBAL, MSGL_ERR, mp_tmsg(MSGT_GLOBAL, MSGL_ERR,
"[Mixer] No balance control available.\n"); "[Mixer] No balance control available.\n");
return; return;

View File

@ -1914,43 +1914,72 @@ static const char *property_error_string(int error_value)
return "UNKNOWN"; return "UNKNOWN";
} }
static void change_video_filters(MPContext *mpctx, const char *cmd, static bool reinit_filters(MPContext *mpctx, enum stream_type mediatype)
const char *arg) {
switch (mediatype) {
case STREAM_VIDEO:
return reinit_video_filters(mpctx) >= 0;
case STREAM_AUDIO:
return reinit_audio_filters(mpctx) >= 0;
}
return false;
}
static void change_filters(MPContext *mpctx, enum stream_type mediatype,
const char *cmd, const char *arg)
{ {
struct MPOpts *opts = &mpctx->opts; struct MPOpts *opts = &mpctx->opts;
struct m_config *conf = mpctx->mconfig; struct m_config *conf = mpctx->mconfig;
struct m_obj_settings *old_vf_settings = NULL; struct m_obj_settings *old_settings = NULL;
bool success = false; bool success = false;
bool need_refresh = false; bool need_refresh = false;
const char *option;
struct m_obj_settings **list;
switch (mediatype) {
case STREAM_VIDEO:
option = "vf";
list = &opts->vf_settings;
break;
case STREAM_AUDIO:
option = "af";
list = &opts->af_settings;
break;
}
// The option parser is used to modify the filter list itself. // The option parser is used to modify the filter list itself.
char optname[20]; char optname[20];
snprintf(optname, sizeof(optname), "vf-%s", cmd); snprintf(optname, sizeof(optname), "%s-%s", option, cmd);
const struct m_option *type = m_config_get_option(conf, bstr0(optname)); const struct m_option *type = m_config_get_option(conf, bstr0(optname));
// Backup old settings, in case it fails // Backup old settings, in case it fails
m_option_copy(type, &old_vf_settings, &opts->vf_settings); m_option_copy(type, &old_settings, list);
if (m_config_set_option0(conf, optname, arg) >= 0) { if (m_config_set_option0(conf, optname, arg) >= 0) {
need_refresh = true; need_refresh = true;
success = reinit_video_filters(mpctx) >= 0; success = reinit_filters(mpctx, mediatype);
} }
if (!success) { if (!success) {
m_option_copy(type, &opts->vf_settings, &old_vf_settings); m_option_copy(type, list, &old_settings);
if (need_refresh) if (need_refresh)
reinit_video_filters(mpctx); reinit_filters(mpctx, mediatype);
} }
m_option_free(type, &old_vf_settings); m_option_free(type, &old_settings);
if (need_refresh) if (need_refresh && mediatype == STREAM_VIDEO)
mp_force_video_refresh(mpctx); mp_force_video_refresh(mpctx);
} }
static void change_video_filters(MPContext *mpctx, const char *cmd,
const char *arg)
{
change_filters(mpctx, STREAM_VIDEO, cmd, arg);
}
void run_command(MPContext *mpctx, mp_cmd_t *cmd) void run_command(MPContext *mpctx, mp_cmd_t *cmd)
{ {
struct MPOpts *opts = &mpctx->opts; struct MPOpts *opts = &mpctx->opts;
sh_audio_t *const sh_audio = mpctx->sh_audio;
sh_video_t *const sh_video = mpctx->sh_video; sh_video_t *const sh_video = mpctx->sh_video;
int osd_duration = opts->osd_duration; int osd_duration = opts->osd_duration;
bool auto_osd = cmd->on_osd == MP_ON_OSD_AUTO; bool auto_osd = cmd->on_osd == MP_ON_OSD_AUTO;
@ -2467,54 +2496,10 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
} }
break; break;
case MP_CMD_AF_SWITCH: case MP_CMD_AF:
if (sh_audio) { change_filters(mpctx, STREAM_AUDIO, cmd->args[0].v.s, cmd->args[1].v.s);
af_uninit(mpctx->mixer.afilter);
af_init(mpctx->mixer.afilter);
}
/* fallthrough */
case MP_CMD_AF_ADD:
case MP_CMD_AF_DEL: {
if (!sh_audio)
break;
char *af_args = strdup(cmd->args[0].v.s);
bstr af_commands = bstr0(af_args);
struct af_instance *af;
while (af_commands.len) {
bstr af_command;
bstr_split_tok(af_commands, ",", &af_command, &af_commands);
char *af_command0 = bstrdup0(NULL, af_command);
if (cmd->id == MP_CMD_AF_DEL) {
af = af_get(mpctx->mixer.afilter, af_command0);
if (af != NULL)
af_remove(mpctx->mixer.afilter, af);
} else
af_add(mpctx->mixer.afilter, af_command0);
talloc_free(af_command0);
}
reinit_audio_chain(mpctx);
free(af_args);
break;
}
case MP_CMD_AF_CLR:
if (!sh_audio)
break;
af_uninit(mpctx->mixer.afilter);
af_init(mpctx->mixer.afilter);
reinit_audio_chain(mpctx);
break;
case MP_CMD_AF_CMDLINE:
if (sh_audio) {
struct af_instance *af = af_get(sh_audio->afilter, cmd->args[0].v.s);
if (!af) {
mp_msg(MSGT_CPLAYER, MSGL_WARN,
"Filter '%s' not found in chain.\n", cmd->args[0].v.s);
break;
}
af->control(af, AF_CONTROL_COMMAND_LINE, cmd->args[1].v.s);
af_reinit(sh_audio->afilter);
}
break; break;
case MP_CMD_VF: case MP_CMD_VF:
change_video_filters(mpctx, cmd->args[0].v.s, cmd->args[1].v.s); change_video_filters(mpctx, cmd->args[0].v.s, cmd->args[1].v.s);
break; break;

View File

@ -210,11 +210,7 @@ static const mp_cmd_t mp_cmds[] = {
}}, }},
{ MP_CMD_DISABLE_INPUT_SECTION, "disable_section", { ARG_STRING } }, { MP_CMD_DISABLE_INPUT_SECTION, "disable_section", { ARG_STRING } },
{ MP_CMD_AF_SWITCH, "af_switch", { ARG_STRING } }, { MP_CMD_AF, "af", { ARG_STRING, ARG_STRING } },
{ MP_CMD_AF_ADD, "af_add", { ARG_STRING } },
{ MP_CMD_AF_DEL, "af_del", { ARG_STRING } },
{ MP_CMD_AF_CLR, "af_clr", },
{ MP_CMD_AF_CMDLINE, "af_cmdline", { ARG_STRING, ARG_STRING } },
{ MP_CMD_VF, "vf", { ARG_STRING, ARG_STRING } }, { MP_CMD_VF, "vf", { ARG_STRING, ARG_STRING } },

View File

@ -77,11 +77,7 @@ enum mp_command_type {
MP_CMD_DVB_SET_CHANNEL = 5101, MP_CMD_DVB_SET_CHANNEL = 5101,
/// Audio Filter commands /// Audio Filter commands
MP_CMD_AF_SWITCH, MP_CMD_AF,
MP_CMD_AF_ADD,
MP_CMD_AF_DEL,
MP_CMD_AF_CLR,
MP_CMD_AF_CMDLINE,
/// Video filter commands /// Video filter commands
MP_CMD_VF, MP_CMD_VF,

View File

@ -296,6 +296,7 @@ double playing_audio_pts(struct MPContext *mpctx);
struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename, int noerr); struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename, int noerr);
int reinit_video_chain(struct MPContext *mpctx); int reinit_video_chain(struct MPContext *mpctx);
int reinit_video_filters(struct MPContext *mpctx); int reinit_video_filters(struct MPContext *mpctx);
int reinit_audio_filters(struct MPContext *mpctx);
void pause_player(struct MPContext *mpctx); void pause_player(struct MPContext *mpctx);
void unpause_player(struct MPContext *mpctx); void unpause_player(struct MPContext *mpctx);
void add_step_frame(struct MPContext *mpctx, int dir); void add_step_frame(struct MPContext *mpctx, int dir);

View File

@ -1185,11 +1185,6 @@ static int build_afilter_chain(struct MPContext *mpctx)
struct ao *ao = mpctx->ao; struct ao *ao = mpctx->ao;
struct MPOpts *opts = &mpctx->opts; struct MPOpts *opts = &mpctx->opts;
int new_srate; int new_srate;
int result;
if (!sh_audio) {
mpctx->mixer.afilter = NULL;
return 0;
}
if (af_control_any_rev(sh_audio->afilter, if (af_control_any_rev(sh_audio->afilter,
AF_CONTROL_PLAYBACK_SPEED | AF_CONTROL_SET, AF_CONTROL_PLAYBACK_SPEED | AF_CONTROL_SET,
&opts->playback_speed)) &opts->playback_speed))
@ -1205,10 +1200,8 @@ static int build_afilter_chain(struct MPContext *mpctx)
opts->playback_speed = (float)new_srate / sh_audio->samplerate; opts->playback_speed = (float)new_srate / sh_audio->samplerate;
} }
} }
result = init_audio_filters(sh_audio, new_srate, return init_audio_filters(sh_audio, new_srate,
&ao->samplerate, &ao->channels, &ao->format); &ao->samplerate, &ao->channels, &ao->format);
mpctx->mixer.afilter = sh_audio->afilter;
return result;
} }
@ -1572,6 +1565,51 @@ static void update_osd_msg(struct MPContext *mpctx)
} }
} }
static int recreate_audio_filters(struct MPContext *mpctx)
{
struct MPOpts *opts = &mpctx->opts;
assert(mpctx->sh_audio);
// init audio filters:
if (!build_afilter_chain(mpctx)) {
mp_tmsg(MSGT_CPLAYER, MSGL_ERR,
"Couldn't find matching filter/ao format!\n");
return -1;
}
mpctx->mixer.afilter = mpctx->sh_audio->afilter;
mpctx->mixer.volstep = opts->volstep;
mpctx->mixer.softvol = opts->softvol;
mpctx->mixer.softvol_max = opts->softvol_max;
mixer_reinit(&mpctx->mixer, mpctx->ao);
if (!(mpctx->initialized_flags & INITIALIZED_VOL)) {
if (opts->mixer_init_volume >= 0) {
mixer_setvolume(&mpctx->mixer, opts->mixer_init_volume,
opts->mixer_init_volume);
}
if (opts->mixer_init_mute >= 0)
mixer_setmute(&mpctx->mixer, opts->mixer_init_mute);
mpctx->initialized_flags |= INITIALIZED_VOL;
}
return 0;
}
int reinit_audio_filters(struct MPContext *mpctx)
{
struct sh_audio *sh_audio = mpctx->sh_audio;
if (!sh_audio)
return -2;
af_uninit(mpctx->sh_audio->afilter);
if (af_init(mpctx->sh_audio->afilter) < 0)
return -1;
if (recreate_audio_filters(mpctx) < 0)
return -1;
return 0;
}
void reinit_audio_chain(struct MPContext *mpctx) void reinit_audio_chain(struct MPContext *mpctx)
{ {
struct MPOpts *opts = &mpctx->opts; struct MPOpts *opts = &mpctx->opts;
@ -1603,7 +1641,9 @@ void reinit_audio_chain(struct MPContext *mpctx)
} }
} }
// first init to detect best values // Determine what the filter chain outputs. build_afilter_chain() also
// needs this for testing whether playback speed is changed by resampling
// or using a special filter.
if (!init_audio_filters(mpctx->sh_audio, // preliminary init if (!init_audio_filters(mpctx->sh_audio, // preliminary init
// input: // input:
mpctx->sh_audio->samplerate, mpctx->sh_audio->samplerate,
@ -1638,25 +1678,9 @@ void reinit_audio_chain(struct MPContext *mpctx)
ao->driver->info->comment); ao->driver->info->comment);
} }
// init audio filters: if (recreate_audio_filters(mpctx) < 0)
if (!build_afilter_chain(mpctx)) {
mp_tmsg(MSGT_CPLAYER, MSGL_ERR,
"Couldn't find matching filter/ao format!\n");
goto init_error; goto init_error;
}
mpctx->mixer.volstep = opts->volstep;
mpctx->mixer.softvol = opts->softvol;
mpctx->mixer.softvol_max = opts->softvol_max;
mixer_reinit(&mpctx->mixer, mpctx->ao);
if (!(mpctx->initialized_flags & INITIALIZED_VOL)) {
if (opts->mixer_init_volume >= 0) {
mixer_setvolume(&mpctx->mixer, opts->mixer_init_volume,
opts->mixer_init_volume);
}
if (opts->mixer_init_mute >= 0)
mixer_setmute(&mpctx->mixer, opts->mixer_init_mute);
mpctx->initialized_flags |= INITIALIZED_VOL;
}
mpctx->syncing_audio = true; mpctx->syncing_audio = true;
return; return;
@ -4512,11 +4536,6 @@ static bool handle_help_options(struct MPContext *mpctx)
talloc_free(list); talloc_free(list);
opt_exit = 1; opt_exit = 1;
} }
if (af_cfg.list && strcmp(af_cfg.list[0], "help") == 0) {
af_help();
mp_msg(MSGT_CPLAYER, MSGL_INFO, "\n");
opt_exit = 1;
}
#ifdef CONFIG_X11 #ifdef CONFIG_X11
if (opts->vo.fstype_list && strcmp(opts->vo.fstype_list[0], "help") == 0) { if (opts->vo.fstype_list && strcmp(opts->vo.fstype_list[0], "help") == 0) {
fstype_help(); fstype_help();

View File

@ -182,6 +182,7 @@ extern char *dvd_device, *cdrom_device;
extern double mf_fps; extern double mf_fps;
extern char * mf_type; extern char * mf_type;
extern const struct m_obj_list vf_obj_list; extern const struct m_obj_list vf_obj_list;
extern const struct m_obj_list af_obj_list;
extern const struct m_obj_list vo_obj_list; extern const struct m_obj_list vo_obj_list;
extern const struct m_obj_list ao_obj_list; extern const struct m_obj_list ao_obj_list;
@ -436,8 +437,7 @@ const m_option_t mp_opts[] = {
// ------------------------- codec/vfilter options -------------------- // ------------------------- codec/vfilter options --------------------
{"af*", &af_cfg.list, CONF_TYPE_STRING_LIST, 0, 0, 0, NULL}, OPT_SETTINGSLIST("af*", af_settings, 0, &af_obj_list),
OPT_SETTINGSLIST("vf*", vf_settings, 0, &vf_obj_list), OPT_SETTINGSLIST("vf*", vf_settings, 0, &vf_obj_list),
OPT_STRING("ad", audio_decoders, 0), OPT_STRING("ad", audio_decoders, 0),

View File

@ -162,6 +162,7 @@ typedef struct MPOpts {
int dtshd; int dtshd;
float playback_speed; float playback_speed;
struct m_obj_settings *vf_settings; struct m_obj_settings *vf_settings;
struct m_obj_settings *af_settings;
float movie_aspect; float movie_aspect;
int flip; int flip;
int field_dominance; int field_dominance;