From: Alex Converse <alex.conve...@gmail.com> Signed-off-by: Justin Ruggles <justin.rugg...@gmail.com> --- doc/filters.texi | 34 +++++++ libavfilter/Makefile | 1 + libavfilter/af_alength.c | 215 ++++++++++++++++++++++++++++++++++++++++++++++ libavfilter/allfilters.c | 1 + libavfilter/version.h | 2 +- 5 files changed, 252 insertions(+), 1 deletions(-) create mode 100644 libavfilter/af_alength.c
diff --git a/doc/filters.texi b/doc/filters.texi index 34db2f4..bb55435 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -133,6 +133,40 @@ For example to force the output to either unsigned 8-bit or signed 16-bit stereo aformat=sample_fmts\=u8\,s16:channel_layouts\=stereo @end example +@section alength + +Shorten or optionally pad input audio to a target length. + +The filter accepts the following named parameters: +@table @option + +@item length +The target length in seconds + +@item pad +A boolean that controls whether or not to pad output with silence to reach +the target length. + +@item frame_size +The size of silent frames to be generated. + +@end table + +For example to limit output audio to at most 10 seconds +@example +alength=length=10 +@end example + +To force output audio to be 10 seconds +@example +alength=length=10:pad=1 +@end example + +To pad or trim audio to match video length +@example +avconv -i INPUT -af alength=pad=1 -shortest OUTPUT +@end example + @section amix Mixes multiple audio inputs into a single output. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 4a3331a..99f59a6 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -25,6 +25,7 @@ OBJS = allfilters.o \ video.o \ OBJS-$(CONFIG_AFORMAT_FILTER) += af_aformat.o +OBJS-$(CONFIG_ALENGTH_FILTER) += af_alength.o OBJS-$(CONFIG_AMIX_FILTER) += af_amix.o OBJS-$(CONFIG_ANULL_FILTER) += af_anull.o OBJS-$(CONFIG_ASHOWINFO_FILTER) += af_ashowinfo.o diff --git a/libavfilter/af_alength.c b/libavfilter/af_alength.c new file mode 100644 index 0000000..a104dd3 --- /dev/null +++ b/libavfilter/af_alength.c @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2012 Google, Inc. + * + * 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 + * Filter that shortens or lengthens an audio stream. + */ + +#include "libavutil/audioconvert.h" +#include "libavutil/common.h" +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" +#include "libavutil/parseutils.h" +#include "avfilter.h" +#include "audio.h" +#include "internal.h" +#include "formats.h" + +typedef struct ALengthContext { + const AVClass *class; + AVRational sample_rate; + int64_t next_pts; + char *length; + int64_t scaled_length; + int64_t final_pts; + int got_output; + int pad; + int frame_size; +} ALengthContext; + +#define OFFSET(x) offsetof(ALengthContext, x) +#define A AV_OPT_FLAG_AUDIO_PARAM + +static const AVOption alength_options[] = { + { "length", "Target length in seconds", OFFSET(length), AV_OPT_TYPE_STRING, { .str = "-1" }, 0, 0, A }, + { "pad", "Pad to length if too short", OFFSET(pad), AV_OPT_TYPE_INT, { 0 }, 0, 1, A }, + { "frame_size", "Frame size", OFFSET(frame_size), AV_OPT_TYPE_INT, { 1024 }, 0, INT_MAX, A }, + { NULL } +}; + +static const AVClass alength_class = { + .class_name = "alength filter", + .item_name = av_default_item_name, + .option = alength_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static av_cold int alength_init(AVFilterContext *ctx, const char *args) +{ + ALengthContext *s = ctx->priv; + int err; + + s->class = &alength_class; + av_opt_set_defaults(s); + + if ((err = av_set_options_string(s, args, "=", ":")) < 0) + return err; + av_opt_free(s); + + err = av_parse_time(&s->scaled_length, s->length, 1); + if (err) + return err; + if (s->scaled_length < 0) + s->scaled_length = AV_NOPTS_VALUE; + + s->next_pts = AV_NOPTS_VALUE; + s->final_pts = AV_NOPTS_VALUE; + av_log(ctx, AV_LOG_VERBOSE, "length:%s pad:%d frame_size:%d\n", + s->length, s->pad, s->frame_size); + + return 0; +} + +static int alength_config_input(AVFilterLink *inlink) +{ + ALengthContext *s = inlink->dst->priv; + s->sample_rate = (AVRational) { 1, inlink->sample_rate }; + if (s->scaled_length == AV_NOPTS_VALUE) + s->scaled_length = INT64_MAX; + else + s->scaled_length = av_rescale_q(s->scaled_length, + AV_TIME_BASE_Q, + s->sample_rate); + return 0; +} + +static int alength_config_output(AVFilterLink *outlink) +{ + ALengthContext *s = outlink->src->priv; + outlink->time_base = s->sample_rate; + return 0; +} + +static int alength_filter_frame(AVFilterLink *inlink, AVFilterBufferRef *buf) +{ + AVFilterContext *ctx = inlink->dst; + ALengthContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + int nb_samples = buf->audio->nb_samples; + int64_t in_pts = AV_NOPTS_VALUE; + + if (buf->pts != AV_NOPTS_VALUE) + in_pts = av_rescale_q(buf->pts, inlink->time_base, s->sample_rate); + + if (s->final_pts == AV_NOPTS_VALUE) { + if (in_pts != AV_NOPTS_VALUE) { + if (in_pts > 0 && s->scaled_length > INT64_MAX - in_pts) + s->final_pts = INT64_MAX; + else + s->final_pts = in_pts + s->scaled_length; + } else { + s->final_pts = s->scaled_length; + } + } + + if (in_pts != AV_NOPTS_VALUE) + s->next_pts = in_pts; + + if (s->next_pts == AV_NOPTS_VALUE) + s->next_pts = 0; + + if (s->next_pts >= s->final_pts) { + avfilter_unref_buffer(buf); + return AVERROR_EOF; + } else if (s->next_pts + nb_samples <= s->final_pts) { + s->next_pts += nb_samples; + s->got_output = 1; + return ff_filter_frame(outlink, buf); + } else { + nb_samples = s->final_pts - s->next_pts; + buf->audio->nb_samples = nb_samples; + s->next_pts += nb_samples; + s->got_output = 1; + return ff_filter_frame(outlink, buf); + } +} + +static int alength_request_frame(AVFilterLink *outlink) +{ + ALengthContext *s = outlink->src->priv; + AVFilterLink *inlink = outlink->src->inputs[0]; + int ret; + + s->got_output = 0; + do { + ret = ff_request_frame(inlink); + } while (!s->got_output && ret >= 0); + + if (s->final_pts == AV_NOPTS_VALUE) + s->final_pts = s->scaled_length; + + if (ret == AVERROR_EOF && s->pad && s->next_pts < s->final_pts) { + int nb_samples = FFMIN(s->frame_size, s->final_pts - s->next_pts); + int nb_channels = av_get_channel_layout_nb_channels(outlink->channel_layout); + AVFilterBufferRef *buf = ff_get_audio_buffer(outlink, AV_PERM_WRITE, + nb_samples); + if (!buf) + return AVERROR(ENOMEM); + av_samples_set_silence(buf->extended_data, 0, nb_samples, nb_channels, + buf->format); + buf->pts = s->next_pts; + if (s->next_pts != AV_NOPTS_VALUE) + s->next_pts += nb_samples; + return ff_filter_frame(outlink, buf); + } + + return ret; +} + +static const AVFilterPad avfilter_af_alength_inputs[] = { + { + .name = "default", + .config_props = alength_config_input, + .type = AVMEDIA_TYPE_AUDIO, + .filter_frame = alength_filter_frame, + }, + { NULL } +}; + +static const AVFilterPad avfilter_af_alength_outputs[] = { + { + .name = "default", + .config_props = alength_config_output, + .type = AVMEDIA_TYPE_AUDIO, + .request_frame = alength_request_frame, + }, + { NULL } +}; + +AVFilter avfilter_af_alength = { + .name = "alength", + .description = NULL_IF_CONFIG_SMALL("Shorten or lengthen an audio stream."), + .priv_size = sizeof(ALengthContext), + .init = alength_init, + + .inputs = avfilter_af_alength_inputs, + .outputs = avfilter_af_alength_outputs, +}; diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index f3ce91c..7547853 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -45,6 +45,7 @@ void avfilter_register_all(void) initialized = 1; REGISTER_FILTER(AFORMAT, aformat, af); + REGISTER_FILTER(ALENGTH, alength, af); REGISTER_FILTER(AMIX, amix, af); REGISTER_FILTER(ANULL, anull, af); REGISTER_FILTER(ASHOWINFO, ashowinfo, af); diff --git a/libavfilter/version.h b/libavfilter/version.h index c09d44b..b62f25c 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -29,7 +29,7 @@ #include "libavutil/avutil.h" #define LIBAVFILTER_VERSION_MAJOR 3 -#define LIBAVFILTER_VERSION_MINOR 3 +#define LIBAVFILTER_VERSION_MINOR 4 #define LIBAVFILTER_VERSION_MICRO 0 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ -- 1.7.1 _______________________________________________ libav-devel mailing list libav-devel@libav.org https://lists.libav.org/mailman/listinfo/libav-devel