From 7678c48a72c9750e52161fe023521c794068a72b Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Tue, 23 Apr 2013 10:59:41 +0200 Subject: Switch filters to AVFrame. Remove route filter for now, it should be replaced with lavfi later. --- src/Makefile | 1 - src/decoder_api.c | 1 - src/decoder_internal.c | 1 - src/filter/autoconvert_filter_plugin.c | 22 +-- src/filter/chain_filter_plugin.c | 28 ++- src/filter/convert_filter_plugin.c | 38 ++-- src/filter/normalize_filter_plugin.c | 31 ++- src/filter/null_filter_plugin.c | 12 +- src/filter/replay_gain_filter_plugin.c | 120 ++++++------ src/filter/route_filter_plugin.c | 348 --------------------------------- src/filter/volume_filter_plugin.c | 101 +++++----- src/filter_plugin.c | 10 +- src/filter_plugin.h | 17 +- src/filter_registry.c | 2 +- src/output_finish.c | 2 - src/output_init.c | 2 - src/output_internal.h | 6 - src/output_thread.c | 148 +++++++------- src/pcm_convert.h | 1 - 19 files changed, 240 insertions(+), 651 deletions(-) delete mode 100644 src/filter/route_filter_plugin.c diff --git a/src/Makefile b/src/Makefile index 61b8fc6b..057f40ee 100644 --- a/src/Makefile +++ b/src/Makefile @@ -172,7 +172,6 @@ OBJS = aiff.o \ filter/normalize_filter_plugin.o \ filter/null_filter_plugin.o \ filter/replay_gain_filter_plugin.o \ - filter/route_filter_plugin.o \ filter/volume_filter_plugin.o \ input/archive_input_plugin.o \ mixer/software_mixer_plugin.o \ diff --git a/src/decoder_api.c b/src/decoder_api.c index 35683d95..939e4470 100644 --- a/src/decoder_api.c +++ b/src/decoder_api.c @@ -23,7 +23,6 @@ #include "decoder_control.h" #include "audio_config.h" #include "song.h" -#include "buffer.h" #include "pipe.h" #include "chunk.h" #include "replay_gain_config.h" diff --git a/src/decoder_internal.c b/src/decoder_internal.c index 30089d17..5078591b 100644 --- a/src/decoder_internal.c +++ b/src/decoder_internal.c @@ -22,7 +22,6 @@ #include "decoder_control.h" #include "pipe.h" #include "input_stream.h" -#include "buffer.h" #include "chunk.h" #include diff --git a/src/filter/autoconvert_filter_plugin.c b/src/filter/autoconvert_filter_plugin.c index 3826a0fb..05efd42e 100644 --- a/src/filter/autoconvert_filter_plugin.c +++ b/src/filter/autoconvert_filter_plugin.c @@ -129,23 +129,17 @@ autoconvert_filter_close(struct filter *_filter) filter_close(filter->filter); } -static const void * -autoconvert_filter_filter(struct filter *_filter, const void *src, - size_t src_size, size_t *dest_size_r, - GError **error_r) +static AVFrame *autoconvert_filter_filter(struct filter *_filter, AVFrame *frame) { - struct autoconvert_filter *filter = - (struct autoconvert_filter *)_filter; + struct autoconvert_filter *filter = (struct autoconvert_filter *)_filter; - if (filter->convert != NULL) { - src = filter_filter(filter->convert, src, src_size, &src_size, - error_r); - if (src == NULL) - return NULL; - } + if (filter->convert != NULL) { + frame = filter_filter(filter->convert, frame); + if (!frame) + return NULL; + } - return filter_filter(filter->filter, src, src_size, dest_size_r, - error_r); + return filter_filter(filter->filter, frame); } static const struct filter_plugin autoconvert_filter_plugin = { diff --git a/src/filter/chain_filter_plugin.c b/src/filter/chain_filter_plugin.c index 2c785a36..50cb66a4 100644 --- a/src/filter/chain_filter_plugin.c +++ b/src/filter/chain_filter_plugin.c @@ -162,26 +162,22 @@ chain_filter_close(struct filter *_filter) g_slist_foreach(chain->children, chain_close_child, NULL); } -static const void * -chain_filter_filter(struct filter *_filter, - const void *src, size_t src_size, - size_t *dest_size_r, GError **error_r) +static AVFrame *chain_filter_filter(struct filter *_filter, + AVFrame *frame) { - struct filter_chain *chain = (struct filter_chain *)_filter; + struct filter_chain *chain = (struct filter_chain *)_filter; - for (GSList *i = chain->children; i != NULL; i = g_slist_next(i)) { - struct filter *filter = i->data; + for (GSList *i = chain->children; i != NULL; i = g_slist_next(i)) { + struct filter *filter = i->data; - /* feed the output of the previous filter as input - into the current one */ - src = filter_filter(filter, src, src_size, &src_size, error_r); - if (src == NULL) - return NULL; - } + /* feed the output of the previous filter as input + into the current one */ + frame = filter_filter(filter, frame); + if (!frame) + return NULL; + } - /* return the output of the last filter */ - *dest_size_r = src_size; - return src; + return frame; } const struct filter_plugin chain_filter_plugin = { diff --git a/src/filter/convert_filter_plugin.c b/src/filter/convert_filter_plugin.c index 40be67f6..2c2c3b0d 100644 --- a/src/filter/convert_filter_plugin.c +++ b/src/filter/convert_filter_plugin.c @@ -97,32 +97,20 @@ convert_filter_close(struct filter *_filter) sizeof(filter->out_audio_format)); } -static const void * -convert_filter_filter(struct filter *_filter, const void *src, size_t src_size, - size_t *dest_size_r, GError **error_r) +static AVFrame *convert_filter_filter(struct filter *_filter, AVFrame *src) { - struct convert_filter *filter = (struct convert_filter *)_filter; - const void *dest; - - if (audio_format_equals(&filter->in_audio_format, - &filter->out_audio_format)) { - /* optimized special case: no-op */ - *dest_size_r = src_size; - return src; - } - - // FIXME - return NULL; -#if 0 - dest = pcm_convert(&filter->state, &filter->in_audio_format, - src, src_size, - &filter->out_audio_format, dest_size_r, - error_r); - if (dest == NULL) - return NULL; - - return dest; -#endif + struct convert_filter *filter = (struct convert_filter *)_filter; + AVFrame *dst; + + /* optimized special case: no-op */ + if (audio_format_equals(&filter->in_audio_format, + &filter->out_audio_format)) + return src; + + dst = pcm_convert(&filter->state, &filter->in_audio_format, + &filter->out_audio_format, src); + av_frame_free(&src); + return dst; } const struct filter_plugin convert_filter_plugin = { diff --git a/src/filter/normalize_filter_plugin.c b/src/filter/normalize_filter_plugin.c index 2151482e..b17a8773 100644 --- a/src/filter/normalize_filter_plugin.c +++ b/src/filter/normalize_filter_plugin.c @@ -21,19 +21,19 @@ #include "filter_plugin.h" #include "filter_internal.h" #include "filter_registry.h" -#include "pcm_buffer.h" #include "audio_format.h" #include "AudioCompress/compress.h" #include #include +#include +#include + struct normalize_filter { struct filter filter; struct Compressor *compressor; - - struct pcm_buffer buffer; }; static inline GQuark @@ -70,8 +70,6 @@ normalize_filter_open(struct filter *_filter, filter->compressor = Compressor_new(0); - pcm_buffer_init(&filter->buffer); - return audio_format; } @@ -80,26 +78,23 @@ normalize_filter_close(struct filter *_filter) { struct normalize_filter *filter = (struct normalize_filter *)_filter; - pcm_buffer_deinit(&filter->buffer); Compressor_delete(filter->compressor); } -static const void * -normalize_filter_filter(struct filter *_filter, - const void *src, size_t src_size, size_t *dest_size_r, - G_GNUC_UNUSED GError **error_r) +static AVFrame *normalize_filter_filter(struct filter *_filter, + AVFrame *frame) { - struct normalize_filter *filter = (struct normalize_filter *)_filter; - void *dest; - - dest = pcm_buffer_get(&filter->buffer, src_size); + struct normalize_filter *filter = (struct normalize_filter *)_filter; + int ret = av_frame_make_writable(frame); - memcpy(dest, src, src_size); + if (ret < 0) { + av_frame_free(&frame); + return NULL; + } - Compressor_Process_int16(filter->compressor, dest, src_size / 2); + Compressor_Process_int16(filter->compressor, (uint16_t*)frame->data[0], frame->linesize[0] / 2); - *dest_size_r = src_size; - return dest; + return frame; } const struct filter_plugin normalize_filter_plugin = { diff --git a/src/filter/null_filter_plugin.c b/src/filter/null_filter_plugin.c index e7c99882..1097c315 100644 --- a/src/filter/null_filter_plugin.c +++ b/src/filter/null_filter_plugin.c @@ -70,17 +70,9 @@ null_filter_close(struct filter *_filter) (void)filter; } -static const void * -null_filter_filter(struct filter *_filter, - const void *src, size_t src_size, - size_t *dest_size_r, G_GNUC_UNUSED GError **error_r) +static AVFrame *null_filter_filter(struct filter *_filter, AVFrame *frame) { - struct null_filter *filter = (struct null_filter *)_filter; - (void)filter; - - /* return the unmodified source buffer */ - *dest_size_r = src_size; - return src; + return frame; } const struct filter_plugin null_filter_plugin = { diff --git a/src/filter/replay_gain_filter_plugin.c b/src/filter/replay_gain_filter_plugin.c index 583a09f9..8e01cb75 100644 --- a/src/filter/replay_gain_filter_plugin.c +++ b/src/filter/replay_gain_filter_plugin.c @@ -23,7 +23,6 @@ #include "filter_internal.h" #include "filter_registry.h" #include "audio_format.h" -#include "pcm_buffer.h" #include "pcm_volume.h" #include "replay_gain_info.h" #include "replay_gain_config.h" @@ -32,6 +31,10 @@ #include #include +#include +#include +#include + #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "replay_gain" @@ -69,8 +72,6 @@ struct replay_gain_filter { unsigned volume; struct audio_format audio_format; - - struct pcm_buffer buffer; }; static inline GQuark @@ -141,67 +142,67 @@ replay_gain_filter_open(struct filter *_filter, (struct replay_gain_filter *)_filter; filter->audio_format = *audio_format; - pcm_buffer_init(&filter->buffer); return &filter->audio_format; } -static void -replay_gain_filter_close(struct filter *_filter) -{ - struct replay_gain_filter *filter = - (struct replay_gain_filter *)_filter; - - pcm_buffer_deinit(&filter->buffer); -} - -static const void * -replay_gain_filter_filter(struct filter *_filter, - const void *src, size_t src_size, - size_t *dest_size_r, GError **error_r) +static AVFrame *replay_gain_filter_filter(struct filter *_filter, AVFrame *src) { - struct replay_gain_filter *filter = - (struct replay_gain_filter *)_filter; - bool success; - void *dest; - enum replay_gain_mode rg_mode; - - /* check if the mode has been changed since the last call */ - rg_mode = replay_gain_get_real_mode(); - - if (filter->mode != rg_mode) { - g_debug("replay gain mode has changed %d->%d\n", filter->mode, rg_mode); - filter->mode = rg_mode; - replay_gain_filter_update(filter); - } - - *dest_size_r = src_size; - - if (filter->volume == PCM_VOLUME_1) - /* optimized special case: 100% volume = no-op */ - return src; - - dest = pcm_buffer_get(&filter->buffer, src_size); - - if (filter->volume <= 0) { - /* optimized special case: 0% volume = memset(0) */ - /* XXX is this valid for all sample formats? What - about floating point? */ - memset(dest, 0, src_size); - return dest; - } - - memcpy(dest, src, src_size); - - success = pcm_volume(dest, src_size, filter->audio_format.format, - filter->volume); - if (!success) { - g_set_error(error_r, replay_gain_quark(), 0, - "pcm_volume() has failed"); - return NULL; - } - - return dest; + struct replay_gain_filter *filter = + (struct replay_gain_filter *)_filter; + int ret; + enum replay_gain_mode rg_mode; + + /* check if the mode has been changed since the last call */ + rg_mode = replay_gain_get_real_mode(); + + if (filter->mode != rg_mode) { + g_debug("replay gain mode has changed %d->%d\n", filter->mode, rg_mode); + filter->mode = rg_mode; + replay_gain_filter_update(filter); + } + + /* optimized special case: 100% volume = no-op */ + if (filter->volume == PCM_VOLUME_1) + return src; + + /* optimized special case: 0% volume = memset(0) */ + if (filter->volume <= 0) { + AVFrame *dst = av_frame_alloc(); + if (!dst) { + av_frame_free(&src); + return NULL; + } + + dst->nb_samples = src->nb_samples; + dst->format = src->format; + dst->channel_layout = src->channel_layout; + av_frame_copy_props(dst, src); + av_frame_free(&src); + + ret = av_frame_get_buffer(dst, 32); + if (ret < 0) + return NULL; + + av_samples_set_silence(dst->extended_data, 0, dst->nb_samples, + av_get_channel_layout_nb_channels(dst->channel_layout), + dst->format); + return dst; + } + + ret = av_frame_make_writable(src); + if (ret < 0) { + av_frame_free(&src); + return NULL; + } + + ret = pcm_volume(src->data[0], src->linesize[0], filter->audio_format.format, filter->volume); + if (!ret) { + g_warning("pcm_volume() has failed"); + return NULL; + } + + return src; } const struct filter_plugin replay_gain_filter_plugin = { @@ -209,7 +210,6 @@ const struct filter_plugin replay_gain_filter_plugin = { .init = replay_gain_filter_init, .finish = replay_gain_filter_finish, .open = replay_gain_filter_open, - .close = replay_gain_filter_close, .filter = replay_gain_filter_filter, }; diff --git a/src/filter/route_filter_plugin.c b/src/filter/route_filter_plugin.c deleted file mode 100644 index 3bf8677e..00000000 --- a/src/filter/route_filter_plugin.c +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program 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. - * - * This program 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 this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/** \file - * - * This filter copies audio data between channels. Useful for - * upmixing mono/stereo audio to surround speaker configurations. - * - * Its configuration consists of a "filter" section with a single - * "routes" entry, formatted as: \\ - * routes "0>1, 1>0, 2>2, 3>3, 3>4" \\ - * where each pair of numbers signifies a set of channels. - * Each source>dest pair leads to the data from channel #source - * being copied to channel #dest in the output. - * - * Example: \\ - * routes "0>0, 1>1, 0>2, 1>3"\\ - * upmixes stereo audio to a 4-speaker system, copying the front-left - * (0) to front left (0) and rear left (2), copying front-right (1) to - * front-right (1) and rear-right (3). - * - * If multiple sources are copied to the same destination channel, only - * one of them takes effect. - */ - -#include "config.h" -#include "conf.h" -#include "audio_format.h" -#include "audio_check.h" -#include "filter_plugin.h" -#include "filter_internal.h" -#include "filter_registry.h" -#include "pcm_buffer.h" - -#include -#include -#include - - -struct route_filter { - - /** - * Inherit (and support cast to/from) filter - */ - struct filter base; - - /** - * The minimum number of channels we need for output - * to be able to perform all the copies the user has specified - */ - unsigned char min_output_channels; - - /** - * The minimum number of input channels we need to - * copy all the data the user has requested. If fewer - * than this many are supplied by the input, undefined - * copy operations are given zeroed sources in stead. - */ - unsigned char min_input_channels; - - /** - * The set of copy operations to perform on each sample - * The index is an output channel to use, the value is - * a corresponding input channel from which to take the - * data. A -1 means "no source" - */ - signed char* sources; - - /** - * The actual input format of our signal, once opened - */ - struct audio_format input_format; - - /** - * The decided upon output format, once opened - */ - struct audio_format output_format; - - /** - * The size, in bytes, of each multichannel frame in the - * input buffer - */ - size_t input_frame_size; - - /** - * The size, in bytes, of each multichannel frame in the - * output buffer - */ - size_t output_frame_size; - - /** - * The output buffer used last time around, can be reused if the size doesn't differ. - */ - struct pcm_buffer output_buffer; - -}; - -/** - * Parse the "routes" section, a string on the form - * a>b, c>d, e>f, ... - * where a... are non-unique, non-negative integers - * and input channel a gets copied to output channel b, etc. - * @param param the configuration block to read - * @param filter a route_filter whose min_channels and sources[] to set - * @return true on success, false on error - */ -static bool -route_filter_parse(const struct config_param *param, - struct route_filter *filter, - GError **error_r) { - - /* TODO: - * With a more clever way of marking "don't copy to output N", - * This could easily be merged into a single loop with some - * dynamic g_realloc() instead of one count run and one g_malloc(). - */ - - gchar **tokens; - int number_of_copies; - - // A cowardly default, just passthrough stereo - const char *routes = - config_get_block_string(param, "routes", "0>0, 1>1"); - - filter->min_input_channels = 0; - filter->min_output_channels = 0; - - tokens = g_strsplit(routes, ",", 255); - number_of_copies = g_strv_length(tokens); - - // Start by figuring out a few basic things about the routing set - for (int c=0; cb string into source and destination - sd = g_strsplit(tokens[c], ">", 2); - if (g_strv_length(sd) != 2) { - g_set_error(error_r, config_quark(), 1, - "Invalid copy around %d in routes spec: %s", - param->line, tokens[c]); - g_strfreev(sd); - g_strfreev(tokens); - return false; - } - - source = strtol(sd[0], NULL, 10); - dest = strtol(sd[1], NULL, 10); - - // Keep track of the highest channel numbers seen - // as either in- or outputs - if (source >= filter->min_input_channels) - filter->min_input_channels = source + 1; - if (dest >= filter->min_output_channels) - filter->min_output_channels = dest + 1; - - g_strfreev(sd); - } - - if (!audio_valid_channel_count(filter->min_output_channels)) { - g_strfreev(tokens); - g_set_error(error_r, audio_format_quark(), 0, - "Invalid number of output channels requested: %d", - filter->min_output_channels); - return false; - } - - // Allocate a map of "copy nothing to me" - filter->sources = - g_malloc(filter->min_output_channels * sizeof(signed char)); - - for (int i=0; imin_output_channels; ++i) - filter->sources[i] = -1; - - // Run through the spec again, and save the - // actual mapping output <- input - for (int c=0; cb string into source and destination - sd = g_strsplit(tokens[c], ">", 2); - if (g_strv_length(sd) != 2) { - g_set_error(error_r, config_quark(), 1, - "Invalid copy around %d in routes spec: %s", - param->line, tokens[c]); - g_strfreev(sd); - g_strfreev(tokens); - return false; - } - - source = strtol(sd[0], NULL, 10); - dest = strtol(sd[1], NULL, 10); - - filter->sources[dest] = source; - - g_strfreev(sd); - } - - g_strfreev(tokens); - - return true; -} - -static struct filter * -route_filter_init(const struct config_param *param, - G_GNUC_UNUSED GError **error_r) -{ - struct route_filter *filter = g_new(struct route_filter, 1); - filter_init(&filter->base, &route_filter_plugin); - - // Allocate and set the filter->sources[] array - route_filter_parse(param, filter, error_r); - - return &filter->base; -} - -static void -route_filter_finish(struct filter *_filter) -{ - struct route_filter *filter = (struct route_filter *)_filter; - - g_free(filter->sources); - g_free(filter); -} - -static const struct audio_format * -route_filter_open(struct filter *_filter, struct audio_format *audio_format, - G_GNUC_UNUSED GError **error_r) -{ - struct route_filter *filter = (struct route_filter *)_filter; - - // Copy the input format for later reference - filter->input_format = *audio_format; - filter->input_frame_size = - audio_format_frame_size(&filter->input_format); - - // Decide on an output format which has enough channels, - // and is otherwise identical - filter->output_format = *audio_format; - filter->output_format.channels = filter->min_output_channels; - - // Precalculate this simple value, to speed up allocation later - filter->output_frame_size = - audio_format_frame_size(&filter->output_format); - - // This buffer grows as needed - pcm_buffer_init(&filter->output_buffer); - - return &filter->output_format; -} - -static void -route_filter_close(struct filter *_filter) -{ - struct route_filter *filter = (struct route_filter *)_filter; - - pcm_buffer_deinit(&filter->output_buffer); -} - -static const void * -route_filter_filter(struct filter *_filter, - const void *src, size_t src_size, - size_t *dest_size_r, G_GNUC_UNUSED GError **error_r) -{ - struct route_filter *filter = (struct route_filter *)_filter; - - size_t number_of_frames = src_size / filter->input_frame_size; - - size_t bytes_per_frame_per_channel = - audio_format_sample_size(&filter->input_format); - - // A moving pointer that always refers to channel 0 in the input, at the currently handled frame - const uint8_t *base_source = src; - - // A moving pointer that always refers to the currently filled channel of the currently handled frame, in the output - uint8_t *chan_destination; - - // Grow our reusable buffer, if needed, and set the moving pointer - *dest_size_r = number_of_frames * filter->output_frame_size; - chan_destination = pcm_buffer_get(&filter->output_buffer, *dest_size_r); - - - // Perform our copy operations, with N input channels and M output channels - for (unsigned int s=0; smin_output_channels; ++c) { - if (filter->sources[c] == -1 || - (unsigned)filter->sources[c] >= filter->input_format.channels) { - // No source for this destination output, - // give it zeroes as input - memset(chan_destination, - 0x00, - bytes_per_frame_per_channel); - } else { - // Get the data from channel sources[c] - // and copy it to the output - const uint8_t *data = base_source + - (filter->sources[c] * bytes_per_frame_per_channel); - memcpy(chan_destination, - data, - bytes_per_frame_per_channel); - } - // Move on to the next output channel - chan_destination += bytes_per_frame_per_channel; - } - - - // Go on to the next N input samples - base_source += filter->input_frame_size; - } - - // Here it is, ladies and gentlemen! Rerouted data! - return (void *) filter->output_buffer.buffer; -} - -const struct filter_plugin route_filter_plugin = { - .name = "route", - .init = route_filter_init, - .finish = route_filter_finish, - .open = route_filter_open, - .close = route_filter_close, - .filter = route_filter_filter, -}; diff --git a/src/filter/volume_filter_plugin.c b/src/filter/volume_filter_plugin.c index e52c0a46..27276998 100644 --- a/src/filter/volume_filter_plugin.c +++ b/src/filter/volume_filter_plugin.c @@ -23,7 +23,6 @@ #include "filter_internal.h" #include "filter_registry.h" #include "conf.h" -#include "pcm_buffer.h" #include "pcm_volume.h" #include "audio_format.h" #include "player_control.h" @@ -31,6 +30,10 @@ #include #include +#include +#include +#include + struct volume_filter { struct filter filter; @@ -40,8 +43,6 @@ struct volume_filter { unsigned volume; struct audio_format audio_format; - - struct pcm_buffer buffer; }; static inline GQuark @@ -75,54 +76,63 @@ volume_filter_open(struct filter *_filter, struct audio_format *audio_format, struct volume_filter *filter = (struct volume_filter *)_filter; filter->audio_format = *audio_format; - pcm_buffer_init(&filter->buffer); return &filter->audio_format; } -static void -volume_filter_close(struct filter *_filter) -{ - struct volume_filter *filter = (struct volume_filter *)_filter; - - pcm_buffer_deinit(&filter->buffer); -} - -static const void * -volume_filter_filter(struct filter *_filter, const void *src, size_t src_size, - size_t *dest_size_r, GError **error_r) +static AVFrame *volume_filter_filter(struct filter *_filter, AVFrame *src) { struct volume_filter *filter = (struct volume_filter *)_filter; - bool success; - void *dest; - - *dest_size_r = src_size; - - if (filter->volume >= PCM_VOLUME_1) - /* optimized special case: 100% volume = no-op */ - return src; - - dest = pcm_buffer_get(&filter->buffer, src_size); - - if (filter->volume <= 0) { - /* optimized special case: 0% volume = memset(0) */ - /* XXX is this valid for all sample formats? What - about floating point? */ - memset(dest, 0, src_size); - return dest; - } - - memcpy(dest, src, src_size); - - success = pcm_volume(dest, src_size, filter->audio_format.format, - filter->volume); - if (!success) { - g_set_error(error_r, volume_quark(), 0, - "pcm_volume() has failed"); - return NULL; - } - - return dest; + int planar = av_sample_fmt_is_planar(src->format); + int planes = planar ? av_get_channel_layout_nb_channels(src->channel_layout) : 1; + int ret, i; + + /* optimized special case: 100% volume = no-op */ + if (filter->volume == PCM_VOLUME_1) + return src; + + /* optimized special case: 0% volume = memset(0) */ + if (filter->volume <= 0) { + AVFrame *dst = av_frame_alloc(); + if (!dst) { + av_frame_free(&src); + return NULL; + } + + dst->nb_samples = src->nb_samples; + dst->format = src->format; + dst->channel_layout = src->channel_layout; + av_frame_copy_props(dst, src); + av_frame_free(&src); + + ret = av_frame_get_buffer(dst, 32); + if (ret < 0) + return NULL; + + av_samples_set_silence(dst->extended_data, 0, dst->nb_samples, + av_get_channel_layout_nb_channels(dst->channel_layout), + dst->format); + return dst; + } + + ret = av_frame_make_writable(src); + if (ret < 0) { + av_frame_free(&src); + return NULL; + } + + for (i = 0; i < planes; i++) { + ret = pcm_volume(src->extended_data[i], src->linesize[0], + av_get_packed_sample_fmt(filter->audio_format.format), + filter->volume); + if (!ret) { + g_warning("pcm_volume() has failed"); + av_frame_free(&src); + return NULL; + } + } + + return src; } const struct filter_plugin volume_filter_plugin = { @@ -130,7 +140,6 @@ const struct filter_plugin volume_filter_plugin = { .init = volume_filter_init, .finish = volume_filter_finish, .open = volume_filter_open, - .close = volume_filter_close, .filter = volume_filter_filter, }; diff --git a/src/filter_plugin.c b/src/filter_plugin.c index 7173134b..61b340c7 100644 --- a/src/filter_plugin.c +++ b/src/filter_plugin.c @@ -101,16 +101,10 @@ filter_close(struct filter *filter) filter->plugin->close(filter); } -const void * -filter_filter(struct filter *filter, const void *src, size_t src_size, - size_t *dest_size_r, - GError **error_r) +AVFrame *filter_filter(struct filter *filter, AVFrame *src) { assert(filter != NULL); assert(src != NULL); - assert(src_size > 0); - assert(dest_size_r != NULL); - assert(error_r == NULL || *error_r == NULL); - return filter->plugin->filter(filter, src, src_size, dest_size_r, error_r); + return filter->plugin->filter(filter, src); } diff --git a/src/filter_plugin.h b/src/filter_plugin.h index 58e34dfb..815ec283 100644 --- a/src/filter_plugin.h +++ b/src/filter_plugin.h @@ -26,10 +26,11 @@ #ifndef MPD_FILTER_PLUGIN_H #define MPD_FILTER_PLUGIN_H +#include + #include -#include -#include +#include struct config_param; struct filter; @@ -68,10 +69,7 @@ struct filter_plugin { /** * Filters a block of PCM data. */ - const void *(*filter)(struct filter *filter, - const void *src, size_t src_size, - size_t *dest_buffer_r, - GError **error_r); + AVFrame* (*filter)(struct filter *filter, AVFrame *src); }; /** @@ -134,7 +132,7 @@ filter_close(struct filter *filter); * Filters a block of PCM data. * * @param filter the filter object - * @param src the input buffer + * @param src the input frame * @param src_size the size of #src_buffer in bytes * @param dest_size_r the size of the returned buffer * @param error location to store the error occurring, or NULL to @@ -142,9 +140,6 @@ filter_close(struct filter *filter); * @return the destination buffer on success (will be invalidated by * filter_close() or filter_filter()), NULL on error */ -const void * -filter_filter(struct filter *filter, const void *src, size_t src_size, - size_t *dest_size_r, - GError **error_r); +AVFrame *filter_filter(struct filter *filter, AVFrame *src); #endif diff --git a/src/filter_registry.c b/src/filter_registry.c index dc188939..4a61d507 100644 --- a/src/filter_registry.c +++ b/src/filter_registry.c @@ -26,7 +26,7 @@ const struct filter_plugin *const filter_plugins[] = { &null_filter_plugin, - &route_filter_plugin, + // FIXME restore (switch to lavfi) &normalize_filter_plugin, &volume_filter_plugin, &replay_gain_filter_plugin, diff --git a/src/output_finish.c b/src/output_finish.c index e11b4367..810044ee 100644 --- a/src/output_finish.c +++ b/src/output_finish.c @@ -45,8 +45,6 @@ ao_base_finish(struct audio_output *ao) filter_free(ao->other_replay_gain_filter); filter_free(ao->filter); - - pcm_buffer_deinit(&ao->cross_fade_buffer); } void diff --git a/src/output_init.c b/src/output_init.c index c3b808e9..e98d4c65 100644 --- a/src/output_init.c +++ b/src/output_init.c @@ -173,8 +173,6 @@ ao_base_init(struct audio_output *ao, ao->allow_play = true; ao->fail_timer = NULL; - pcm_buffer_init(&ao->cross_fade_buffer); - /* set up the filter chain */ ao->filter = filter_chain_new(); diff --git a/src/output_internal.h b/src/output_internal.h index 3e737127..3da40c36 100644 --- a/src/output_internal.h +++ b/src/output_internal.h @@ -21,7 +21,6 @@ #define MPD_OUTPUT_INTERNAL_H #include "audio_format.h" -#include "pcm_buffer.h" #include @@ -140,11 +139,6 @@ struct audio_output { */ struct audio_format out_audio_format; - /** - * The buffer used to allocate the cross-fading result. - */ - struct pcm_buffer cross_fade_buffer; - /** * The filter object of this audio output. This is an * instance of chain_filter_plugin. diff --git a/src/output_thread.c b/src/output_thread.c index fde9050f..b9a4f3ea 100644 --- a/src/output_thread.c +++ b/src/output_thread.c @@ -38,6 +38,10 @@ #include #include +#include +#include +#include + #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "output" @@ -315,120 +319,101 @@ ao_wait(struct audio_output *ao) } } -static const char * -ao_chunk_data(struct audio_output *ao, const struct music_chunk *chunk, - struct filter *replay_gain_filter, - unsigned *replay_gain_serial_p, - size_t *length_r) +static AVFrame *ao_chunk_data(struct audio_output *ao, const struct music_chunk *chunk, + struct filter *replay_gain_filter, + unsigned *replay_gain_serial_p) { + AVFrame *frame = NULL; + assert(chunk != NULL); assert(!music_chunk_is_empty(chunk)); assert(music_chunk_check_format(chunk, &ao->in_audio_format)); - const char *data = chunk->frame ? chunk->frame->data[0] : NULL; - size_t length = data ? av_samples_get_buffer_size(NULL, ao->in_audio_format.channels, - chunk->frame->nb_samples, - chunk->frame->format, 1) : 0; + frame = av_frame_clone(chunk->frame); + if (!frame) + return NULL; - if (length > 0 && replay_gain_filter != NULL) { + if (replay_gain_filter) { if (chunk->replay_gain_serial != *replay_gain_serial_p) { replay_gain_filter_set_info(replay_gain_filter, - chunk->replay_gain_serial != 0 - ? &chunk->replay_gain_info - : NULL); + chunk->replay_gain_serial != 0 + ? &chunk->replay_gain_info + : NULL); *replay_gain_serial_p = chunk->replay_gain_serial; } - - GError *error = NULL; - data = filter_filter(replay_gain_filter, data, length, - &length, &error); - if (data == NULL) { - g_warning("\"%s\" [%s] failed to filter: %s", - ao->name, ao->plugin->name, error->message); - g_error_free(error); + frame = filter_filter(replay_gain_filter, frame); + if (!frame) { + g_warning("\"%s\" [%s] failed to apply replay gain", + ao->name, ao->plugin->name); return NULL; } } - *length_r = length; - return data; + return frame; } -static const char * -ao_filter_chunk(struct audio_output *ao, const struct music_chunk *chunk, - size_t *length_r) +static AVFrame *ao_filter_chunk(struct audio_output *ao, const struct music_chunk *chunk) { - GError *error = NULL; + AVFrame *frame; + int ret; - size_t length; - const char *data = ao_chunk_data(ao, chunk, ao->replay_gain_filter, - &ao->replay_gain_serial, &length); - if (data == NULL) + frame = ao_chunk_data(ao, chunk, ao->replay_gain_filter, + &ao->replay_gain_serial); + if (!frame) return NULL; - if (length == 0) { - /* empty chunk, nothing to do */ - *length_r = 0; - return data; - } - /* cross-fade */ - - if (chunk->other != NULL) { - size_t other_length; - const char *other_data = - ao_chunk_data(ao, chunk->other, - ao->other_replay_gain_filter, - &ao->other_replay_gain_serial, - &other_length); - if (other_data == NULL) + if (chunk->other) { + AVFrame *other = ao_chunk_data(ao, chunk->other, + ao->other_replay_gain_filter, + &ao->other_replay_gain_serial); + if (!other) { + av_frame_free(&frame); return NULL; - - if (other_length == 0) { - *length_r = 0; - return data; } /* if the "other" chunk is longer, then that trailer is used as-is, without mixing; it is part of the "next" song being faded in, and if there's a rest, it means cross-fading ends here */ + ret = av_frame_make_writable(other); + if (ret < 0) { + av_frame_free(&frame); + av_frame_free(&other); + return NULL; + } - if (length > other_length) - length = other_length; - - char *dest = pcm_buffer_get(&ao->cross_fade_buffer, - other_length); - memcpy(dest, other_data, other_length); - if (!pcm_mix(dest, data, length, ao->in_audio_format.format, - 1.0 - chunk->mix_ratio)) { + ret = pcm_mix(other->data[0], frame->data[0], + FFMIN(frame->linesize[0], other->linesize[0]), + ao->in_audio_format.format, + 1.0 - chunk->mix_ratio); + av_frame_free(&frame); + if (!ret) { g_warning("Cannot cross-fade format %s", - sample_format_to_string(ao->in_audio_format.format)); + sample_format_to_string(ao->in_audio_format.format)); + av_frame_free(&other); return NULL; } - data = dest; - length = other_length; + frame = other; } /* apply filter chain */ - - data = filter_filter(ao->filter, data, length, &length, &error); - if (data == NULL) { - g_warning("\"%s\" [%s] failed to filter: %s", - ao->name, ao->plugin->name, error->message); - g_error_free(error); + frame = filter_filter(ao->filter, frame); + if (!frame) { + g_warning("\"%s\" [%s] failed to filter\n", + ao->name, ao->plugin->name); return NULL; } - *length_r = length; - return data; + return frame; } -static bool -ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk) +static bool ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk) { - GError *error = NULL; + AVFrame *frame; + const uint8_t *data; + size_t size; assert(ao != NULL); assert(ao->filter != NULL); @@ -438,14 +423,11 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk) ao_plugin_send_tag(ao, chunk->tag); g_mutex_lock(ao->mutex); } + if (!chunk->frame) + return true; - size_t size; -#if GCC_CHECK_VERSION(4,7) - /* workaround -Wmaybe-uninitialized false positive */ - size = 0; -#endif - const char *data = ao_filter_chunk(ao, chunk, &size); - if (data == NULL) { + frame = ao_filter_chunk(ao, chunk); + if (!frame) { ao_close(ao, false); /* don't automatically reopen this device for 10 @@ -454,7 +436,11 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk) return false; } + data = frame->data[0]; + size = frame->nb_samples * ao->out_audio_format.channels * + av_get_bytes_per_sample(frame->format); while (size > 0 && ao->command == AO_COMMAND_NONE) { + GError *error = NULL; size_t nbytes; if (!ao_wait(ao)) @@ -466,7 +452,7 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk) if (nbytes == 0) { /* play()==0 means failure */ g_warning("\"%s\" [%s] failed to play: %s", - ao->name, ao->plugin->name, error->message); + ao->name, ao->plugin->name, error->message); g_error_free(error); ao_close(ao, false); @@ -475,6 +461,7 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk) 10 seconds */ assert(ao->fail_timer == NULL); ao->fail_timer = g_timer_new(); + av_frame_free(&frame); return false; } @@ -486,6 +473,7 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk) size -= nbytes; } + av_frame_free(&frame); return true; } diff --git a/src/pcm_convert.h b/src/pcm_convert.h index b9b1af17..9f4f1a68 100644 --- a/src/pcm_convert.h +++ b/src/pcm_convert.h @@ -22,7 +22,6 @@ #include "pcm_dsd.h" #include "pcm_dither.h" -#include "pcm_buffer.h" #include #include -- cgit v1.2.3