From b9f804b566c4c528714e4ec5e63675ad7ba5fefd Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 18 Jan 2018 14:44:20 +0100 Subject: audio: rewrite filtering glue code Use the new filtering code for audio too. --- audio/filter/af.c | 824 ----------------------------------------- audio/filter/af.h | 163 -------- audio/filter/af_format.c | 173 ++++----- audio/filter/af_lavcac3enc.c | 504 ++++++++++++------------- audio/filter/af_lavfi.c | 413 --------------------- audio/filter/af_lavrresample.c | 187 +++------- audio/filter/af_rubberband.c | 446 +++++++++++++--------- audio/filter/af_scaletempo.c | 577 +++++++++++++++++------------ audio/filter/tools.c | 72 ---- 9 files changed, 985 insertions(+), 2374 deletions(-) delete mode 100644 audio/filter/af.c delete mode 100644 audio/filter/af.h delete mode 100644 audio/filter/af_lavfi.c delete mode 100644 audio/filter/tools.c (limited to 'audio/filter') diff --git a/audio/filter/af.c b/audio/filter/af.c deleted file mode 100644 index cf200bbb84..0000000000 --- a/audio/filter/af.c +++ /dev/null @@ -1,824 +0,0 @@ -/* - * This file is part of mpv. - * - * mpv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * mpv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with mpv. If not, see . - */ - -#include "config.h" -#include -#include -#include -#include - -#include "common/common.h" -#include "common/global.h" - -#include "options/m_option.h" -#include "options/m_config.h" - -#include "audio/audio_buffer.h" -#include "af.h" - -// Static list of filters -extern const struct af_info af_info_format; -extern const struct af_info af_info_lavcac3enc; -extern const struct af_info af_info_lavrresample; -extern const struct af_info af_info_scaletempo; -extern const struct af_info af_info_lavfi; -extern const struct af_info af_info_lavfi_bridge; -extern const struct af_info af_info_rubberband; - -static const struct af_info *const filter_list[] = { - &af_info_format, - &af_info_lavcac3enc, - &af_info_lavrresample, -#if HAVE_RUBBERBAND - &af_info_rubberband, -#endif - &af_info_scaletempo, - &af_info_lavfi, - &af_info_lavfi_bridge, - 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, - .set_defaults = af->set_defaults, - .p = af, - }; - return true; -} - -const struct m_obj_list af_obj_list = { - .get_desc = get_desc, - .description = "audio filters", - .allow_disable_entries = true, - .allow_unknown_entries = true, - .aliases = { - {"force", "format"}, - {0} - }, -}; - -static void af_forget_frames(struct af_instance *af) -{ - for (int n = 0; n < af->num_out_queued; n++) - talloc_free(af->out_queued[n]); - af->num_out_queued = 0; -} - -static void af_chain_forget_frames(struct af_stream *s) -{ - for (struct af_instance *cur = s->first; cur; cur = cur->next) - af_forget_frames(cur); -} - -static void af_copy_unset_fields(struct mp_audio *dst, struct mp_audio *src) -{ - if (dst->format == AF_FORMAT_UNKNOWN) - mp_audio_set_format(dst, src->format); - if (dst->nch == 0) - mp_audio_set_channels(dst, &src->channels); - 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->priv)->input); - return AF_OK; - } - return AF_UNKNOWN; -} - -static int output_control(struct af_instance* af, int cmd, void* arg) -{ - struct af_stream *s = af->priv; - 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 mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE; - } - } - return AF_UNKNOWN; -} - -static int dummy_filter(struct af_instance *af, struct mp_audio *frame) -{ - af_add_output_frame(af, frame); - return 0; -} - -/* Function for creating a new filter of type name.The name may -contain the commandline parameters for the filter */ -static struct af_instance *af_create(struct af_stream *s, char *name, - char **args) -{ - const char *lavfi_name = NULL; - char **lavfi_args = NULL; - struct m_obj_desc desc; - if (!m_obj_list_find(&desc, &af_obj_list, bstr0(name))) { - if (!m_obj_list_find(&desc, &af_obj_list, bstr0("lavfi-bridge"))) { - MP_ERR(s, "Couldn't find audio filter '%s'.\n", name); - return NULL; - } - lavfi_name = name; - lavfi_args = args; - args = NULL; - if (strncmp(lavfi_name, "lavfi-", 6) == 0) - lavfi_name += 6; - } - MP_VERBOSE(s, "Adding filter %s \n", name); - - struct af_instance *af = talloc_zero(NULL, struct af_instance); - *af = (struct af_instance) { - .full_name = talloc_strdup(af, name), - .info = desc.p, - .data = talloc_zero(af, struct mp_audio), - .log = mp_log_new(af, s->log, name), - .opts = s->opts, - .global = s->global, - .out_pool = mp_audio_pool_create(af), - }; - struct m_config *config = - m_config_from_obj_desc_and_args(af, s->log, s->global, &desc, - name, s->opts->af_defs, args); - if (!config) - goto error; - if (lavfi_name) { - // Pass the filter arguments as proper sub-options to the bridge filter. - struct m_config_option *name_opt = m_config_get_co(config, bstr0("name")); - assert(name_opt); - assert(name_opt->opt->type == &m_option_type_string); - if (m_config_set_option_raw(config, name_opt, &lavfi_name, 0) < 0) - goto error; - struct m_config_option *opts = m_config_get_co(config, bstr0("opts")); - assert(opts); - assert(opts->opt->type == &m_option_type_keyvalue_list); - if (m_config_set_option_raw(config, opts, &lavfi_args, 0) < 0) - goto error; - af->full_name = talloc_asprintf(af, "%s (lavfi)", af->full_name); - } - af->priv = config->optstruct; - - // Initialize the new filter - if (af->info->open(af) != AF_OK) - goto error; - - return af; - -error: - MP_ERR(s, "Couldn't create or open audio filter '%s'\n", name); - talloc_free(af); - return NULL; -} - -/* Create and insert a new filter of type name before the filter in the - argument. This function can be called during runtime, the return - value is the new filter */ -static struct af_instance *af_prepend(struct af_stream *s, - struct af_instance *af, - char *name, char **args) -{ - 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, args); - if (!new) - return NULL; - // Update pointers - new->next = af; - new->prev = af->prev; - af->prev = new; - new->prev->next = new; - return new; -} - -// Uninit and remove the filter "af" -static 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_VERBOSE(s, "Removing filter %s \n", af->info->name); - - // Detach pointers - af->prev->next = af->next; - af->next->prev = af->prev; - - if (af->uninit) - af->uninit(af); - af_forget_frames(af); - talloc_free(af); -} - -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) { - af_remove(s, af); - goto repeat; - } - } -} - -static void af_print_filter_chain(struct af_stream *s, struct af_instance *at, - int msg_level) -{ - MP_MSG(s, msg_level, "Audio filter chain:\n"); - - struct af_instance *af = s->first; - while (af) { - char b[128] = {0}; - mp_snprintf_cat(b, sizeof(b), " [%s] ", af->full_name); - if (af->label) - mp_snprintf_cat(b, sizeof(b), "\"%s\" ", af->label); - if (af->data) - mp_snprintf_cat(b, sizeof(b), "%s", mp_audio_config_to_str(af->data)); - if (af->auto_inserted) - mp_snprintf_cat(b, sizeof(b), " [a]"); - if (af == at) - mp_snprintf_cat(b, sizeof(b), " <-"); - MP_MSG(s, msg_level, "%s\n", b); - - af = af->next; - } - - MP_MSG(s, msg_level, " [ao] %s\n", mp_audio_config_to_str(&s->output)); -} - -static void reset_formats(struct af_stream *s) -{ - struct mp_audio none = {0}; - for (struct af_instance *af = s->first; af; af = af->next) { - if (af != s->first && af != s->last) - mp_audio_copy_config(af->data, &none); - } -} - -static int filter_reinit(struct af_instance *af) -{ - struct af_instance *prev = af->prev; - assert(prev); - - // Check if this is the first filter - struct mp_audio in = *prev->data; - // Reset just in case... - mp_audio_set_null_data(&in); - - if (!mp_audio_config_valid(&in)) - return AF_ERROR; - - af->fmt_in = in; - int rv = af->control(af, AF_CONTROL_REINIT, &in); - if (rv == AF_OK && !mp_audio_config_equals(&in, prev->data)) - rv = AF_FALSE; // conversion filter needed - if (rv == AF_FALSE) - af->fmt_in = in; - - if (rv == AF_OK) { - if (!mp_audio_config_valid(af->data)) - return AF_ERROR; - af->fmt_out = *af->data; - } - - return rv; -} - -static int filter_reinit_with_conversion(struct af_stream *s, struct af_instance *af) -{ - int rv = filter_reinit(af); - - // Conversion filter is needed - if (rv == AF_FALSE) { - // First try if we can change the output format of the previous - // filter to the input format the current filter is expecting. - struct mp_audio in = af->fmt_in; - if (af->prev != s->first && !mp_audio_config_equals(af->prev->data, &in)) { - // This should have been successful (because it succeeded - // before), even if just reverting to the old output format. - mp_audio_copy_config(af->prev->data, &in); - rv = filter_reinit(af->prev); - if (rv != AF_OK) - return rv; - } - if (!mp_audio_config_equals(af->prev->data, &in)) { - // Retry with conversion filter added. - char *opts[] = {"deprecation-warning", "no", NULL}; - struct af_instance *new = - af_prepend(s, af, "lavrresample", opts); - if (!new) - return AF_ERROR; - new->auto_inserted = true; - mp_audio_copy_config(new->data, &in); - rv = filter_reinit(new); - if (rv != AF_OK) - af_remove(s, new); - } - if (rv == AF_OK) - rv = filter_reinit(af); - } - - return rv; -} - -static int af_find_output_conversion(struct af_stream *s, struct mp_audio *cfg) -{ - assert(mp_audio_config_valid(&s->output)); - assert(s->initialized > 0); - - if (mp_chmap_equals_reordered(&s->input.channels, &s->output.channels)) - return AF_ERROR; - - // Heuristic to detect point of conversion. If it looks like something - // more complicated is going on, better bail out. - // We expect that the last filter converts channels. - struct af_instance *conv = s->last->prev; - if (!conv->auto_inserted) - return AF_ERROR; - if (!(mp_chmap_equals_reordered(&conv->fmt_in.channels, &s->input.channels) && - mp_chmap_equals_reordered(&conv->fmt_out.channels, &s->output.channels))) - return AF_ERROR; - // Also, should be the only one which does auto conversion. - for (struct af_instance *af = s->first->next; af != s->last; af = af->next) - { - if (af != conv && af->auto_inserted && - !mp_chmap_equals_reordered(&af->fmt_in.channels, &af->fmt_out.channels)) - return AF_ERROR; - } - // And not if it's the only filter. - if (conv->prev == s->first && conv->next == s->last) - return AF_ERROR; - - *cfg = s->output; - return AF_OK; -} - -// Return AF_OK on success or AF_ERROR on failure. -static int af_do_reinit(struct af_stream *s, bool second_pass) -{ - struct mp_audio convert_early = {0}; - if (second_pass) { - // If a channel conversion happens, and it is done by an auto-inserted - // filter, then insert a filter to convert it early. Otherwise, do - // nothing and return immediately. - if (af_find_output_conversion(s, &convert_early) != AF_OK) - return AF_OK; - } - - remove_auto_inserted_filters(s); - af_chain_forget_frames(s); - reset_formats(s); - s->first->fmt_in = s->first->fmt_out = s->input; - - if (mp_audio_config_valid(&convert_early)) { - char *opts[] = {"deprecation-warning", "no", NULL}; - struct af_instance *new = af_prepend(s, s->first, "lavrresample", opts); - if (!new) - return AF_ERROR; - new->auto_inserted = true; - mp_audio_copy_config(new->data, &convert_early); - int rv = filter_reinit(new); - if (rv != AF_DETACH && rv != AF_OK) - return AF_ERROR; - MP_VERBOSE(s, "Moving up output conversion.\n"); - } - - // 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; - while (af) { - int rv = filter_reinit_with_conversion(s, af); - - switch (rv) { - case AF_OK: - af = af->next; - break; - case AF_FALSE: { - // If the format conversion is (probably) caused by spdif, then - // (as a feature) drop the filter, instead of failing hard. - int fmt_in1 = af->prev->data->format; - int fmt_in2 = af->fmt_in.format; - if (af_fmt_is_valid(fmt_in1) && af_fmt_is_valid(fmt_in2)) { - bool spd1 = af_fmt_is_spdif(fmt_in1); - bool spd2 = af_fmt_is_spdif(fmt_in2); - if (spd1 != spd2 && af->next) { - MP_WARN(af, "Filter %s apparently cannot be used due to " - "spdif passthrough - removing it.\n", - af->info->name); - struct af_instance *aft = af->prev; - af_remove(s, af); - af = aft->next; - continue; - } - } - goto negotiate_error; - } - case AF_DETACH: { // Filter is redundant and wants to be unloaded - struct af_instance *aft = af->prev; // never NULL - af_remove(s, af); - af = aft->next; - break; - } - default: - MP_ERR(s, "Reinitialization did not work, " - "audio filter '%s' returned error code %i\n", - af->info->name, rv); - goto error; - } - } - - /* 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. (Audio outputs generally can't change format at runtime.) */ - af_copy_unset_fields(&s->output, &s->filter_output); - if (mp_audio_config_equals(&s->output, &s->filter_output)) { - s->initialized = 1; - af_print_filter_chain(s, NULL, MSGL_V); - return AF_OK; - } - - goto error; - -negotiate_error: - MP_ERR(s, "Unable to convert audio input format to output format.\n"); -error: - s->initialized = -1; - af_print_filter_chain(s, af, MSGL_ERR); - return AF_ERROR; -} - -static int af_reinit(struct af_stream *s) -{ - int r = af_do_reinit(s, false); - if (r == AF_OK && mp_audio_config_valid(&s->output)) { - r = af_do_reinit(s, true); - if (r != AF_OK) { - MP_ERR(s, "Failed second pass filter negotiation.\n"); - r = af_do_reinit(s, false); - } - } - return r; -} - -// Uninit and remove all filters -void af_uninit(struct af_stream *s) -{ - while (s->first->next && s->first->next != s->last) - af_remove(s, s->first->next); - af_chain_forget_frames(s); - s->initialized = 0; -} - -struct af_stream *af_new(struct mpv_global *global) -{ - struct af_stream *s = talloc_zero(NULL, struct af_stream); - s->log = mp_log_new(s, global->log, "!af"); - - static const struct af_info in = { .name = "in" }; - s->first = talloc(s, struct af_instance); - *s->first = (struct af_instance) { - .full_name = "in", - .info = &in, - .log = s->log, - .control = input_control, - .filter_frame = dummy_filter, - .priv = s, - .data = &s->input, - }; - - static const struct af_info out = { .name = "out" }; - s->last = talloc(s, struct af_instance); - *s->last = (struct af_instance) { - .full_name = "out", - .info = &out, - .log = s->log, - .control = output_control, - .filter_frame = dummy_filter, - .priv = s, - .data = &s->filter_output, - }; - - s->first->next = s->last; - s->last->prev = s->first; - s->opts = global->opts; - s->global = global; - return s; -} - -void af_destroy(struct af_stream *s) -{ - af_uninit(s); - talloc_free(s); -} - -/* Initialize the stream "s". This function creates a new filter list - if necessary according to the values set in input and output. Input - and output should contain the format of the current movie and the - format of the preferred output respectively. The function is - reentrant i.e. if called with an already initialized stream the - stream will be reinitialized. - If one of the preferred output parameters is 0 the one that needs - no conversion is used (i.e. the output format in the last filter). - The return value is 0 if success and -1 if failure */ -int af_init(struct af_stream *s) -{ - // Precaution in case caller is misbehaving - mp_audio_set_null_data(&s->input); - mp_audio_set_null_data(&s->output); - - // Check if this is the first call - if (s->first->next == s->last) { - // Add all filters in the list (if there are any) - struct m_obj_settings *list = s->opts->af_settings; - for (int i = 0; list && list[i].name; i++) { - if (!list[i].enabled) - continue; - struct af_instance *af = - af_prepend(s, s->last, list[i].name, list[i].attribs); - if (!af) { - af_uninit(s); - s->initialized = -1; - return -1; - } - af->label = talloc_strdup(af, list[i].label); - } - } - - if (af_reinit(s) != AF_OK) { - // Something is stuffed audio out will not work - MP_ERR(s, "Could not create audio filter chain.\n"); - return -1; - } - return 0; -} - -/* Add filter during execution. This function adds the filter "name" - 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, - If the filter couldn't be added the return value is NULL. */ -struct af_instance *af_add(struct af_stream *s, char *name, char *label, - char **args) -{ - assert(label); - - if (af_find_by_label(s, label)) - return NULL; - - struct af_instance *new = af_prepend(s, s->last, name, args); - if (!new) - return NULL; - new->label = talloc_strdup(new, label); - - // Reinitialize the filter list - if (af_reinit(s) != AF_OK) { - af_remove_by_label(s, label); - return NULL; - } - return af_find_by_label(s, label); -} - -struct af_instance *af_find_by_label(struct af_stream *s, char *label) -{ - for (struct af_instance *af = s->first; af; af = af->next) { - if (af->label && strcmp(af->label, label) == 0) - return af; - } - return NULL; -} - -/* Remove the first filter that matches this name. Return number of filters - * removed (0, 1), or a negative error code if reinit after removing failed. - */ -int af_remove_by_label(struct af_stream *s, char *label) -{ - struct af_instance *af = af_find_by_label(s, label); - if (!af) - return 0; - af_remove(s, af); - if (af_reinit(s) != AF_OK) { - af_uninit(s); - af_init(s); - return -1; - } - return 1; -} - -/* Calculate the total delay [seconds of output] caused by the filters */ -double af_calc_delay(struct af_stream *s) -{ - struct af_instance *af = s->first; - double delay = 0.0; - while (af) { - delay += af->delay; - for (int n = 0; n < af->num_out_queued; n++) - delay += af->out_queued[n]->samples / (double)af->data->rate; - af = af->next; - } - return delay; -} - -/* Send control to all filters, starting with the last until one accepts the - * command with AF_OK. Return the accepting filter. */ -struct af_instance *af_control_any_rev(struct af_stream *s, int cmd, void *arg) -{ - int res = AF_UNKNOWN; - struct af_instance *filt = s->last; - while (filt) { - res = filt->control(filt, cmd, arg); - if (res == AF_OK) - return filt; - filt = filt->prev; - } - return NULL; -} - -/* Send control to all filters. Never stop, even if a filter returns AF_OK. */ -void af_control_all(struct af_stream *s, int cmd, void *arg) -{ - for (struct af_instance *af = s->first; af; af = af->next) - af->control(af, cmd, arg); -} - -int af_control_by_label(struct af_stream *s, int cmd, void *arg, bstr label) -{ - char *label_str = bstrdup0(NULL, label); - struct af_instance *cur = af_find_by_label(s, label_str); - talloc_free(label_str); - if (cur) { - return cur->control ? cur->control(cur, cmd, arg) : CONTROL_NA; - } else { - return CONTROL_UNKNOWN; - } -} - -int af_send_command(struct af_stream *s, char *label, char *cmd, char *arg) -{ - char *args[2] = {cmd, arg}; - if (strcmp(label, "all") == 0) { - af_control_all(s, AF_CONTROL_COMMAND, args); - return 0; - } else { - return af_control_by_label(s, AF_CONTROL_COMMAND, args, bstr0(label)); - } -} - -// Used by filters to add a filtered frame to the output queue. -// Ownership of frame is transferred from caller to the filter chain. -void af_add_output_frame(struct af_instance *af, struct mp_audio *frame) -{ - if (frame) { - assert(mp_audio_config_equals(&af->fmt_out, frame)); - MP_TARRAY_APPEND(af, af->out_queued, af->num_out_queued, frame); - } -} - -static bool af_has_output_frame(struct af_instance *af) -{ - if (!af->num_out_queued && af->filter_out) { - if (af->filter_out(af) < 0) - MP_ERR(af, "Error filtering frame.\n"); - } - return af->num_out_queued > 0; -} - -static struct mp_audio *af_dequeue_output_frame(struct af_instance *af) -{ - struct mp_audio *res = NULL; - if (af_has_output_frame(af)) { - res = af->out_queued[0]; - MP_TARRAY_REMOVE_AT(af->out_queued, af->num_out_queued, 0); - } - return res; -} - -static void read_remaining(struct af_instance *af) -{ - int num_frames; - do { - num_frames = af->num_out_queued; - if (!af->filter_out || af->filter_out(af) < 0) - break; - } while (num_frames != af->num_out_queued); -} - -static int af_do_filter(struct af_instance *af, struct mp_audio *frame) -{ - if (frame) - assert(mp_audio_config_equals(&af->fmt_in, frame)); - int r = af->filter_frame(af, frame); - if (r < 0) - MP_ERR(af, "Error filtering frame.\n"); - return r; -} - -// Input a frame into the filter chain. Ownership of frame is transferred. -// Return >= 0 on success, < 0 on failure (even if output frames were produced) -int af_filter_frame(struct af_stream *s, struct mp_audio *frame) -{ - assert(frame); - if (s->initialized < 1) { - talloc_free(frame); - return -1; - } - return af_do_filter(s->first, frame); -} - -// Output the next queued frame (if any) from the full filter chain. -// The frame can be retrieved with af_read_output_frame(). -// eof: if set, assume there's no more input i.e. af_filter_frame() will -// not be called (until reset) - flush all internally delayed frames -// returns: -1: error, 0: no output, 1: output available -int af_output_frame(struct af_stream *s, bool eof) -{ - if (s->last->num_out_queued) - return 1; - if (s->initialized < 1) - return -1; - while (1) { - struct af_instance *last = NULL; - for (struct af_instance * cur = s->first; cur; cur = cur->next) { - // Flush remaining frames on EOF, but do that only if the previous - // filters have been flushed (i.e. they have no more output). - if (eof && !last) { - read_remaining(cur); - int r = af_do_filter(cur, NULL); - if (r < 0) - return r; - } - if (af_has_output_frame(cur)) - last = cur; - } - if (!last) - return 0; - if (!last->next) - return 1; - int r = af_do_filter(last->next, af_dequeue_output_frame(last)); - if (r < 0) - return r; - } -} - -struct mp_audio *af_read_output_frame(struct af_stream *s) -{ - if (!s->last->num_out_queued) - af_output_frame(s, false); - return af_dequeue_output_frame(s->last); -} - -void af_unread_output_frame(struct af_stream *s, struct mp_audio *frame) -{ - struct af_instance *af = s->last; - MP_TARRAY_INSERT_AT(af, af->out_queued, af->num_out_queued, 0, frame); -} - -// Make sure the caller can change data referenced by the frame. -// Return negative error code on failure (i.e. you can't write). -int af_make_writeable(struct af_instance *af, struct mp_audio *frame) -{ - return mp_audio_pool_make_writeable(af->out_pool, frame); -} - -void af_seek_reset(struct af_stream *s) -{ - af_control_all(s, AF_CONTROL_RESET, NULL); - af_chain_forget_frames(s); -} diff --git a/audio/filter/af.h b/audio/filter/af.h deleted file mode 100644 index 3a07a5465f..0000000000 --- a/audio/filter/af.h +++ /dev/null @@ -1,163 +0,0 @@ -/* - * This file is part of mpv. - * - * mpv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * mpv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with mpv. If not, see . - */ - -#ifndef MPLAYER_AF_H -#define MPLAYER_AF_H - -#include -#include -#include - -#include "config.h" -#if !(HAVE_LIBAF && HAVE_GPL) -#error "libaf/GPL disabled" -#endif - -#include "options/options.h" -#include "audio/format.h" -#include "audio/chmap.h" -#include "audio/audio.h" -#include "common/msg.h" -#include "common/common.h" - -struct af_instance; -struct mpv_global; - -// Number of channels -#define AF_NCH MP_NUM_CHANNELS - -// Flags for af->filter() -#define AF_FILTER_FLAG_EOF 1 - -/* Audio filter information not specific for current instance, but for - a specific filter */ -struct af_info { - const char *info; - const char *name; - int (*open)(struct af_instance *vf); - int priv_size; - const void *priv_defaults; - const struct m_option *options; - // For m_obj_desc.set_defaults - void (*set_defaults)(struct mpv_global *global, void *p); -}; - -// Linked list of audio filters -struct af_instance { - const struct af_info *info; - char *full_name; - struct mp_log *log; - struct MPOpts *opts; - struct mpv_global *global; - int (*control)(struct af_instance *af, int cmd, void *arg); - void (*uninit)(struct af_instance *af); - /* Feed a frame. The frame is NULL if EOF was reached, and the filter - * should drain all remaining buffered data. - * Use af_add_output_frame() to output data. The optional filter_out - * callback can be set to produce output frames gradually. - */ - int (*filter_frame)(struct af_instance *af, struct mp_audio *frame); - int (*filter_out)(struct af_instance *af); - void *priv; - struct mp_audio *data; // configuration and buffer for outgoing data stream - - struct af_instance *next; - struct af_instance *prev; - double delay; /* Delay caused by the filter, in seconds of audio consumed - * without corresponding output */ - bool auto_inserted; // inserted by af.c, such as conversion filters - char *label; - - struct mp_audio fmt_in, fmt_out; - - struct mp_audio **out_queued; - int num_out_queued; - - struct mp_audio_pool *out_pool; -}; - -// Current audio stream -struct af_stream { - int initialized; // 0: no, 1: yes, -1: attempted to, but failed - - // The first and last filter in the list - struct af_instance *first; - struct af_instance *last; - // 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. - struct mp_audio input; - struct mp_audio output; - struct mp_audio filter_output; - - struct mp_log *log; - struct MPOpts *opts; - struct mpv_global *global; -}; - -// Return values -#define AF_DETACH (CONTROL_OK + 1) -#define AF_OK CONTROL_OK -#define AF_TRUE CONTROL_TRUE -#define AF_FALSE CONTROL_FALSE -#define AF_UNKNOWN CONTROL_UNKNOWN -#define AF_ERROR CONTROL_ERROR - -// Parameters for af_control_* -enum af_control { - AF_CONTROL_REINIT = 1, - AF_CONTROL_RESET, - AF_CONTROL_SET_PLAYBACK_SPEED, - AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE, - AF_CONTROL_GET_METADATA, - AF_CONTROL_COMMAND, -}; - -// Argument for AF_CONTROL_SET_PAN_LEVEL -typedef struct af_control_ext_s { - void* arg; // Argument - int ch; // Chanel number -} af_control_ext_t; - -struct af_stream *af_new(struct mpv_global *global); -void af_destroy(struct af_stream *s); -int af_init(struct af_stream *s); -void af_uninit(struct af_stream *s); -struct af_instance *af_add(struct af_stream *s, char *name, char *label, - char **args); -int af_remove_by_label(struct af_stream *s, char *label); -struct af_instance *af_find_by_label(struct af_stream *s, char *label); -struct af_instance *af_control_any_rev(struct af_stream *s, int cmd, void *arg); -void af_control_all(struct af_stream *s, int cmd, void *arg); -int af_control_by_label(struct af_stream *s, int cmd, void *arg, bstr label); -void af_seek_reset(struct af_stream *s); -int af_send_command(struct af_stream *s, char *label, char *cmd, char *arg); - -void af_add_output_frame(struct af_instance *af, struct mp_audio *frame); -int af_filter_frame(struct af_stream *s, struct mp_audio *frame); -int af_output_frame(struct af_stream *s, bool eof); -struct mp_audio *af_read_output_frame(struct af_stream *s); -void af_unread_output_frame(struct af_stream *s, struct mp_audio *frame); -int af_make_writeable(struct af_instance *af, struct mp_audio *frame); - -double af_calc_delay(struct af_stream *s); - -int af_test_output(struct af_instance *af, struct mp_audio *out); - -int af_from_ms(int n, float *in, int *out, int rate, float mi, float ma); -float af_softclip(float a); - -#endif /* MPLAYER_AF_H */ diff --git a/audio/filter/af_format.c b/audio/filter/af_format.c index c4af9b768b..3e1eef664c 100644 --- a/audio/filter/af_format.c +++ b/audio/filter/af_format.c @@ -15,18 +15,14 @@ * License along with mpv. If not, see . */ -#include - -#include - -#include "options/m_option.h" - +#include "audio/aframe.h" #include "audio/format.h" -#include "af.h" - -struct priv { - struct m_config *config; +#include "filters/f_autoconvert.h" +#include "filters/filter_internal.h" +#include "filters/user_filters.h" +#include "options/m_option.h" +struct f_opts { int in_format; int in_srate; struct m_channels in_channels; @@ -37,98 +33,109 @@ struct priv { int fail; }; -static void force_in_params(struct af_instance *af, struct mp_audio *in) -{ - struct priv *priv = af->priv; - - if (priv->in_format != AF_FORMAT_UNKNOWN) - mp_audio_set_format(in, priv->in_format); - - if (priv->in_channels.num_chmaps > 0) - mp_audio_set_channels(in, &priv->in_channels.chmaps[0]); - - if (priv->in_srate) - in->rate = priv->in_srate; -} +struct priv { + struct f_opts *opts; + struct mp_pin *in_pin; +}; -static void force_out_params(struct af_instance *af, struct mp_audio *out) +static void process(struct mp_filter *f) { - struct priv *priv = af->priv; + struct priv *p = f->priv; - if (priv->out_format != AF_FORMAT_UNKNOWN) - mp_audio_set_format(out, priv->out_format); + if (!mp_pin_can_transfer_data(f->ppins[1], p->in_pin)) + return; - if (priv->out_channels.num_chmaps > 0) - mp_audio_set_channels(out, &priv->out_channels.chmaps[0]); + struct mp_frame frame = mp_pin_out_read(p->in_pin); - if (priv->out_srate) - out->rate = priv->out_srate; -} + if (p->opts->fail) { + MP_ERR(f, "Failing on purpose.\n"); + goto error; + } -static int control(struct af_instance *af, int cmd, void *arg) -{ - struct priv *priv = af->priv; + if (frame.type == MP_FRAME_EOF) { + mp_pin_in_write(f->ppins[1], frame); + return; + } - switch (cmd) { - case AF_CONTROL_REINIT: { - struct mp_audio *in = arg; - struct mp_audio orig_in = *in; - struct mp_audio *out = af->data; + if (frame.type != MP_FRAME_AUDIO) { + MP_ERR(f, "audio frame expected\n"); + goto error; + } - force_in_params(af, in); - mp_audio_copy_config(out, in); - force_out_params(af, out); + struct mp_aframe *in = frame.data; - if (in->nch != out->nch || in->bps != out->bps) { - MP_ERR(af, "Forced input/output formats are incompatible.\n"); - return AF_ERROR; + if (p->opts->out_channels.num_chmaps > 0) { + if (!mp_aframe_set_chmap(in, &p->opts->out_channels.chmaps[0])) { + MP_ERR(f, "could not force output channels\n"); + goto error; } + } - if (priv->fail) { - MP_ERR(af, "Failing on purpose.\n"); - return AF_ERROR; - } + if (p->opts->out_srate) + mp_aframe_set_rate(in, p->opts->out_srate); - return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE; - } - } - return AF_UNKNOWN; -} + mp_pin_in_write(f->ppins[1], frame); + return; -static int filter(struct af_instance *af, struct mp_audio *data) -{ - if (data) - mp_audio_copy_config(data, af->data); - af_add_output_frame(af, data); - return 0; +error: + mp_frame_unref(&frame); + mp_filter_internal_mark_failed(f); } -static int af_open(struct af_instance *af) +static const struct mp_filter_info af_format_filter = { + .name = "format", + .priv_size = sizeof(struct priv), + .process = process, +}; + +static struct mp_filter *af_format_create(struct mp_filter *parent, + void *options) { - af->control = control; - af->filter_frame = filter; + struct mp_filter *f = mp_filter_create(parent, &af_format_filter); + if (!f) { + talloc_free(options); + return NULL; + } - force_in_params(af, af->data); - force_out_params(af, af->data); + struct priv *p = f->priv; + p->opts = talloc_steal(p, options); - return AF_OK; -} + mp_filter_add_pin(f, MP_PIN_IN, "in"); + mp_filter_add_pin(f, MP_PIN_OUT, "out"); -#define OPT_BASE_STRUCT struct priv + struct mp_autoconvert *conv = mp_autoconvert_create(f); + if (!conv) + abort(); -const struct af_info af_info_format = { - .info = "Force audio format", - .name = "format", - .open = af_open, - .priv_size = sizeof(struct priv), - .options = (const struct m_option[]) { - OPT_AUDIOFORMAT("format", in_format, 0), - OPT_INTRANGE("srate", in_srate, 0, 1000, 8*48000), - OPT_CHANNELS("channels", in_channels, 0, .min = 1), - OPT_AUDIOFORMAT("out-format", out_format, 0), - OPT_INTRANGE("out-srate", out_srate, 0, 1000, 8*48000), - OPT_CHANNELS("out-channels", out_channels, 0, .min = 1), - OPT_FLAG("fail", fail, 0), - {0} + if (p->opts->in_format) + mp_autoconvert_add_afmt(conv, p->opts->in_format); + if (p->opts->in_srate) + mp_autoconvert_add_srate(conv, p->opts->in_srate); + if (p->opts->in_channels.num_chmaps > 0) + mp_autoconvert_add_chmap(conv, &p->opts->in_channels.chmaps[0]); + + mp_pin_connect(conv->f->pins[0], f->ppins[0]); + p->in_pin = conv->f->pins[1]; + + return f; +} + +#define OPT_BASE_STRUCT struct f_opts + +const struct mp_user_filter_entry af_format = { + .desc = { + .name = "format", + .description = "Force audio format", + .priv_size = sizeof(struct f_opts), + .options = (const struct m_option[]) { + OPT_AUDIOFORMAT("format", in_format, 0), + OPT_INTRANGE("srate", in_srate, 0, 1000, 8*48000), + OPT_CHANNELS("channels", in_channels, 0, .min = 1), + OPT_INTRANGE("out-srate", out_srate, 0, 1000, 8*48000), + OPT_CHANNELS("out-channels", out_channels, 0, .min = 1), + OPT_FLAG("fail", fail, 0), + {0} + }, }, + .create = af_format_create, }; diff --git a/audio/filter/af_lavcac3enc.c b/audio/filter/af_lavcac3enc.c index 14aa53b980..c7582cf52b 100644 --- a/audio/filter/af_lavcac3enc.c +++ b/audio/filter/af_lavcac3enc.c @@ -31,14 +31,17 @@ #include #include -#include "config.h" - -#include "common/av_common.h" -#include "common/common.h" -#include "af.h" -#include "audio/audio_buffer.h" +#include "audio/aframe.h" #include "audio/chmap_sel.h" #include "audio/fmt-conversion.h" +#include "audio/format.h" +#include "common/av_common.h" +#include "common/common.h" +#include "filters/f_autoconvert.h" +#include "filters/f_utils.h" +#include "filters/filter_internal.h" +#include "filters/user_filters.h" +#include "options/m_option.h" #define AC3_MAX_CHANNELS 6 @@ -49,173 +52,89 @@ const uint16_t ac3_bitrate_tab[19] = { 160, 192, 224, 256, 320, 384, 448, 512, 576, 640 }; -// Data for specific instances of this filter -typedef struct af_ac3enc_s { +struct f_opts { + int add_iec61937_header; + int bit_rate; + int min_channel_num; + char *encoder; + char **avopts; +}; + +struct priv { + struct f_opts *opts; + + struct mp_pin *in_pin; + struct mp_aframe *cur_format; + struct mp_aframe *in_frame; + struct mp_aframe_pool *out_pool; + struct AVCodec *lavc_acodec; struct AVCodecContext *lavc_actx; int bit_rate; - struct mp_audio *input; // frame passed to libavcodec - struct mp_audio *pending; // unconsumed input data - int in_samples; // samples of input per AC3 frame int out_samples; // upper bound on encoded output per AC3 frame - int64_t encoder_buffered; - - int cfg_add_iec61937_header; - int cfg_bit_rate; - int cfg_min_channel_num; - char *cfg_encoder; - char **cfg_avopts; -} af_ac3enc_t; - -// fmt carries the input format. Change it to the best next-possible format -// the encoder likely accepts. -static void select_encode_format(AVCodecContext *c, struct mp_audio *fmt) -{ - int formats[AF_FORMAT_COUNT]; - af_get_best_sample_formats(fmt->format, formats); - - for (int n = 0; formats[n]; n++) { - const enum AVSampleFormat *lf = c->codec->sample_fmts; - for (int i = 0; lf && lf[i] != AV_SAMPLE_FMT_NONE; i++) { - int mpfmt = af_from_avformat(lf[i]); - if (mpfmt && mpfmt == formats[n]) { - mp_audio_set_format(fmt, mpfmt); - goto done_fmt; - } - } - } -done_fmt: ; +}; - int rate = - af_select_best_samplerate(fmt->rate, c->codec->supported_samplerates); - if (rate > 0) - fmt->rate = rate; +static bool reinit(struct mp_filter *f) +{ + struct priv *s = f->priv; - struct mp_chmap_sel sel = {0}; - const uint64_t *lch = c->codec->channel_layouts; - for (int n = 0; lch && lch[n]; n++) { - struct mp_chmap chmap = {0}; - mp_chmap_from_lavc(&chmap, lch[n]); - mp_chmap_sel_add_map(&sel, &chmap); - } - struct mp_chmap res = fmt->channels; - mp_chmap_sel_adjust(&sel, &res); - if (!mp_chmap_is_empty(&res)) - mp_audio_set_channels(fmt, &res); -} + mp_aframe_reset(s->cur_format); -// Initialization and runtime control -static int control(struct af_instance *af, int cmd, void *arg) -{ - af_ac3enc_t *s = af->priv; static const int default_bit_rate[AC3_MAX_CHANNELS+1] = \ {0, 96000, 192000, 256000, 384000, 448000, 448000}; - switch (cmd){ - case AF_CONTROL_REINIT: { - struct mp_audio *in = arg; - struct mp_audio orig_in = *in; - - if (!af_fmt_is_pcm(in->format) || in->nch < s->cfg_min_channel_num) - return AF_DETACH; - - // At least currently, the AC3 encoder doesn't export sample rates. - in->rate = 48000; - select_encode_format(s->lavc_actx, in); - - af->data->rate = in->rate; - mp_audio_set_format(af->data, AF_FORMAT_S_AC3); - mp_audio_set_num_channels(af->data, 2); - - if (!mp_audio_config_equals(in, &orig_in)) - return AF_FALSE; - - if (s->cfg_add_iec61937_header) { - s->out_samples = AC3_FRAME_SIZE; - } else { - s->out_samples = AC3_MAX_CODED_FRAME_SIZE / af->data->sstride; - } - - mp_audio_copy_config(s->input, in); - - talloc_free(s->pending); - s->pending = NULL; - - MP_DBG(af, "reinit: %d, %d, %d.\n", in->nch, in->rate, s->in_samples); + if (s->opts->add_iec61937_header) { + s->out_samples = AC3_FRAME_SIZE; + } else { + s->out_samples = AC3_MAX_CODED_FRAME_SIZE / + mp_aframe_get_sstride(s->in_frame); + } - int bit_rate = s->bit_rate ? s->bit_rate : default_bit_rate[in->nch]; + int format = mp_aframe_get_format(s->in_frame); + int rate = mp_aframe_get_rate(s->in_frame); + struct mp_chmap chmap = {0}; + mp_aframe_get_chmap(s->in_frame, &chmap); - if (s->lavc_actx->channels != in->nch || - s->lavc_actx->sample_rate != in->rate || - s->lavc_actx->bit_rate != bit_rate) - { - avcodec_close(s->lavc_actx); + int bit_rate = s->bit_rate; + if (!bit_rate && chmap.num < AC3_MAX_CHANNELS + 1) + bit_rate = default_bit_rate[chmap.num]; - // Put sample parameters - s->lavc_actx->sample_fmt = af_to_avformat(in->format); - s->lavc_actx->channels = in->nch; - s->lavc_actx->channel_layout = mp_chmap_to_lavc(&in->channels); - s->lavc_actx->sample_rate = in->rate; - s->lavc_actx->bit_rate = bit_rate; + avcodec_close(s->lavc_actx); - if (avcodec_open2(s->lavc_actx, s->lavc_acodec, NULL) < 0) { - MP_ERR(af, "Couldn't open codec %s, br=%d.\n", "ac3", bit_rate); - return AF_ERROR; - } + // Put sample parameters + s->lavc_actx->sample_fmt = af_to_avformat(format); + s->lavc_actx->channels = chmap.num; + s->lavc_actx->channel_layout = mp_chmap_to_lavc(&chmap); + s->lavc_actx->sample_rate = rate; + s->lavc_actx->bit_rate = bit_rate; - if (s->lavc_actx->frame_size < 1) { - MP_ERR(af, "encoder didn't specify input frame size\n"); - return AF_ERROR; - } - } - s->in_samples = s->lavc_actx->frame_size; - mp_audio_realloc(s->input, s->in_samples); - s->input->samples = 0; - s->encoder_buffered = 0; - return AF_OK; + if (avcodec_open2(s->lavc_actx, s->lavc_acodec, NULL) < 0) { + MP_ERR(f, "Couldn't open codec %s, br=%d.\n", "ac3", bit_rate); + return false; } - case AF_CONTROL_RESET: - if (avcodec_is_open(s->lavc_actx)) - avcodec_flush_buffers(s->lavc_actx); - talloc_free(s->pending); - s->pending = NULL; - s->input->samples = 0; - s->encoder_buffered = 0; - return AF_OK; - } - return AF_UNKNOWN; -} - -// Deallocate memory -static void uninit(struct af_instance* af) -{ - af_ac3enc_t *s = af->priv; - if (s) { - avcodec_free_context(&s->lavc_actx); - talloc_free(s->pending); + if (s->lavc_actx->frame_size < 1) { + MP_ERR(f, "encoder didn't specify input frame size\n"); + return false; } + + mp_aframe_config_copy(s->cur_format, s->in_frame); + return true; } -static void update_delay(struct af_instance *af) +static void reset(struct mp_filter *f) { - af_ac3enc_t *s = af->priv; - af->delay = ((s->pending ? s->pending->samples : 0) + s->input->samples + - s->encoder_buffered) / (double)s->input->rate; + struct priv *s = f->priv; + + TA_FREEP(&s->in_frame); } -static int filter_frame(struct af_instance *af, struct mp_audio *audio) +static void destroy(struct mp_filter *f) { - af_ac3enc_t *s = af->priv; + struct priv *s = f->priv; - // filter_output must have been called until no output was produced. - if (s->pending && s->pending->samples) - MP_ERR(af, "broken data flow\n"); - - talloc_free(s->pending); - s->pending = audio; - update_delay(af); - return 0; + reset(f); + avcodec_free_context(&s->lavc_actx); } static void swap_16(uint16_t *ptr, size_t size) @@ -224,105 +143,84 @@ static void swap_16(uint16_t *ptr, size_t size) ptr[n] = av_bswap16(ptr[n]); } -// Copy data from input frame to encode frame (because libavcodec wants a full -// AC3 frame for encoding, while filter input frames can be smaller or larger). -// Return true if the frame is complete. -static bool fill_buffer(struct af_instance *af) -{ - af_ac3enc_t *s = af->priv; - - af->delay = 0; - - if (s->pending) { - if (!mp_audio_is_writeable(s->input)) - assert(s->input->samples == 0); // we can't have sent a partial frame - mp_audio_realloc_min(s->input, s->in_samples); - int copy = MPMIN(s->in_samples - s->input->samples, s->pending->samples); - s->input->samples += copy; - mp_audio_copy(s->input, s->input->samples - copy, s->pending, 0, copy); - mp_audio_skip_samples(s->pending, copy); - } - update_delay(af); - return s->input->samples >= s->in_samples; -} - -// Return <0 on error, 0 on need more input, 1 on success (and *frame set). -// To actually advance the read pointer, set s->input->samples=0 afterwards. -static int read_input_frame(struct af_instance *af, AVFrame *frame) +static void process(struct mp_filter *f) { - af_ac3enc_t *s = af->priv; - if (!fill_buffer(af)) - return 0; // need more input + struct priv *s = f->priv; - if (mp_audio_to_avframe(s->input, frame) < 0) - return -1; - - return 1; -} - -static int filter_out(struct af_instance *af) -{ - af_ac3enc_t *s = af->priv; - - if (!s->pending) - return 0; - - AVFrame *frame = av_frame_alloc(); - if (!frame) { - MP_FATAL(af, "Could not allocate memory \n"); - return -1; - } - int err = -1; + if (!mp_pin_in_needs_data(f->ppins[1])) + return; + bool err = true; + struct mp_aframe *out = NULL; AVPacket pkt = {0}; av_init_packet(&pkt); // Send input as long as it wants. while (1) { - err = read_input_frame(af, frame); - if (err < 0) + if (avcodec_is_open(s->lavc_actx)) { + int lavc_ret = avcodec_receive_packet(s->lavc_actx, &pkt); + if (lavc_ret >= 0) + break; + if (lavc_ret < 0 && lavc_ret != AVERROR(EAGAIN)) { + MP_FATAL(f, "Encode failed (receive).\n"); + goto done; + } + } + AVFrame *frame = NULL; + struct mp_frame input = mp_pin_out_read(s->in_pin); + // The following code assumes no sample data buffering in the encoder. + if (input.type == MP_FRAME_EOF) { + mp_pin_in_write(f->ppins[1], input); + return; + } else if (input.type == MP_FRAME_AUDIO) { + TA_FREEP(&s->in_frame); + s->in_frame = input.data; + frame = mp_frame_to_av(input, NULL); + if (!frame) + goto done; + if (mp_aframe_get_channels(s->in_frame) < s->opts->min_channel_num) { + // Just pass it through. + s->in_frame = NULL; + mp_pin_in_write(f->ppins[1], input); + return; + } + if (!mp_aframe_config_equals(s->in_frame, s->cur_format)) { + if (!reinit(f)) + goto done; + } + } else if (input.type) { goto done; - if (err == 0) - break; - err = -1; + } else { + return; // no data yet + } int lavc_ret = avcodec_send_frame(s->lavc_actx, frame); - // On EAGAIN, we're supposed to read remaining output. - if (lavc_ret == AVERROR(EAGAIN)) - break; - if (lavc_ret < 0) { - MP_FATAL(af, "Encode failed.\n"); + av_frame_free(&frame); + if (lavc_ret < 0 && lavc_ret != AVERROR(EAGAIN)) { + MP_FATAL(f, "Encode failed (send).\n"); goto done; } - s->encoder_buffered += s->input->samples; - s->input->samples = 0; - } - int lavc_ret = avcodec_receive_packet(s->lavc_actx, &pkt); - if (lavc_ret == AVERROR(EAGAIN)) { - // Need to buffer more input. - err = 0; - goto done; - } - if (lavc_ret < 0) { - MP_FATAL(af, "Encode failed.\n"); - goto done; } - MP_DBG(af, "avcodec_encode_audio got %d, pending %d.\n", - pkt.size, s->pending->samples + s->input->samples); + if (!s->in_frame) + goto done; - s->encoder_buffered -= AC3_FRAME_SIZE; + out = mp_aframe_create(); + mp_aframe_set_format(out, AF_FORMAT_S_AC3); + mp_aframe_set_chmap(out, &(struct mp_chmap)MP_CHMAP_INIT_STEREO); + mp_aframe_set_rate(out, 48000); - struct mp_audio *out = - mp_audio_pool_get(af->out_pool, af->data, s->out_samples); - if (!out) + if (mp_aframe_pool_allocate(s->out_pool, out, s->out_samples) < 0) goto done; - mp_audio_copy_attributes(out, s->pending); + + int sstride = mp_aframe_get_sstride(out); + + mp_aframe_copy_attributes(out, s->in_frame); int frame_size = pkt.size; int header_len = 0; char hdr[8]; - if (s->cfg_add_iec61937_header && pkt.size > 5) { + if (s->opts->add_iec61937_header && pkt.size > 5) { int bsmod = pkt.data[5] & 0x7; int len = frame_size; @@ -336,48 +234,69 @@ static int filter_out(struct af_instance *af) AV_WL16(hdr + 6, len << 3); // number of bits in payload } - if (frame_size > out->samples * out->sstride) + if (frame_size > s->out_samples * sstride) abort(); - char *buf = (char *)out->planes[0]; + uint8_t **planes = mp_aframe_get_data_rw(out); + if (!planes) + goto done; + char *buf = planes[0]; memcpy(buf, hdr, header_len); memcpy(buf + header_len, pkt.data, pkt.size); memset(buf + header_len + pkt.size, 0, frame_size - (header_len + pkt.size)); swap_16((uint16_t *)(buf + header_len), pkt.size / 2); - out->samples = frame_size / out->sstride; - af_add_output_frame(af, out); + mp_aframe_set_size(out, frame_size / sstride); + mp_pin_in_write(f->ppins[1], MAKE_FRAME(MP_FRAME_AUDIO, out)); + out = NULL; err = 0; done: av_packet_unref(&pkt); - av_frame_free(&frame); - update_delay(af); - return err; + talloc_free(out); + if (err) + mp_filter_internal_mark_failed(f); } -static int af_open(struct af_instance* af){ +static const struct mp_filter_info af_lavcac3enc_filter = { + .name = "lavcac3enc", + .priv_size = sizeof(struct priv), + .process = process, + .reset = reset, + .destroy = destroy, +}; - af_ac3enc_t *s = af->priv; - af->control=control; - af->uninit=uninit; - af->filter_frame = filter_frame; - af->filter_out = filter_out; +static struct mp_filter *af_lavcac3enc_create(struct mp_filter *parent, + void *options) +{ + struct mp_filter *f = mp_filter_create(parent, &af_lavcac3enc_filter); + if (!f) { + talloc_free(options); + return NULL; + } + + mp_filter_add_pin(f, MP_PIN_IN, "in"); + mp_filter_add_pin(f, MP_PIN_OUT, "out"); - s->lavc_acodec = avcodec_find_encoder_by_name(s->cfg_encoder); + struct priv *s = f->priv; + s->opts = talloc_steal(s, options); + s->cur_format = talloc_steal(s, mp_aframe_create()); + s->out_pool = mp_aframe_pool_create(s); + + s->lavc_acodec = avcodec_find_encoder_by_name(s->opts->encoder); if (!s->lavc_acodec) { - MP_ERR(af, "Couldn't find encoder %s.\n", s->cfg_encoder); - return AF_ERROR; + MP_ERR(f, "Couldn't find encoder %s.\n", s->opts->encoder); + goto error; } s->lavc_actx = avcodec_alloc_context3(s->lavc_acodec); if (!s->lavc_actx) { - MP_ERR(af, "Audio LAVC, couldn't allocate context!\n"); - return AF_ERROR; + MP_ERR(f, "Audio LAVC, couldn't allocate context!\n"); + goto error; } - if (mp_set_avopts(af->log, s->lavc_actx, s->cfg_avopts) < 0) - return AF_ERROR; + if (mp_set_avopts(f->log, s->lavc_actx, s->opts->avopts) < 0) + goto error; // For this one, we require the decoder to expert lists of all supported // parameters. (Not all decoders do that, but the ones we're interested @@ -385,50 +304,85 @@ static int af_open(struct af_instance* af){ if (!s->lavc_acodec->sample_fmts || !s->lavc_acodec->channel_layouts) { - MP_ERR(af, "Audio encoder doesn't list supported parameters.\n"); - return AF_ERROR; + MP_ERR(f, "Audio encoder doesn't list supported parameters.\n"); + goto error; } - s->input = talloc_zero(s, struct mp_audio); - - if (s->cfg_bit_rate) { + if (s->opts->bit_rate) { int i; for (i = 0; i < 19; i++) { - if (ac3_bitrate_tab[i] == s->cfg_bit_rate) { + if (ac3_bitrate_tab[i] == s->opts->bit_rate) { s->bit_rate = ac3_bitrate_tab[i] * 1000; break; } } if (i >= 19) { - MP_WARN(af, "unable set unsupported bitrate %d, using default " + MP_WARN(f, "unable set unsupported bitrate %d, using default " "bitrate (check manpage to see supported bitrates).\n", - s->cfg_bit_rate); + s->opts->bit_rate); } } - return AF_OK; -} + struct mp_autoconvert *conv = mp_autoconvert_create(f); + if (!conv) + abort(); -#define OPT_BASE_STRUCT struct af_ac3enc_s + const enum AVSampleFormat *lf = s->lavc_acodec->sample_fmts; + for (int i = 0; lf && lf[i] != AV_SAMPLE_FMT_NONE; i++) { + int mpfmt = af_from_avformat(lf[i]); + if (mpfmt) + mp_autoconvert_add_afmt(conv, mpfmt); + } -const struct af_info af_info_lavcac3enc = { - .info = "runtime encode to ac3 using libavcodec", - .name = "lavcac3enc", - .open = af_open, - .priv_size = sizeof(struct af_ac3enc_s), - .priv_defaults = &(const struct af_ac3enc_s){ - .cfg_add_iec61937_header = 1, - .cfg_bit_rate = 640, - .cfg_min_channel_num = 3, - .cfg_encoder = "ac3", - }, - .options = (const struct m_option[]) { - OPT_FLAG("tospdif", cfg_add_iec61937_header, 0), - OPT_CHOICE_OR_INT("bitrate", cfg_bit_rate, 0, 32, 640, - ({"auto", 0}, {"default", 0})), - OPT_INTRANGE("minch", cfg_min_channel_num, 0, 2, 6), - OPT_STRING("encoder", cfg_encoder, 0), - OPT_KEYVALUELIST("o", cfg_avopts, 0), - {0} + const uint64_t *lch = s->lavc_acodec->channel_layouts; + for (int n = 0; lch && lch[n]; n++) { + struct mp_chmap chmap = {0}; + mp_chmap_from_lavc(&chmap, lch[n]); + if (mp_chmap_is_valid(&chmap)) + mp_autoconvert_add_chmap(conv, &chmap); + } + + // At least currently, the AC3 encoder doesn't export sample rates. + mp_autoconvert_add_srate(conv, 48000); + + mp_pin_connect(conv->f->pins[0], f->ppins[0]); + + struct mp_filter *fs = mp_fixed_aframe_size_create(f, AC3_FRAME_SIZE, true); + if (!fs) + abort(); + + mp_pin_connect(fs->pins[0], conv->f->pins[1]); + s->in_pin = fs->pins[1]; + + return f; + +error: + talloc_free(f); + return NULL; +} + +#define OPT_BASE_STRUCT struct f_opts + +const struct mp_user_filter_entry af_lavcac3enc = { + .desc = { + .description = "runtime encode to ac3 using libavcodec", + .name = "lavcac3enc", + .priv_size = sizeof(OPT_BASE_STRUCT), + .priv_defaults = &(const OPT_BASE_STRUCT) { + .add_iec61937_header = 1, + .bit_rate = 640, + .min_channel_num = 3, + .encoder = "ac3", + }, + .options = (const struct m_option[]) { + OPT_FLAG("tospdif", add_iec61937_header, 0), + OPT_CHOICE_OR_INT("bitrate", bit_rate, 0, 32, 640, + ({"auto", 0}, {"default", 0})), + OPT_INTRANGE("minch", min_channel_num, 0, 2, 6), + OPT_STRING("encoder", encoder, 0), + OPT_KEYVALUELIST("o", avopts, 0), + {0} + }, }, + .create = af_lavcac3enc_create, }; diff --git a/audio/filter/af_lavfi.c b/audio/filter/af_lavfi.c deleted file mode 100644 index ab8a026de7..0000000000 --- a/audio/filter/af_lavfi.c +++ /dev/null @@ -1,413 +0,0 @@ -/* - * This file is part of mpv. - * - * Filter graph creation code taken from FFmpeg ffplay.c (LGPL 2.1 or later) - * - * mpv is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * mpv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with mpv. If not, see . - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config.h" - -#include "audio/format.h" -#include "audio/fmt-conversion.h" -#include "af.h" - -#include "common/av_common.h" -#include "common/tags.h" - -#include "options/m_option.h" - -// FFmpeg and Libav have slightly different APIs, just enough to cause us -// unnecessary pain. -#if LIBAVFILTER_VERSION_MICRO < 100 -#define graph_parse(graph, filters, inputs, outputs, log_ctx) \ - avfilter_graph_parse(graph, filters, inputs, outputs, log_ctx) -#define avfilter_graph_send_command(a, b, c, d, e, f, g) -1 -#else -#define graph_parse(graph, filters, inputs, outputs, log_ctx) \ - avfilter_graph_parse_ptr(graph, filters, &(inputs), &(outputs), log_ctx) -#endif - -struct priv { - // Single filter bridge, instead of a graph. - bool is_bridge; - - AVFilterGraph *graph; - AVFilterContext *in; - AVFilterContext *out; - - int64_t samples_in; - - AVRational timebase_out; - - bool eof; - - struct mp_tags *metadata; - - // options - char *cfg_graph; - char **cfg_avopts; - char *cfg_filter_name; - char **cfg_filter_opts; -}; - -static void destroy_graph(struct af_instance *af) -{ - struct priv *p = af->priv; - avfilter_graph_free(&p->graph); - p->in = p->out = NULL; - p->samples_in = 0; - p->eof = false; -} - -static bool recreate_graph(struct af_instance *af, struct mp_audio *config) -{ - void *tmp = talloc_new(NULL); - struct priv *p = af->priv; - AVFilterContext *in = NULL, *out = NULL; - bool ok = false; - - if (!p->is_bridge && bstr0(p->cfg_graph).len == 0) { - MP_FATAL(af, "lavfi: no filter graph set\n"); - return false; - } - - destroy_graph(af); - - AVFilterGraph *graph = avfilter_graph_alloc(); - if (!graph) - goto error; - - if (mp_set_avopts(af->log, graph, p->cfg_avopts) < 0) - goto error; - - AVFilterInOut *outputs = avfilter_inout_alloc(); - AVFilterInOut *inputs = avfilter_inout_alloc(); - if (!outputs || !inputs) - goto error; - - char *src_args = talloc_asprintf(tmp, - "sample_rate=%d:sample_fmt=%s:time_base=%d/%d:" - "channel_layout=0x%"PRIx64, config->rate, - av_get_sample_fmt_name(af_to_avformat(config->format)), - 1, config->rate, mp_chmap_to_lavc(&config->channels)); - - if (avfilter_graph_create_filter(&in, avfilter_get_by_name("abuffer"), - "src", src_args, NULL, graph) < 0) - goto error; - - if (avfilter_graph_create_filter(&out, avfilter_get_by_name("abuffersink"), - "out", NULL, NULL, graph) < 0) - goto error; - - if (p->is_bridge) { - AVFilterContext *filter = avfilter_graph_alloc_filter(graph, - avfilter_get_by_name(p->cfg_filter_name), "filter"); - if (!filter) - goto error; - - if (mp_set_avopts(af->log, filter->priv, p->cfg_filter_opts) < 0) - goto error; - - if (avfilter_init_str(filter, NULL) < 0) - goto error; - - // Yep, we have to manually link those filters. - if (filter->nb_inputs != 1 || - avfilter_pad_get_type(filter->input_pads, 0) != AVMEDIA_TYPE_AUDIO || - filter->nb_outputs != 1 || - avfilter_pad_get_type(filter->output_pads, 0) != AVMEDIA_TYPE_AUDIO) - { - MP_ERR(af, "The filter is required to have 1 audio input pad and " - "1 audio output pad.\n"); - goto error; - } - if (avfilter_link(in, 0, filter, 0) < 0 || - avfilter_link(filter, 0, out, 0) < 0) - { - MP_ERR(af, "Failed to link filter.\n"); - goto error; - } - } else { - MP_VERBOSE(af, "lavfi: create graph: '%s'\n", p->cfg_graph); - - outputs->name = av_strdup("in"); - outputs->filter_ctx = in; - - inputs->name = av_strdup("out"); - inputs->filter_ctx = out; - - if (graph_parse(graph, p->cfg_graph, inputs, outputs, NULL) < 0) - goto error; - } - - if (avfilter_graph_config(graph, NULL) < 0) - goto error; - - p->in = in; - p->out = out; - p->graph = graph; - - assert(out->nb_inputs == 1); - assert(in->nb_outputs == 1); - - ok = true; -error: - - if (!ok) { - MP_FATAL(af, "Can't configure libavfilter graph.\n"); - avfilter_graph_free(&graph); - } - avfilter_inout_free(&inputs); - avfilter_inout_free(&outputs); - talloc_free(tmp); - return ok; -} - -static void reset(struct af_instance *af) -{ - if (!recreate_graph(af, &af->fmt_in)) - MP_FATAL(af, "Can't recreate libavfilter filter after a seek reset.\n"); -} - -static int control(struct af_instance *af, int cmd, void *arg) -{ - struct priv *p = af->priv; - - switch (cmd) { - case AF_CONTROL_REINIT: { - struct mp_audio *in = arg; - struct mp_audio orig_in = *in; - struct mp_audio *out = af->data; - - if (af_to_avformat(in->format) == AV_SAMPLE_FMT_NONE) - mp_audio_set_format(in, AF_FORMAT_FLOAT); - - // Removing this requires fixing AVFrame.data vs. AVFrame.extended_data - if (in->channels.num > AV_NUM_DATA_POINTERS) - return AF_ERROR; - - if (!mp_chmap_is_lavc(&in->channels)) - mp_chmap_reorder_to_lavc(&in->channels); // will always work - - if (!recreate_graph(af, in)) - return AF_ERROR; - - AVFilterLink *l_out = p->out->inputs[0]; - - out->rate = l_out->sample_rate; - - mp_audio_set_format(out, af_from_avformat(l_out->format)); - - struct mp_chmap out_cm; - mp_chmap_from_lavc(&out_cm, l_out->channel_layout); - mp_audio_set_channels(out, &out_cm); - - if (!mp_audio_config_valid(out) || out->channels.num > AV_NUM_DATA_POINTERS) - return AF_ERROR; - - p->timebase_out = l_out->time_base; - - return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE; - } - case AF_CONTROL_COMMAND: { - if (!p->graph) - break; - char **args = arg; - return avfilter_graph_send_command(p->graph, "all", - args[0], args[1], &(char){0}, 0, 0) - >= 0 ? CONTROL_OK : CONTROL_ERROR; - } - case AF_CONTROL_GET_METADATA: - if (p->metadata) { - *(struct mp_tags *)arg = *p->metadata; - return CONTROL_OK; - } - return CONTROL_NA; - case AF_CONTROL_RESET: - reset(af); - return AF_OK; - } - return AF_UNKNOWN; -} - -static void get_metadata_from_av_frame(struct af_instance *af, AVFrame *frame) -{ -#if LIBAVUTIL_VERSION_MICRO >= 100 - struct priv *p = af->priv; - if (!p->metadata) - p->metadata = talloc_zero(p, struct mp_tags); - - mp_tags_copy_from_av_dictionary(p->metadata, frame->metadata); -#endif -} - -static int filter_frame(struct af_instance *af, struct mp_audio *data) -{ - struct priv *p = af->priv; - AVFrame *frame = NULL; - - if (p->eof && data) - reset(af); - - if (!p->graph) - goto error; - - if (!data) { - if (p->eof) - return 0; - p->eof = true; - } - - if (data) { - frame = mp_audio_to_avframe_and_unref(data); - data = NULL; - if (!frame) - goto error; - - // Timebase is 1/sample_rate - frame->pts = p->samples_in; - p->samples_in += frame->nb_samples; - } - - if (av_buffersrc_add_frame(p->in, frame) < 0) - goto error; - - av_frame_free(&frame); - talloc_free(data); - return 0; -error: - av_frame_free(&frame); - talloc_free(data); - return -1; -} - -static int filter_out(struct af_instance *af) -{ - struct priv *p = af->priv; - - if (!p->graph) - goto error; - - AVFrame *frame = av_frame_alloc(); - if (!frame) - goto error; - - int err = av_buffersink_get_frame(p->out, frame); - if (err == AVERROR(EAGAIN) || err == AVERROR_EOF) { - // Not an error situation - no more output buffers in queue. - // AVERROR_EOF means we shouldn't even give the filter more - // input, but we don't handle that completely correctly. - av_frame_free(&frame); - p->eof |= err == AVERROR_EOF; - return 0; - } - - struct mp_audio *out = mp_audio_from_avframe(frame); - if (!out) - goto error; - - mp_audio_copy_config(out, af->data); - - if (frame->pts != AV_NOPTS_VALUE) { - double in_time = p->samples_in / (double)af->fmt_in.rate; - double out_time = frame->pts * av_q2d(p->timebase_out); - // Need pts past the last output sample. - out_time += out->samples / (double)out->rate; - - af->delay = in_time - out_time; - } - - get_metadata_from_av_frame(af, frame); - af_add_output_frame(af, out); - av_frame_free(&frame); - return 0; -error: - av_frame_free(&frame); - return -1; -} - -static void uninit(struct af_instance *af) -{ - destroy_graph(af); -} - -static int af_open(struct af_instance *af) -{ - struct priv *p = af->priv; - - af->control = control; - af->uninit = uninit; - af->filter_frame = filter_frame; - af->filter_out = filter_out; - - if (p->is_bridge) { - if (!p->cfg_filter_name) { - MP_ERR(af, "Filter name not set!\n"); - return 0; - } - if (!avfilter_get_by_name(p->cfg_filter_name)) { - MP_ERR(af, "libavfilter filter '%s' not found!\n", p->cfg_filter_name); - return 0; - } - } - return AF_OK; -} - -#define OPT_BASE_STRUCT struct priv - -const struct af_info af_info_lavfi = { - .info = "libavfilter bridge", - .name = "lavfi", - .open = af_open, - .priv_size = sizeof(struct priv), - .options = (const struct m_option[]) { - OPT_STRING("graph", cfg_graph, 0), - OPT_KEYVALUELIST("o", cfg_avopts, 0), - {0} - }, -}; - -const struct af_info af_info_lavfi_bridge = { - .info = "libavfilter bridge (explicit options)", - .name = "lavfi-bridge", - .open = af_open, - .priv_size = sizeof(struct priv), - .priv_defaults = &(const struct priv){ - .is_bridge = true, - }, - .options = (const struct m_option[]) { - OPT_STRING("name", cfg_filter_name, M_OPT_MIN, .min = 1), - OPT_KEYVALUELIST("opts", cfg_filter_opts, 0), - OPT_STRING("graph", cfg_graph, 0), - OPT_KEYVALUELIST("o", cfg_avopts, 0), - {0} - }, -}; diff --git a/audio/filter/af_lavrresample.c b/audio/filter/af_lavrresample.c index f13093da40..baa78acb6e 100644 --- a/audio/filter/af_lavrresample.c +++ b/audio/filter/af_lavrresample.c @@ -32,132 +32,19 @@ #include "common/av_common.h" #include "common/msg.h" +#include "filters/f_swresample.h" +#include "filters/filter_internal.h" +#include "filters/user_filters.h" #include "options/m_config.h" #include "options/m_option.h" -#include "audio/filter/af.h" -#include "audio/fmt-conversion.h" -#include "osdep/endian.h" -#include "audio/aconverter.h" +#include "options/options.h" struct af_resample { int allow_detach; - double playback_speed; struct mp_resample_opts opts; int global_normalize; - struct mp_aconverter *converter; - int deprecation_warning; }; -static int control(struct af_instance *af, int cmd, void *arg) -{ - struct af_resample *s = af->priv; - - switch (cmd) { - case AF_CONTROL_REINIT: { - struct mp_audio *in = arg; - struct mp_audio *out = af->data; - struct mp_audio orig_in = *in; - - if (((out->rate == in->rate) || (out->rate == 0)) && - (out->format == in->format) && - (mp_chmap_equals(&out->channels, &in->channels) || out->nch == 0) && - s->allow_detach && s->playback_speed == 1.0) - return AF_DETACH; - - if (out->rate == 0) - out->rate = in->rate; - - if (mp_chmap_is_empty(&out->channels)) - mp_audio_set_channels(out, &in->channels); - - if (af_to_avformat(in->format) == AV_SAMPLE_FMT_NONE) - mp_audio_set_format(in, AF_FORMAT_FLOAT); - if (af_to_avformat(out->format) == AV_SAMPLE_FMT_NONE) - mp_audio_set_format(out, in->format); - - int r = ((in->format == orig_in.format) && - mp_chmap_equals(&in->channels, &orig_in.channels)) - ? AF_OK : AF_FALSE; - - if (r == AF_OK) { - if (!mp_aconverter_reconfig(s->converter, - in->rate, in->format, in->channels, - out->rate, out->format, out->channels)) - r = AF_ERROR; - } - return r; - } - case AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE: { - s->playback_speed = *(double *)arg; - return AF_OK; - } - case AF_CONTROL_RESET: - mp_aconverter_flush(s->converter); - return AF_OK; - } - return AF_UNKNOWN; -} - -static void uninit(struct af_instance *af) -{ - struct af_resample *s = af->priv; - - talloc_free(s->converter); -} - -static int filter(struct af_instance *af, struct mp_audio *in) -{ - struct af_resample *s = af->priv; - - mp_aconverter_set_speed(s->converter, s->playback_speed); - - af->filter_out(af); - - struct mp_aframe *aframe = mp_audio_to_aframe(in); - if (!aframe && in) - return -1; - talloc_free(in); - bool ok = mp_aconverter_write_input(s->converter, aframe); - if (!ok) - talloc_free(aframe); - - return ok ? 0 : -1; -} - -static int filter_out(struct af_instance *af) -{ - struct af_resample *s = af->priv; - bool eof; - struct mp_aframe *out = mp_aconverter_read_output(s->converter, &eof); - if (out) - af_add_output_frame(af, mp_audio_from_aframe(out)); - talloc_free(out); - af->delay = mp_aconverter_get_latency(s->converter); - return 0; -} - -static int af_open(struct af_instance *af) -{ - struct af_resample *s = af->priv; - - af->control = control; - af->uninit = uninit; - af->filter_frame = filter; - af->filter_out = filter_out; - - if (s->opts.normalize < 0) - s->opts.normalize = s->global_normalize; - - s->converter = mp_aconverter_create(af->global, af->log, &s->opts); - - if (s->deprecation_warning) { - MP_WARN(af, "This filter is deprecated! Use the --audio-resample- options" - " to customize resampling, or the --af=aresample filter.\n"); - } - - return AF_OK; -} - static void set_defaults(struct mpv_global *global, void *p) { struct af_resample *s = p; @@ -165,7 +52,7 @@ static void set_defaults(struct mpv_global *global, void *p) struct mp_resample_opts *opts = &s->opts; struct mp_resample_opts *src_opts = - mp_get_config_group(s, global, &resample_config); + mp_get_config_group(s, global, &resample_conf); s->global_normalize = src_opts->normalize; @@ -180,28 +67,46 @@ static void set_defaults(struct mpv_global *global, void *p) #define OPT_BASE_STRUCT struct af_resample -const struct af_info af_info_lavrresample = { - .info = "Sample frequency conversion using libavresample", - .name = "lavrresample", - .open = af_open, - .priv_size = sizeof(struct af_resample), - .priv_defaults = &(const struct af_resample) { - .opts = MP_RESAMPLE_OPTS_DEF, - .playback_speed = 1.0, - .allow_detach = 1, - .deprecation_warning = 1, - }, - .options = (const struct m_option[]) { - OPT_INTRANGE("filter-size", opts.filter_size, 0, 0, 32), - OPT_INTRANGE("phase-shift", opts.phase_shift, 0, 0, 30), - OPT_FLAG("linear", opts.linear, 0), - OPT_DOUBLE("cutoff", opts.cutoff, M_OPT_RANGE, .min = 0, .max = 1), - OPT_FLAG("detach", allow_detach, 0), - OPT_CHOICE("normalize", opts.normalize, 0, - ({"no", 0}, {"yes", 1}, {"auto", -1})), - OPT_KEYVALUELIST("o", opts.avopts, 0), - OPT_FLAG("deprecation-warning", deprecation_warning, 0), - {0} +static struct mp_filter *af_lavrresample_create(struct mp_filter *parent, + void *options) +{ + struct af_resample *s = options; + + if (s->opts.normalize < 0) + s->opts.normalize = s->global_normalize; + + struct mp_swresample *swr = mp_swresample_create(parent, &s->opts); + if (!swr) + abort(); + + MP_WARN(swr->f, "This filter is deprecated! Use the --audio-resample- options" + " to customize resampling, or the --af=aresample filter.\n"); + + talloc_free(s); + return swr->f; +} + +const struct mp_user_filter_entry af_lavrresample = { + .desc = { + .description = "Sample frequency conversion using libavresample", + .name = "lavrresample", + .priv_size = sizeof(struct af_resample), + .priv_defaults = &(const struct af_resample) { + .opts = MP_RESAMPLE_OPTS_DEF, + .allow_detach = 1, + }, + .options = (const struct m_option[]) { + OPT_INTRANGE("filter-size", opts.filter_size, 0, 0, 32), + OPT_INTRANGE("phase-shift", opts.phase_shift, 0, 0, 30), + OPT_FLAG("linear", opts.linear, 0), + OPT_DOUBLE("cutoff", opts.cutoff, M_OPT_RANGE, .min = 0, .max = 1), + OPT_FLAG("detach", allow_detach, 0), // does nothing + OPT_CHOICE("normalize", opts.normalize, 0, + ({"no", 0}, {"yes", 1}, {"auto", -1})), + OPT_KEYVALUELIST("o", opts.avopts, 0), + {0} + }, + .set_defaults = set_defaults, }, - .set_defaults = set_defaults, + .create = af_lavrresample_create, }; diff --git a/audio/filter/af_rubberband.c b/audio/filter/af_rubberband.c index 58cf077d8b..6c8c773e62 100644 --- a/audio/filter/af_rubberband.c +++ b/audio/filter/af_rubberband.c @@ -20,242 +20,348 @@ #include +#include "audio/aframe.h" +#include "audio/format.h" #include "common/common.h" -#include "af.h" +#include "filters/f_autoconvert.h" +#include "filters/filter_internal.h" +#include "filters/user_filters.h" +#include "options/m_option.h" + +// command line options +struct f_opts { + int transients, detector, phase, window, + smoothing, formant, pitch, channels; + double scale; +}; struct priv { + struct f_opts *opts; + + struct mp_pin *in_pin; + struct mp_aframe *cur_format; + struct mp_aframe_pool *out_pool; + bool sent_final; RubberBandState rubber; double speed; double pitch; - struct mp_audio *pending; - bool needs_reset; + struct mp_aframe *pending; // Estimate how much librubberband has buffered internally. // I could not find a way to do this with the librubberband API. double rubber_delay; - // command line options - int opt_transients, opt_detector, opt_phase, opt_window, - opt_smoothing, opt_formant, opt_pitch, opt_channels; }; -static void update_speed(struct af_instance *af, double new_speed) +static void update_speed(struct priv *p, double new_speed) { - struct priv *p = af->priv; - p->speed = new_speed; - rubberband_set_time_ratio(p->rubber, 1.0 / p->speed); + if (p->rubber) + rubberband_set_time_ratio(p->rubber, 1.0 / p->speed); } -static bool update_pitch(struct af_instance *af, double new_pitch) +static bool update_pitch(struct priv *p, double new_pitch) { if (new_pitch < 0.01 || new_pitch > 100.0) return false; - struct priv *p = af->priv; - p->pitch = new_pitch; - rubberband_set_pitch_scale(p->rubber, p->pitch); + if (p->rubber) + rubberband_set_pitch_scale(p->rubber, p->pitch); return true; } -static int control(struct af_instance *af, int cmd, void *arg) +static bool init_rubberband(struct mp_filter *f) { - struct priv *p = af->priv; + struct priv *p = f->priv; - switch (cmd) { - case AF_CONTROL_REINIT: { - struct mp_audio *in = arg; - struct mp_audio orig_in = *in; - struct mp_audio *out = af->data; + assert(!p->rubber); + assert(p->pending); - in->format = AF_FORMAT_FLOATP; - mp_audio_copy_config(out, in); - - if (p->rubber) - rubberband_delete(p->rubber); - - int opts = p->opt_transients | p->opt_detector | p->opt_phase | - p->opt_window | p->opt_smoothing | p->opt_formant | - p->opt_pitch | p-> opt_channels | - RubberBandOptionProcessRealTime; - - p->rubber = rubberband_new(in->rate, in->channels.num, opts, 1.0, 1.0); - if (!p->rubber) { - MP_FATAL(af, "librubberband initialization failed.\n"); - return AF_ERROR; - } + int opts = p->opts->transients | p->opts->detector | p->opts->phase | + p->opts->window | p->opts->smoothing | p->opts->formant | + p->opts->pitch | p-> opts->channels | + RubberBandOptionProcessRealTime; - update_speed(af, p->speed); - update_pitch(af, p->pitch); - control(af, AF_CONTROL_RESET, NULL); + int rate = mp_aframe_get_rate(p->pending); + int channels = mp_aframe_get_channels(p->pending); + if (mp_aframe_get_format(p->pending) != AF_FORMAT_FLOATP) + return false; - return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE; - } - case AF_CONTROL_SET_PLAYBACK_SPEED: { - update_speed(af, *(double *)arg); - return AF_OK; - } - case AF_CONTROL_RESET: - if (p->rubber) - rubberband_reset(p->rubber); - talloc_free(p->pending); - p->pending = NULL; - p->rubber_delay = 0; - return AF_OK; - case AF_CONTROL_COMMAND: { - char **args = arg; - char *endptr; - double pitch = p->pitch; - if (!strcmp(args[0], "set-pitch")) { - pitch = strtod(args[1], &endptr); - if (*endptr) - return CONTROL_ERROR; - return update_pitch(af, pitch) ? CONTROL_OK : CONTROL_ERROR; - } else if (!strcmp(args[0], "multiply-pitch")) { - double mult = strtod(args[1], &endptr); - if (*endptr || mult <= 0) - return CONTROL_ERROR; - pitch *= mult; - return update_pitch(af, pitch) ? CONTROL_OK : CONTROL_ERROR; - } else { - return CONTROL_ERROR; - } - } + p->rubber = rubberband_new(rate, channels, opts, 1.0, 1.0); + if (!p->rubber) { + MP_FATAL(f, "librubberband initialization failed.\n"); + return false; } - return AF_UNKNOWN; -} -static int filter_frame(struct af_instance *af, struct mp_audio *data) -{ - struct priv *p = af->priv; + mp_aframe_config_copy(p->cur_format, p->pending); - talloc_free(p->pending); - p->pending = data; + update_speed(p, p->speed); + update_pitch(p, p->pitch); - return 0; + return true; } -static int filter_out(struct af_instance *af) +static void process(struct mp_filter *f) { - struct priv *p = af->priv; + struct priv *p = f->priv; + + if (!mp_pin_in_needs_data(f->ppins[1])) + return; - while (rubberband_available(p->rubber) <= 0) { + while (!p->rubber || !p->pending || rubberband_available(p->rubber) <= 0) { const float *dummy[MP_NUM_CHANNELS] = {0}; const float **in_data = dummy; size_t in_samples = 0; - if (p->pending) { - if (!p->pending->samples) - break; - // recover from previous EOF - if (p->needs_reset) { - rubberband_reset(p->rubber); - p->rubber_delay = 0; + bool eof = false; + if (!p->pending || !mp_aframe_get_size(p->pending)) { + struct mp_frame frame = mp_pin_out_read(p->in_pin); + if (frame.type == MP_FRAME_AUDIO) { + TA_FREEP(&p->pending); + p->pending = frame.data; + } else if (frame.type == MP_FRAME_EOF) { + eof = true; + } else if (frame.type) { + MP_ERR(f, "unexpected frame type\n"); + goto error; + } else { + return; // no new data yet } - p->needs_reset = false; + } + assert(p->pending || eof); + if (!p->rubber) { + if (!p->pending) { + mp_pin_in_write(f->ppins[1], MP_EOF_FRAME); + return; + } + if (!init_rubberband(f)) + goto error; + } + + bool format_change = + p->pending && !mp_aframe_config_equals(p->pending, p->cur_format); + + if (p->pending && !format_change) { size_t needs = rubberband_get_samples_required(p->rubber); - in_data = (void *)&p->pending->planes; - in_samples = MPMIN(p->pending->samples, needs); + uint8_t **planes = mp_aframe_get_data_ro(p->pending); + int num_planes = mp_aframe_get_planes(p->pending); + for (int n = 0; n < num_planes; n++) + in_data[n] = (void *)planes[n]; + in_samples = MPMIN(mp_aframe_get_size(p->pending), needs); } - if (p->needs_reset) - break; // previous EOF - p->needs_reset = !p->pending; // EOF + bool final = format_change || eof; + if (!p->sent_final) + rubberband_process(p->rubber, in_data, in_samples, final); + p->sent_final |= final; - rubberband_process(p->rubber, in_data, in_samples, p->needs_reset); p->rubber_delay += in_samples; - if (!p->pending) - break; - mp_audio_skip_samples(p->pending, in_samples); + if (p->pending && !format_change) + mp_aframe_skip_samples(p->pending, in_samples); + + if (rubberband_available(p->rubber) > 0) { + if (eof) + mp_pin_out_repeat_eof(p->in_pin); // drain more next time + } else { + if (eof) { + mp_pin_in_write(f->ppins[1], MP_EOF_FRAME); + rubberband_reset(p->rubber); + TA_FREEP(&p->pending); + p->sent_final = false; + return; + } else if (format_change) { + // go on with proper reinit on the next iteration + rubberband_delete(p->rubber); + p->sent_final = false; + p->rubber = NULL; + } + } } + assert(p->pending); + int out_samples = rubberband_available(p->rubber); if (out_samples > 0) { - struct mp_audio *out = - mp_audio_pool_get(af->out_pool, af->data, out_samples); - if (!out) - return -1; - if (p->pending) - mp_audio_copy_config(out, p->pending); - - float **out_data = (void *)&out->planes; - out->samples = rubberband_retrieve(p->rubber, out_data, out->samples); - p->rubber_delay -= out->samples * p->speed; - - af_add_output_frame(af, out); + struct mp_aframe *out = mp_aframe_new_ref(p->cur_format); + if (mp_aframe_pool_allocate(p->out_pool, out, out_samples) < 0) { + talloc_free(out); + goto error; + } + + mp_aframe_copy_attributes(out, p->pending); + + float *out_data[MP_NUM_CHANNELS] = {0}; + uint8_t **planes = mp_aframe_get_data_rw(out); + assert(planes); + int num_planes = mp_aframe_get_planes(out); + for (int n = 0; n < num_planes; n++) + out_data[n] = (void *)planes[n]; + + out_samples = rubberband_retrieve(p->rubber, out_data, out_samples); + + if (!out_samples) { + mp_filter_internal_mark_progress(f); // unexpected, just try again + talloc_free(out); + return; + } + + mp_aframe_set_size(out, out_samples); + + p->rubber_delay -= out_samples * p->speed; + + double pts = mp_aframe_get_pts(p->pending); + if (pts != MP_NOPTS_VALUE) { + // Note: rubberband_get_latency() does not do what you'd expect. + double delay = p->rubber_delay / mp_aframe_get_effective_rate(out); + mp_aframe_set_pts(out, pts - delay); + } + + mp_aframe_mul_speed(out, p->speed); + + mp_pin_in_write(f->ppins[1], MAKE_FRAME(MP_FRAME_AUDIO, out)); } - int delay_samples = p->rubber_delay; - if (p->pending) - delay_samples += p->pending->samples; - af->delay = delay_samples / (af->data->rate * p->speed); + return; +error: + mp_filter_internal_mark_failed(f); +} - return 0; +static bool command(struct mp_filter *f, struct mp_filter_command *cmd) +{ + struct priv *p = f->priv; + + switch (cmd->type) { + case MP_FILTER_COMMAND_TEXT: { + char *endptr = NULL; + double pitch = p->pitch; + if (!strcmp(cmd->cmd, "set-pitch")) { + pitch = strtod(cmd->arg, &endptr); + if (*endptr) + return false; + return update_pitch(p, pitch); + } else if (!strcmp(cmd->cmd, "multiply-pitch")) { + double mult = strtod(cmd->arg, &endptr); + if (*endptr || mult <= 0) + return false; + pitch *= mult; + return update_pitch(p, pitch); + } + return false; + } + case MP_FILTER_COMMAND_SET_SPEED: + update_speed(p, cmd->speed); + return true; + } + + return false; } -static void uninit(struct af_instance *af) +static void reset(struct mp_filter *f) { - struct priv *p = af->priv; + struct priv *p = f->priv; if (p->rubber) - rubberband_delete(p->rubber); - talloc_free(p->pending); + rubberband_reset(p->rubber); + p->sent_final = false; + TA_FREEP(&p->pending); } -static int af_open(struct af_instance *af) +static void destroy(struct mp_filter *f) { - af->control = control; - af->filter_frame = filter_frame; - af->filter_out = filter_out; - af->uninit = uninit; - return AF_OK; + struct priv *p = f->priv; + + if (p->rubber) + rubberband_delete(p->rubber); + talloc_free(p->pending); } -#define OPT_BASE_STRUCT struct priv -const struct af_info af_info_rubberband = { - .info = "Pitch conversion with librubberband", +static const struct mp_filter_info af_rubberband_filter = { .name = "rubberband", - .open = af_open, .priv_size = sizeof(struct priv), - .priv_defaults = &(const struct priv) { - .speed = 1.0, - .pitch = 1.0, - .opt_pitch = RubberBandOptionPitchHighConsistency, - .opt_transients = RubberBandOptionTransientsMixed, - .opt_formant = RubberBandOptionFormantPreserved, - .opt_channels = RubberBandOptionChannelsTogether, - }, - .options = (const struct m_option[]) { - OPT_CHOICE("transients", opt_transients, 0, - ({"crisp", RubberBandOptionTransientsCrisp}, - {"mixed", RubberBandOptionTransientsMixed}, - {"smooth", RubberBandOptionTransientsSmooth})), - OPT_CHOICE("detector", opt_detector, 0, - ({"compound", RubberBandOptionDetectorCompound}, - {"percussive", RubberBandOptionDetectorPercussive}, - {"soft", RubberBandOptionDetectorSoft})), - OPT_CHOICE("phase", opt_phase, 0, - ({"laminar", RubberBandOptionPhaseLaminar}, - {"independent", RubberBandOptionPhaseIndependent})), - OPT_CHOICE("window", opt_window, 0, - ({"standard", RubberBandOptionWindowStandard}, - {"short", RubberBandOptionWindowShort}, - {"long", RubberBandOptionWindowLong})), - OPT_CHOICE("smoothing", opt_smoothing, 0, - ({"off", RubberBandOptionSmoothingOff}, - {"on", RubberBandOptionSmoothingOn})), - OPT_CHOICE("formant", opt_formant, 0, - ({"shifted", RubberBandOptionFormantShifted}, - {"preserved", RubberBandOptionFormantPreserved})), - OPT_CHOICE("pitch", opt_pitch, 0, - ({"quality", RubberBandOptionPitchHighQuality}, - {"speed", RubberBandOptionPitchHighSpeed}, - {"consistency", RubberBandOptionPitchHighConsistency})), - OPT_CHOICE("channels", opt_channels, 0, - ({"apart", RubberBandOptionChannelsApart}, - {"together", RubberBandOptionChannelsTogether})), - OPT_DOUBLE("pitch-scale", pitch, M_OPT_RANGE, .min = 0.01, .max = 100), - {0} + .process = process, + .command = command, + .reset = reset, + .destroy = destroy, +}; + +static struct mp_filter *af_rubberband_create(struct mp_filter *parent, + void *options) +{ + struct mp_filter *f = mp_filter_create(parent, &af_rubberband_filter); + if (!f) { + talloc_free(options); + return NULL; + } + + mp_filter_add_pin(f, MP_PIN_IN, "in"); + mp_filter_add_pin(f, MP_PIN_OUT, "out"); + + struct priv *p = f->priv; + p->opts = talloc_steal(p, options); + p->speed = 1.0; + p->pitch = p->opts->scale; + p->cur_format = talloc_steal(p, mp_aframe_create()); + p->out_pool = mp_aframe_pool_create(p); + + struct mp_autoconvert *conv = mp_autoconvert_create(f); + if (!conv) + abort(); + + mp_autoconvert_add_afmt(conv, AF_FORMAT_FLOATP); + + mp_pin_connect(conv->f->pins[0], f->ppins[0]); + p->in_pin = conv->f->pins[1]; + + return f; +} + +#define OPT_BASE_STRUCT struct f_opts + +const struct mp_user_filter_entry af_rubberband = { + .desc = { + .description = "Pitch conversion with librubberband", + .name = "rubberband", + .priv_size = sizeof(OPT_BASE_STRUCT), + .priv_defaults = &(const OPT_BASE_STRUCT) { + .scale = 1.0, + .pitch = RubberBandOptionPitchHighConsistency, + .transients = RubberBandOptionTransientsMixed, + .formant = RubberBandOptionFormantPreserved, + .channels = RubberBandOptionChannelsTogether, + }, + .options = (const struct m_option[]) { + OPT_CHOICE("transients", transients, 0, + ({"crisp", RubberBandOptionTransientsCrisp}, + {"mixed", RubberBandOptionTransientsMixed}, + {"smooth", RubberBandOptionTransientsSmooth})), + OPT_CHOICE("detector", detector, 0, + ({"compound", RubberBandOptionDetectorCompound}, + {"percussive", RubberBandOptionDetectorPercussive}, + {"soft", RubberBandOptionDetectorSoft})), + OPT_CHOICE("phase", phase, 0, + ({"laminar", RubberBandOptionPhaseLaminar}, + {"independent", RubberBandOptionPhaseIndependent})), + OPT_CHOICE("window", window, 0, + ({"standard", RubberBandOptionWindowStandard}, + {"short", RubberBandOptionWindowShort}, + {"long", RubberBandOptionWindowLong})), + OPT_CHOICE("smoothing", smoothing, 0, + ({"off", RubberBandOptionSmoothingOff}, + {"on", RubberBandOptionSmoothingOn})), + OPT_CHOICE("formant", formant, 0, + ({"shifted", RubberBandOptionFormantShifted}, + {"preserved", RubberBandOptionFormantPreserved})), + OPT_CHOICE("pitch", pitch, 0, + ({"quality", RubberBandOptionPitchHighQuality}, + {"speed", RubberBandOptionPitchHighSpeed}, + {"consistency", RubberBandOptionPitchHighConsistency})), + OPT_CHOICE("channels", channels, 0, + ({"apart", RubberBandOptionChannelsApart}, + {"together", RubberBandOptionChannelsTogether})), + OPT_DOUBLE("pitch-scale", scale, M_OPT_RANGE, .min = 0.01, .max = 100), + {0} + }, }, + .create = af_rubberband_create, }; diff --git a/audio/filter/af_scaletempo.c b/audio/filter/af_scaletempo.c index 0499631ea9..4e48e6168b 100644 --- a/audio/filter/af_scaletempo.c +++ b/audio/filter/af_scaletempo.c @@ -35,14 +35,32 @@ #include #include +#include "audio/aframe.h" +#include "audio/format.h" #include "common/common.h" - -#include "af.h" +#include "filters/f_autoconvert.h" +#include "filters/filter_internal.h" +#include "filters/user_filters.h" #include "options/m_option.h" -// Data for specific instances of this filter -typedef struct af_scaletempo_s -{ +struct f_opts { + float scale_nominal; + float ms_stride; + float ms_search; + float percent_overlap; +#define SCALE_TEMPO 1 +#define SCALE_PITCH 2 + int speed_opt; +}; + +struct priv { + struct f_opts *opts; + + struct mp_pin *in_pin; + struct mp_aframe *cur_format; + struct mp_aframe_pool *out_pool; + double current_pts; + // stride float scale; float speed; @@ -62,28 +80,21 @@ typedef struct af_scaletempo_s int bytes_standing; void *buf_overlap; void *table_blend; - void (*output_overlap)(struct af_scaletempo_s *s, void *out_buf, + void (*output_overlap)(struct priv *s, void *out_buf, int bytes_off); // best overlap int frames_search; int num_channels; void *buf_pre_corr; void *table_window; - int (*best_overlap_offset)(struct af_scaletempo_s *s); - // command line - float scale_nominal; - float ms_stride; - float percent_overlap; - float ms_search; -#define SCALE_TEMPO 1 -#define SCALE_PITCH 2 - int speed_opt; -} af_scaletempo_t; + int (*best_overlap_offset)(struct priv *s); +}; -static int fill_queue(struct af_instance *af, struct mp_audio *data, int offset) +static bool reinit(struct mp_filter *f, struct mp_aframe *in); + +static int fill_queue(struct priv *s, struct mp_aframe *in, int offset) { - af_scaletempo_t *s = af->priv; - int bytes_in = (data ? mp_audio_psize(data) : 0) - offset; + int bytes_in = in ? mp_aframe_get_size(in) * s->bytes_per_frame - offset : 0; int offset_unchanged = offset; if (s->bytes_to_slide > 0) { @@ -106,8 +117,8 @@ static int fill_queue(struct af_instance *af, struct mp_audio *data, int offset) if (bytes_in > 0) { int bytes_copy = MPMIN(s->bytes_queue - s->bytes_queued, bytes_in); assert(bytes_copy >= 0); - memcpy(s->buf_queue + s->bytes_queued, - (int8_t *)data->planes[0] + offset, bytes_copy); + uint8_t **planes = mp_aframe_get_data_ro(in); + memcpy(s->buf_queue + s->bytes_queued, planes[0] + offset, bytes_copy); s->bytes_queued += bytes_copy; offset += bytes_copy; } @@ -117,7 +128,7 @@ static int fill_queue(struct af_instance *af, struct mp_audio *data, int offset) #define UNROLL_PADDING (4 * 4) -static int best_overlap_offset_float(af_scaletempo_t *s) +static int best_overlap_offset_float(struct priv *s) { float best_corr = INT_MIN; int best_off = 0; @@ -146,7 +157,7 @@ static int best_overlap_offset_float(af_scaletempo_t *s) return best_off * 4 * s->num_channels; } -static int best_overlap_offset_s16(af_scaletempo_t *s) +static int best_overlap_offset_s16(struct priv *s) { int64_t best_corr = INT64_MIN; int best_off = 0; @@ -183,7 +194,7 @@ static int best_overlap_offset_s16(af_scaletempo_t *s) return best_off * 2 * s->num_channels; } -static void output_overlap_float(af_scaletempo_t *s, void *buf_out, +static void output_overlap_float(struct priv *s, void *buf_out, int bytes_off) { float *pout = buf_out; @@ -196,7 +207,7 @@ static void output_overlap_float(af_scaletempo_t *s, void *buf_out, } } -static void output_overlap_s16(af_scaletempo_t *s, void *buf_out, +static void output_overlap_s16(struct priv *s, void *buf_out, int bytes_off) { int16_t *pout = buf_out; @@ -209,28 +220,65 @@ static void output_overlap_s16(af_scaletempo_t *s, void *buf_out, } } -static int filter(struct af_instance *af, struct mp_audio *data) +static void process(struct mp_filter *f) { - af_scaletempo_t *s = af->priv; + struct priv *s = f->priv; + + if (!mp_pin_can_transfer_data(f->ppins[1], s->in_pin)) + return; - if (s->scale == 1.0) { - af->delay = 0; - af_add_output_frame(af, data); - return 0; + struct mp_aframe *in = NULL, *out = NULL; + + struct mp_frame frame = mp_pin_out_read(s->in_pin); + if (frame.type != MP_FRAME_AUDIO && frame.type != MP_FRAME_EOF) { + MP_ERR(f, "unexpected frame type\n"); + goto error; + } + + in = frame.type == MP_FRAME_AUDIO ? frame.data : NULL; + bool is_eof = !in; + + // EOF before it was even initialized once. + if (is_eof && !mp_aframe_config_is_valid(s->cur_format)) { + mp_pin_in_write(f->ppins[1], MP_EOF_FRAME); + return; + } + + if (in && !mp_aframe_config_equals(in, s->cur_format)) { + if (s->bytes_queued) { + // Drain remaining data before executing the format change. + MP_VERBOSE(f, "draining\n"); + mp_pin_out_unread(s->in_pin, frame); + in = NULL; + } else { + if (!reinit(f, in)) { + MP_ERR(f, "initialization failed\n"); + goto error; + } + } } - int in_samples = data ? data->samples : 0; - struct mp_audio *out = mp_audio_pool_get(af->out_pool, af->data, - ((int)(in_samples / s->frames_stride_scaled) + 1) * s->frames_stride); - if (!out) { - talloc_free(data); - return -1; + int in_samples = in ? mp_aframe_get_size(in) : 0; + + int max_out_samples = + ((int)(in_samples / s->frames_stride_scaled) + 1) * s->frames_stride; + if (!in) + max_out_samples += s->bytes_queued; + out = mp_aframe_new_ref(s->cur_format); + if (mp_aframe_pool_allocate(s->out_pool, out, max_out_samples) < 0) + goto error; + + if (in) { + mp_aframe_copy_attributes(out, in); + s->current_pts = mp_aframe_end_pts(in); } - if (data) - mp_audio_copy_attributes(out, data); - int offset_in = fill_queue(af, data, 0); - int8_t *pout = out->planes[0]; + int offset_in = fill_queue(s, in, 0); + uint8_t **out_planes = mp_aframe_get_data_rw(out); + if (!out_planes) + goto error; + int8_t *pout = out_planes[0]; + int out_offset = 0; while (s->bytes_queued >= s->bytes_queue) { int ti; float tf; @@ -240,12 +288,12 @@ static int filter(struct af_instance *af, struct mp_audio *data) if (s->output_overlap) { if (s->best_overlap_offset) bytes_off = s->best_overlap_offset(s); - s->output_overlap(s, pout, bytes_off); + s->output_overlap(s, pout + out_offset, bytes_off); } - memcpy(pout + s->bytes_overlap, + memcpy(pout + out_offset + s->bytes_overlap, s->buf_queue + bytes_off + s->bytes_overlap, s->bytes_standing); - pout += s->bytes_stride; + out_offset += s->bytes_stride; // input stride memcpy(s->buf_overlap, @@ -256,239 +304,302 @@ static int filter(struct af_instance *af, struct mp_audio *data) s->frames_stride_error = tf - ti; s->bytes_to_slide = ti * s->bytes_per_frame; - offset_in += fill_queue(af, data, offset_in); + offset_in += fill_queue(s, in, offset_in); } + // Drain remaining buffered data. + if (!in && s->bytes_queued) { + memcpy(pout + out_offset, s->buf_queue, s->bytes_queued); + out_offset += s->bytes_queued; + s->bytes_queued = 0; + } + mp_aframe_set_size(out, out_offset / s->bytes_per_frame); // This filter can have a negative delay when scale > 1: // output corresponding to some length of input can be decided and written // after receiving only a part of that input. - af->delay = (s->bytes_queued - s->bytes_to_slide) / s->scale - / out->sstride / out->rate; + double delay = (out_offset * s->speed + s->bytes_queued - s->bytes_to_slide) / + s->bytes_per_frame / mp_aframe_get_effective_rate(out); - out->samples = (pout - (int8_t *)out->planes[0]) / out->sstride; - talloc_free(data); - if (out->samples) { - af_add_output_frame(af, out); - } else { - talloc_free(out); + if (s->current_pts != MP_NOPTS_VALUE) + mp_aframe_set_pts(out, s->current_pts - delay); + + mp_aframe_mul_speed(out, s->speed); + + if (!mp_aframe_get_size(out)) + TA_FREEP(&out); + + if (is_eof && out) { + mp_pin_out_repeat_eof(s->in_pin); + } else if (is_eof && !out) { + mp_pin_in_write(f->ppins[1], MP_EOF_FRAME); + } else if (!is_eof && !out) { + mp_pin_out_request_data(s->in_pin); } - return 0; + + if (out) + mp_pin_in_write(f->ppins[1], MAKE_FRAME(MP_FRAME_AUDIO, out)); + + talloc_free(in); + return; + +error: + talloc_free(in); + talloc_free(out); + mp_filter_internal_mark_failed(f); } -static void update_speed(struct af_instance *af, float speed) +static void update_speed(struct priv *s, float speed) { - af_scaletempo_t *s = af->priv; - s->speed = speed; - double factor = (s->speed_opt & SCALE_PITCH) ? 1.0 / s->speed : s->speed; - s->scale = factor * s->scale_nominal; + double factor = (s->opts->speed_opt & SCALE_PITCH) ? 1.0 / s->speed : s->speed; + s->scale = factor * s->opts->scale_nominal; s->frames_stride_scaled = s->scale * s->frames_stride; s->frames_stride_error = MPMIN(s->frames_stride_error, s->frames_stride_scaled); } -// Initialization and runtime control -static int control(struct af_instance *af, int cmd, void *arg) +static bool reinit(struct mp_filter *f, struct mp_aframe *in) { - af_scaletempo_t *s = af->priv; - switch (cmd) { - case AF_CONTROL_REINIT: { - struct mp_audio *data = (struct mp_audio *)arg; - float srate = data->rate / 1000.0; - int nch = data->nch; - int use_int = 0; - - mp_audio_force_interleaved_format(data); - mp_audio_copy_config(af->data, data); - - if (data->format == AF_FORMAT_S16) { - use_int = 1; - } else { - mp_audio_set_format(af->data, AF_FORMAT_FLOAT); - } - int bps = af->data->bps; + struct priv *s = f->priv; - s->frames_stride = srate * s->ms_stride; - s->bytes_stride = s->frames_stride * bps * nch; - af->delay = 0; + mp_aframe_reset(s->cur_format); - update_speed(af, s->speed); + float srate = mp_aframe_get_rate(in) / 1000.0; + int nch = mp_aframe_get_channels(in); + int format = mp_aframe_get_format(in); - int frames_overlap = s->frames_stride * s->percent_overlap; - if (frames_overlap <= 0) { - s->bytes_standing = s->bytes_stride; - s->samples_standing = s->bytes_standing / bps; - s->output_overlap = NULL; - s->bytes_overlap = 0; - } else { - s->samples_overlap = frames_overlap * nch; - s->bytes_overlap = frames_overlap * nch * bps; - s->bytes_standing = s->bytes_stride - s->bytes_overlap; - s->samples_standing = s->bytes_standing / bps; - s->buf_overlap = realloc(s->buf_overlap, s->bytes_overlap); - s->table_blend = realloc(s->table_blend, s->bytes_overlap * 4); - if (!s->buf_overlap || !s->table_blend) { - MP_FATAL(af, "Out of memory\n"); - return AF_ERROR; + int use_int = 0; + if (format == AF_FORMAT_S16) { + use_int = 1; + } else if (format != AF_FORMAT_FLOAT) { + return false; + } + int bps = use_int ? 2 : 4; + + s->frames_stride = srate * s->opts->ms_stride; + s->bytes_stride = s->frames_stride * bps * nch; + + update_speed(s, s->speed); + + int frames_overlap = s->frames_stride * s->opts->percent_overlap; + if (frames_overlap <= 0) { + s->bytes_standing = s->bytes_stride; + s->samples_standing = s->bytes_standing / bps; + s->output_overlap = NULL; + s->bytes_overlap = 0; + } else { + s->samples_overlap = frames_overlap * nch; + s->bytes_overlap = frames_overlap * nch * bps; + s->bytes_standing = s->bytes_stride - s->bytes_overlap; + s->samples_standing = s->bytes_standing / bps; + s->buf_overlap = realloc(s->buf_overlap, s->bytes_overlap); + s->table_blend = realloc(s->table_blend, s->bytes_overlap * 4); + if (!s->buf_overlap || !s->table_blend) { + MP_FATAL(f, "Out of memory\n"); + return false; + } + memset(s->buf_overlap, 0, s->bytes_overlap); + if (use_int) { + int32_t *pb = s->table_blend; + int64_t blend = 0; + for (int i = 0; i < frames_overlap; i++) { + int32_t v = blend / frames_overlap; + for (int j = 0; j < nch; j++) + *pb++ = v; + blend += 65536; // 2^16 } - memset(s->buf_overlap, 0, s->bytes_overlap); - if (use_int) { - int32_t *pb = s->table_blend; - int64_t blend = 0; - for (int i = 0; i < frames_overlap; i++) { - int32_t v = blend / frames_overlap; - for (int j = 0; j < nch; j++) - *pb++ = v; - blend += 65536; // 2^16 - } - s->output_overlap = output_overlap_s16; - } else { - float *pb = s->table_blend; - for (int i = 0; i < frames_overlap; i++) { - float v = i / (float)frames_overlap; - for (int j = 0; j < nch; j++) - *pb++ = v; - } - s->output_overlap = output_overlap_float; + s->output_overlap = output_overlap_s16; + } else { + float *pb = s->table_blend; + for (int i = 0; i < frames_overlap; i++) { + float v = i / (float)frames_overlap; + for (int j = 0; j < nch; j++) + *pb++ = v; } + s->output_overlap = output_overlap_float; } + } - s->frames_search = (frames_overlap > 1) ? srate * s->ms_search : 0; - if (s->frames_search <= 0) - s->best_overlap_offset = NULL; - else { - if (use_int) { - int64_t t = frames_overlap; - int32_t n = 8589934588LL / (t * t); // 4 * (2^31 - 1) / t^2 - s->buf_pre_corr = realloc(s->buf_pre_corr, - s->bytes_overlap * 2 + UNROLL_PADDING); - s->table_window = realloc(s->table_window, - s->bytes_overlap * 2 - nch * bps * 2); - if (!s->buf_pre_corr || !s->table_window) { - MP_FATAL(af, "Out of memory\n"); - return AF_ERROR; - } - memset((char *)s->buf_pre_corr + s->bytes_overlap * 2, 0, - UNROLL_PADDING); - int32_t *pw = s->table_window; - for (int i = 1; i < frames_overlap; i++) { - int32_t v = (i * (t - i) * n) >> 15; - for (int j = 0; j < nch; j++) - *pw++ = v; - } - s->best_overlap_offset = best_overlap_offset_s16; - } else { - s->buf_pre_corr = realloc(s->buf_pre_corr, s->bytes_overlap); - s->table_window = realloc(s->table_window, - s->bytes_overlap - nch * bps); - if (!s->buf_pre_corr || !s->table_window) { - MP_FATAL(af, "Out of memory\n"); - return AF_ERROR; - } - float *pw = s->table_window; - for (int i = 1; i < frames_overlap; i++) { - float v = i * (frames_overlap - i); - for (int j = 0; j < nch; j++) - *pw++ = v; - } - s->best_overlap_offset = best_overlap_offset_float; + s->frames_search = (frames_overlap > 1) ? srate * s->opts->ms_search : 0; + if (s->frames_search <= 0) + s->best_overlap_offset = NULL; + else { + if (use_int) { + int64_t t = frames_overlap; + int32_t n = 8589934588LL / (t * t); // 4 * (2^31 - 1) / t^2 + s->buf_pre_corr = realloc(s->buf_pre_corr, + s->bytes_overlap * 2 + UNROLL_PADDING); + s->table_window = realloc(s->table_window, + s->bytes_overlap * 2 - nch * bps * 2); + if (!s->buf_pre_corr || !s->table_window) { + MP_FATAL(f, "Out of memory\n"); + return false; + } + memset((char *)s->buf_pre_corr + s->bytes_overlap * 2, 0, + UNROLL_PADDING); + int32_t *pw = s->table_window; + for (int i = 1; i < frames_overlap; i++) { + int32_t v = (i * (t - i) * n) >> 15; + for (int j = 0; j < nch; j++) + *pw++ = v; } + s->best_overlap_offset = best_overlap_offset_s16; + } else { + s->buf_pre_corr = realloc(s->buf_pre_corr, s->bytes_overlap); + s->table_window = realloc(s->table_window, + s->bytes_overlap - nch * bps); + if (!s->buf_pre_corr || !s->table_window) { + MP_FATAL(f, "Out of memory\n"); + return false; + } + float *pw = s->table_window; + for (int i = 1; i < frames_overlap; i++) { + float v = i * (frames_overlap - i); + for (int j = 0; j < nch; j++) + *pw++ = v; + } + s->best_overlap_offset = best_overlap_offset_float; } + } - s->bytes_per_frame = bps * nch; - s->num_channels = nch; - - s->bytes_queue = (s->frames_search + s->frames_stride + frames_overlap) - * bps * nch; - s->buf_queue = realloc(s->buf_queue, s->bytes_queue + UNROLL_PADDING); - if (!s->buf_queue) { - MP_FATAL(af, "Out of memory\n"); - return AF_ERROR; - } + s->bytes_per_frame = bps * nch; + s->num_channels = nch; - s->bytes_queued = 0; - s->bytes_to_slide = 0; - - MP_DBG(af, "" - "%.2f stride_in, %i stride_out, %i standing, " - "%i overlap, %i search, %i queue, %s mode\n", - s->frames_stride_scaled, - (int)(s->bytes_stride / nch / bps), - (int)(s->bytes_standing / nch / bps), - (int)(s->bytes_overlap / nch / bps), - s->frames_search, - (int)(s->bytes_queue / nch / bps), - (use_int ? "s16" : "float")); - - return af_test_output(af, (struct mp_audio *)arg); + s->bytes_queue = (s->frames_search + s->frames_stride + frames_overlap) + * bps * nch; + s->buf_queue = realloc(s->buf_queue, s->bytes_queue + UNROLL_PADDING); + if (!s->buf_queue) { + MP_FATAL(f, "Out of memory\n"); + return false; } - case AF_CONTROL_SET_PLAYBACK_SPEED: { - double speed = *(double *)arg; - if (s->speed_opt & SCALE_TEMPO) { - if (s->speed_opt & SCALE_PITCH) - break; - update_speed(af, speed); - } else if (s->speed_opt & SCALE_PITCH) { - update_speed(af, speed); - break; // do not signal OK + + s->bytes_queued = 0; + s->bytes_to_slide = 0; + + MP_DBG(f, "" + "%.2f stride_in, %i stride_out, %i standing, " + "%i overlap, %i search, %i queue, %s mode\n", + s->frames_stride_scaled, + (int)(s->bytes_stride / nch / bps), + (int)(s->bytes_standing / nch / bps), + (int)(s->bytes_overlap / nch / bps), + s->frames_search, + (int)(s->bytes_queue / nch / bps), + (use_int ? "s16" : "float")); + + mp_aframe_config_copy(s->cur_format, in); + + return true; +} + +static bool command(struct mp_filter *f, struct mp_filter_command *cmd) +{ + struct priv *s = f->priv; + + if (cmd->type == MP_FILTER_COMMAND_SET_SPEED) { + if (s->opts->speed_opt & SCALE_TEMPO) { + if (s->opts->speed_opt & SCALE_PITCH) + return false; + update_speed(s, cmd->speed); + return true; + } else if (s->opts->speed_opt & SCALE_PITCH) { + update_speed(s, cmd->speed); + return false; // do not signal OK } - return AF_OK; - } - case AF_CONTROL_RESET: - s->bytes_queued = 0; - s->bytes_to_slide = 0; - s->frames_stride_error = 0; - memset(s->buf_overlap, 0, s->bytes_overlap); } - return AF_UNKNOWN; + + return false; } -// Deallocate memory -static void uninit(struct af_instance *af) +static void reset(struct mp_filter *f) { - af_scaletempo_t *s = af->priv; + struct priv *s = f->priv; + + s->current_pts = MP_NOPTS_VALUE; + s->bytes_queued = 0; + s->bytes_to_slide = 0; + s->frames_stride_error = 0; + memset(s->buf_overlap, 0, s->bytes_overlap); +} + +static void destroy(struct mp_filter *f) +{ + struct priv *s = f->priv; free(s->buf_queue); free(s->buf_overlap); free(s->buf_pre_corr); free(s->table_blend); free(s->table_window); + mp_filter_free_children(f); } -// Allocate memory and set function pointers -static int af_open(struct af_instance *af) +static const struct mp_filter_info af_scaletempo_filter = { + .name = "scaletempo", + .priv_size = sizeof(struct priv), + .process = process, + .command = command, + .reset = reset, + .destroy = destroy, +}; + +static struct mp_filter *af_scaletempo_create(struct mp_filter *parent, + void *options) { - af->control = control; - af->uninit = uninit; - af->filter_frame = filter; - return AF_OK; -} + struct mp_filter *f = mp_filter_create(parent, &af_scaletempo_filter); + if (!f) { + talloc_free(options); + return NULL; + } -#define OPT_BASE_STRUCT af_scaletempo_t + mp_filter_add_pin(f, MP_PIN_IN, "in"); + mp_filter_add_pin(f, MP_PIN_OUT, "out"); -const struct af_info af_info_scaletempo = { - .info = "Scale audio tempo while maintaining pitch", - .name = "scaletempo", - .open = af_open, - .priv_size = sizeof(af_scaletempo_t), - .priv_defaults = &(const af_scaletempo_t) { - .ms_stride = 60, - .percent_overlap = .20, - .ms_search = 14, - .speed_opt = SCALE_TEMPO, - .speed = 1.0, - .scale_nominal = 1.0, - }, - .options = (const struct m_option[]) { - OPT_FLOAT("scale", scale_nominal, M_OPT_MIN, .min = 0.01), - OPT_FLOAT("stride", ms_stride, M_OPT_MIN, .min = 0.01), - OPT_FLOAT("overlap", percent_overlap, M_OPT_RANGE, .min = 0, .max = 1), - OPT_FLOAT("search", ms_search, M_OPT_MIN, .min = 0), - OPT_CHOICE("speed", speed_opt, 0, - ({"pitch", SCALE_PITCH}, - {"tempo", SCALE_TEMPO}, - {"none", 0}, - {"both", SCALE_TEMPO | SCALE_PITCH})), - {0} + struct priv *s = f->priv; + s->opts = talloc_steal(s, options); + s->speed = 1.0; + s->cur_format = talloc_steal(s, mp_aframe_create()); + s->out_pool = mp_aframe_pool_create(s); + + struct mp_autoconvert *conv = mp_autoconvert_create(f); + if (!conv) + abort(); + + mp_autoconvert_add_afmt(conv, AF_FORMAT_S16); + mp_autoconvert_add_afmt(conv, AF_FORMAT_FLOAT); + + mp_pin_connect(conv->f->pins[0], f->ppins[0]); + s->in_pin = conv->f->pins[1]; + + return f; +} + +#define OPT_BASE_STRUCT struct f_opts + +const struct mp_user_filter_entry af_scaletempo = { + .desc = { + .description = "Scale audio tempo while maintaining pitch", + .name = "scaletempo", + .priv_size = sizeof(OPT_BASE_STRUCT), + .priv_defaults = &(const OPT_BASE_STRUCT) { + .ms_stride = 60, + .percent_overlap = .20, + .ms_search = 14, + .speed_opt = SCALE_TEMPO, + .scale_nominal = 1.0, + }, + .options = (const struct m_option[]) { + OPT_FLOAT("scale", scale_nominal, M_OPT_MIN, .min = 0.01), + OPT_FLOAT("stride", ms_stride, M_OPT_MIN, .min = 0.01), + OPT_FLOAT("overlap", percent_overlap, M_OPT_RANGE, .min = 0, .max = 1), + OPT_FLOAT("search", ms_search, M_OPT_MIN, .min = 0), + OPT_CHOICE("speed", speed_opt, 0, + ({"pitch", SCALE_PITCH}, + {"tempo", SCALE_TEMPO}, + {"none", 0}, + {"both", SCALE_TEMPO | SCALE_PITCH})), + {0} + }, }, + .create = af_scaletempo_create, }; diff --git a/audio/filter/tools.c b/audio/filter/tools.c deleted file mode 100644 index 4ebea64d4a..0000000000 --- a/audio/filter/tools.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * This file is part of mpv. - * - * mpv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * mpv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with mpv. If not, see . - */ - -#include -#include - -#include "common/common.h" -#include "af.h" - -/* Convert from ms to sample time */ -int af_from_ms(int n, float* in, int* out, int rate, float mi, float ma) -{ - int i = 0; - // Sanity check - if(!in || !out) - return AF_ERROR; - - for(i=0;idata with out - * Note: logically, *out=*af->data always happens, because out contains the - * format only, no actual audio data or memory allocations. *out always - * contains the parameters from af->data after the function returns. - */ -int af_test_output(struct af_instance* af, struct mp_audio* out) -{ - if((af->data->format != out->format) || - (af->data->bps != out->bps) || - (af->data->rate != out->rate) || - !mp_chmap_equals(&af->data->channels, &out->channels)){ - *out = *af->data; - return AF_FALSE; - } - return AF_OK; -} - -/* Soft clipping, the sound of a dream, thanks to Jon Wattes - post to Musicdsp.org */ -float af_softclip(float a) -{ - if (a >= M_PI/2) - return 1.0; - else if (a <= -M_PI/2) - return -1.0; - else - return sin(a); -} -- cgit v1.2.3