diff --git a/doc/filters.texi b/doc/filters.texi index 0314bfaf20..f066657add 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -107,6 +107,32 @@ build. Below is a description of the currently available audio filters. +@section aformat + +Convert the input audio to one of the specified formats. The framework will +negotiate the most appropriate format to minimize conversions. + +The filter accepts the following named parameters: +@table @option + +@item sample_fmts +A comma-separated list of requested sample formats. + +@item sample_rates +A comma-separated list of requested sample rates. + +@item channel_layouts +A comma-separated list of requested channel layouts. + +@end table + +If a parameter is omitted, all values are allowed. + +For example to force the output to either unsigned 8-bit or signed 16-bit stereo: +@example +aformat=sample_fmts\=u8\,s16:channel_layouts\=stereo +@end example + @section anull Pass the audio source unchanged to the output. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 6a6bfd6811..df75bd5e74 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -22,6 +22,7 @@ OBJS = allfilters.o \ graphparser.o \ vf_scale.o \ +OBJS-$(CONFIG_AFORMAT_FILTER) += af_aformat.o OBJS-$(CONFIG_ANULL_FILTER) += af_anull.o OBJS-$(CONFIG_RESAMPLE_FILTER) += af_resample.o diff --git a/libavfilter/af_aformat.c b/libavfilter/af_aformat.c new file mode 100644 index 0000000000..84442d379e --- /dev/null +++ b/libavfilter/af_aformat.c @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2011 Mina Nagy Zaki + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * format audio filter + */ + +#include "libavutil/audioconvert.h" +#include "libavutil/avstring.h" +#include "libavutil/opt.h" + +#include "audio.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" + +typedef struct AFormatContext { + const AVClass *class; + + AVFilterFormats *formats; + AVFilterFormats *sample_rates; + AVFilterChannelLayouts *channel_layouts; + + char *formats_str; + char *sample_rates_str; + char *channel_layouts_str; +} AFormatContext; + +#define OFFSET(x) offsetof(AFormatContext, x) +#define A AV_OPT_FLAG_AUDIO_PARAM +static const AVOption options[] = { + { "sample_fmts", "A comma-separated list of sample formats.", OFFSET(formats_str), AV_OPT_TYPE_STRING, .flags = A }, + { "sample_rates", "A comma-separated list of sample rates.", OFFSET(sample_rates_str), AV_OPT_TYPE_STRING, .flags = A }, + { "channel_layouts", "A comma-separated list of channel layouts.", OFFSET(channel_layouts_str), AV_OPT_TYPE_STRING, .flags = A }, + { NULL }, +}; + +static const AVClass aformat_class = { + .class_name = "aformat filter", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +#define PARSE_FORMATS(str, type, list, add_to_list, get_fmt, none, desc) \ +do { \ + char *next, *cur = str; \ + while (cur) { \ + type fmt; \ + next = strchr(cur, ','); \ + if (next) \ + *next++ = 0; \ + \ + if ((fmt = get_fmt(cur)) == none) { \ + av_log(ctx, AV_LOG_ERROR, "Error parsing " desc ": %s.\n", cur);\ + ret = AVERROR(EINVAL); \ + goto fail; \ + } \ + add_to_list(&list, fmt); \ + \ + cur = next; \ + } \ +} while (0) + +static int get_sample_rate(const char *samplerate) +{ + int ret = strtol(samplerate, NULL, 0); + return FFMAX(ret, 0); +} + +static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) +{ + AFormatContext *s = ctx->priv; + int ret; + + if (!args) { + av_log(ctx, AV_LOG_ERROR, "No parameters supplied.\n"); + return AVERROR(EINVAL); + } + + s->class = &aformat_class; + av_opt_set_defaults(s); + + if ((ret = av_set_options_string(s, args, "=", ":")) < 0) { + av_log(ctx, AV_LOG_ERROR, "Error parsing options string '%s'.\n", args); + return ret; + } + + PARSE_FORMATS(s->formats_str, enum AVSampleFormat, s->formats, + avfilter_add_format, av_get_sample_fmt, AV_SAMPLE_FMT_NONE, "sample format"); + PARSE_FORMATS(s->sample_rates_str, int, s->sample_rates, avfilter_add_format, + get_sample_rate, 0, "sample rate"); + PARSE_FORMATS(s->channel_layouts_str, uint64_t, s->channel_layouts, + ff_add_channel_layout, av_get_channel_layout, 0, + "channel layout"); + +fail: + av_opt_free(s); + return ret; +} + +static int query_formats(AVFilterContext *ctx) +{ + AFormatContext *s = ctx->priv; + + avfilter_set_common_formats(ctx, s->formats ? s->formats : + avfilter_all_formats(AVMEDIA_TYPE_AUDIO)); + ff_set_common_samplerates(ctx, s->sample_rates ? s->sample_rates : + ff_all_samplerates()); + ff_set_common_channel_layouts(ctx, s->channel_layouts ? s->channel_layouts : + ff_all_channel_layouts()); + + return 0; +} + +AVFilter avfilter_af_aformat = { + .name = "aformat", + .description = NULL_IF_CONFIG_SMALL("Convert the input audio to one of the specified formats."), + .init = init, + .query_formats = query_formats, + .priv_size = sizeof(AFormatContext), + + .inputs = (AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .filter_samples = ff_null_filter_samples }, + { .name = NULL}}, + .outputs = (AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_AUDIO}, + { .name = NULL}}, +}; diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index c84b3f2587..4f5f852b8b 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -34,6 +34,7 @@ void avfilter_register_all(void) return; initialized = 1; + REGISTER_FILTER (AFORMAT, aformat, af); REGISTER_FILTER (ANULL, anull, af); REGISTER_FILTER (RESAMPLE, resample, af);