aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Khirnov <anton@khirnov.net>2013-04-23 10:59:41 +0200
committerAnton Khirnov <anton@khirnov.net>2015-02-20 09:18:26 +0100
commit7678c48a72c9750e52161fe023521c794068a72b (patch)
treea5e74060e70ca3a7f3ea25a4add287c58a9576a8
parent3a3d6f18b34dff582a3529de85ce540660031d85 (diff)
Switch filters to AVFrame.
Remove route filter for now, it should be replaced with lavfi later.
-rw-r--r--src/Makefile1
-rw-r--r--src/decoder_api.c1
-rw-r--r--src/decoder_internal.c1
-rw-r--r--src/filter/autoconvert_filter_plugin.c22
-rw-r--r--src/filter/chain_filter_plugin.c28
-rw-r--r--src/filter/convert_filter_plugin.c38
-rw-r--r--src/filter/normalize_filter_plugin.c31
-rw-r--r--src/filter/null_filter_plugin.c12
-rw-r--r--src/filter/replay_gain_filter_plugin.c120
-rw-r--r--src/filter/route_filter_plugin.c348
-rw-r--r--src/filter/volume_filter_plugin.c101
-rw-r--r--src/filter_plugin.c10
-rw-r--r--src/filter_plugin.h17
-rw-r--r--src/filter_registry.c2
-rw-r--r--src/output_finish.c2
-rw-r--r--src/output_init.c2
-rw-r--r--src/output_internal.h6
-rw-r--r--src/output_thread.c148
-rw-r--r--src/pcm_convert.h1
19 files changed, 240 insertions, 651 deletions
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 <assert.h>
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 <assert.h>
#include <string.h>
+#include <libavutil/frame.h>
+#include <libavutil/samplefmt.h>
+
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 <assert.h>
#include <string.h>
+#include <libavutil/channel_layout.h>
+#include <libavutil/frame.h>
+#include <libavutil/samplefmt.h>
+
#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 <assert.h>
-#include <string.h>
-#include <stdlib.h>
-
-
-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; c<number_of_copies; ++c) {
-
- // String and int representations of the source/destination
- gchar **sd;
- int source, dest;
-
- // Squeeze whitespace
- g_strstrip(tokens[c]);
-
- // Split the a>b 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; i<filter->min_output_channels; ++i)
- filter->sources[i] = -1;
-
- // Run through the spec again, and save the
- // actual mapping output <- input
- for (int c=0; c<number_of_copies; ++c) {
-
- // String and int representations of the source/destination
- gchar **sd;
- int source, dest;
-
- // Split the a>b 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; s<number_of_frames; ++s) {
-
- // Need to perform one copy per output channel
- for (unsigned int c=0; c<filter->min_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 <assert.h>
#include <string.h>
+#include <libavutil/channel_layout.h>
+#include <libavutil/frame.h>
+#include <libavutil/samplefmt.h>
+
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 <stddef.h>
+
#include <glib.h>
-#include <stdbool.h>
-#include <stddef.h>
+#include <libavutil/frame.h>
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 <glib.h>
@@ -141,11 +140,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 <stdlib.h>
#include <errno.h>
+#include <libavutil/common.h>
+#include <libavutil/frame.h>
+#include <libavutil/samplefmt.h>
+
#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 <libavutil/frame.h>
#include <libavutil/samplefmt.h>