From ba21499648bbffc5518a41dc01a51449b9871088 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2014 01:47:40 -0500 Subject: [PATCH 1/5] lavfi: add compand audio filter Signed-off-by: Anton Khirnov (cherry picked from commit 738f83582a3aaabb81309eacd4ab9c3d2acb4071) Conflicts: libavfilter/version.h --- Changelog | 4 + doc/filters.texi | 73 +++++ libavfilter/Makefile | 1 + libavfilter/af_compand.c | 587 +++++++++++++++++++++++++++++++++++++++ libavfilter/allfilters.c | 1 + libavfilter/version.h | 2 +- 6 files changed, 667 insertions(+), 1 deletion(-) create mode 100644 libavfilter/af_compand.c diff --git a/Changelog b/Changelog index bed6c315f5..427a617664 100644 --- a/Changelog +++ b/Changelog @@ -1,6 +1,10 @@ Entries are sorted chronologically from oldest to youngest within each release, releases are sorted from youngest to oldest. +version : +- compand audio filter + + version 10: - av_strnstr - support ID3v2 tags in ASF files diff --git a/doc/filters.texi b/doc/filters.texi index 8c83b4e7a2..5b47709c50 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -467,6 +467,79 @@ To fix a 5.1 WAV improperly encoded in AAC's native channel order avconv -i in.wav -filter 'channelmap=1|2|0|5|3|4:channel_layout=5.1' out.wav @end example +@section compand +Compress or expand audio dynamic range. + +A description of the accepted options follows. + +@table @option + +@item attacks +@item decays +Set list of times in seconds for each channel over which the instantaneous level +of the input signal is averaged to determine its volume. @var{attacks} refers to +increase of volume and @var{decays} refers to decrease of volume. For most +situations, the attack time (response to the audio getting louder) should be +shorter than the decay time because the human ear is more sensitive to sudden +loud audio than sudden soft audio. A typical value for attack is 0.3 seconds and +a typical value for decay is 0.8 seconds. + +@item points +Set list of points for the transfer function, specified in dB relative to the +maximum possible signal amplitude. Each key points list must be defined using +the following syntax: @code{x0/y0|x1/y1|x2/y2|....} + +The input values must be in strictly increasing order but the transfer function +does not have to be monotonically rising. The point @code{0/0} is assumed but +may be overridden (by @code{0/out-dBn}). Typical values for the transfer +function are @code{-70/-70|-60/-20}. + +@item soft-knee +Set the curve radius in dB for all joints. Defaults to 0.01. + +@item gain +Set additional gain in dB to be applied at all points on the transfer function. +This allows easy adjustment of the overall gain. Defaults to 0. + +@item volume +Set initial volume in dB to be assumed for each channel when filtering starts. +This permits the user to supply a nominal level initially, so that, for +example, a very large gain is not applied to initial signal levels before the +companding has begun to operate. A typical value for audio which is initially +quiet is -90 dB. Defaults to 0. + +@item delay +Set delay in seconds. The input audio is analyzed immediately, but audio is +delayed before being fed to the volume adjuster. Specifying a delay +approximately equal to the attack/decay times allows the filter to effectively +operate in predictive rather than reactive mode. Defaults to 0. + +@end table + +@subsection Examples + +@itemize +@item +Make music with both quiet and loud passages suitable for listening in a noisy +environment: +@example +compand=.3|.3:1|1:-90/-60|-60/-40|-40/-30|-20/-20:6:0:-90:0.2 +@end example + +@item +Noise gate for when the noise is at a lower level than the signal: +@example +compand=.1|.1:.2|.2:-900/-900|-50.1/-900|-50/-50:.01:0:-90:.1 +@end example + +@item +Here is another noise gate, this time for when the noise is at a higher level +than the signal (making it, in some ways, similar to squelch): +@example +compand=.1|.1:.1|.1:-45.1/-45.1|-45/-900|0/-900:.01:45:-90:.1 +@end example +@end itemize + @section join Join multiple input streams into one multi-channel stream. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 92c1561799..23dbd1d1c1 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -34,6 +34,7 @@ OBJS-$(CONFIG_ASYNCTS_FILTER) += af_asyncts.o OBJS-$(CONFIG_ATRIM_FILTER) += trim.o OBJS-$(CONFIG_CHANNELMAP_FILTER) += af_channelmap.o OBJS-$(CONFIG_CHANNELSPLIT_FILTER) += af_channelsplit.o +OBJS-$(CONFIG_COMPAND_FILTER) += af_compand.o OBJS-$(CONFIG_JOIN_FILTER) += af_join.o OBJS-$(CONFIG_RESAMPLE_FILTER) += af_resample.o OBJS-$(CONFIG_VOLUME_FILTER) += af_volume.o diff --git a/libavfilter/af_compand.c b/libavfilter/af_compand.c new file mode 100644 index 0000000000..19065944f9 --- /dev/null +++ b/libavfilter/af_compand.c @@ -0,0 +1,587 @@ +/* + * Copyright (c) 1999 Chris Bagwell + * Copyright (c) 1999 Nick Bailey + * Copyright (c) 2007 Rob Sykes + * Copyright (c) 2013 Paul B Mahol + * Copyright (c) 2014 Andrew Kelley + * + * 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 + * audio compand filter + */ + +#include + +#include "libavutil/channel_layout.h" +#include "libavutil/common.h" +#include "libavutil/mathematics.h" +#include "libavutil/mem.h" +#include "libavutil/opt.h" +#include "audio.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" + +typedef struct ChanParam { + float attack; + float decay; + float volume; +} ChanParam; + +typedef struct CompandSegment { + float x, y; + float a, b; +} CompandSegment; + +typedef struct CompandContext { + const AVClass *class; + int nb_channels; + int nb_segments; + char *attacks, *decays, *points; + CompandSegment *segments; + ChanParam *channels; + float in_min_lin; + float out_min_lin; + double curve_dB; + double gain_dB; + double initial_volume; + double delay; + AVFrame *delay_frame; + int delay_samples; + int delay_count; + int delay_index; + int64_t pts; + + int (*compand)(AVFilterContext *ctx, AVFrame *frame); +} CompandContext; + +#define OFFSET(x) offsetof(CompandContext, x) +#define A AV_OPT_FLAG_AUDIO_PARAM + +static const AVOption compand_options[] = { + { "attacks", "set time over which increase of volume is determined", OFFSET(attacks), AV_OPT_TYPE_STRING, { .str = "0.3" }, 0, 0, A }, + { "decays", "set time over which decrease of volume is determined", OFFSET(decays), AV_OPT_TYPE_STRING, { .str = "0.8" }, 0, 0, A }, + { "points", "set points of transfer function", OFFSET(points), AV_OPT_TYPE_STRING, { .str = "-70/-70|-60/-20" }, 0, 0, A }, + { "soft-knee", "set soft-knee", OFFSET(curve_dB), AV_OPT_TYPE_DOUBLE, { .dbl = 0.01 }, 0.01, 900, A }, + { "gain", "set output gain", OFFSET(gain_dB), AV_OPT_TYPE_DOUBLE, { .dbl = 0 }, -900, 900, A }, + { "volume", "set initial volume", OFFSET(initial_volume), AV_OPT_TYPE_DOUBLE, { .dbl = 0 }, -900, 0, A }, + { "delay", "set delay for samples before sending them to volume adjuster", OFFSET(delay), AV_OPT_TYPE_DOUBLE, { .dbl = 0 }, 0, 20, A }, + { NULL } +}; + +static const AVClass compand_class = { + .class_name = "compand filter", + .item_name = av_default_item_name, + .option = compand_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static av_cold int init(AVFilterContext *ctx) +{ + CompandContext *s = ctx->priv; + s->pts = AV_NOPTS_VALUE; + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + CompandContext *s = ctx->priv; + + av_freep(&s->channels); + av_freep(&s->segments); + av_frame_free(&s->delay_frame); +} + +static int query_formats(AVFilterContext *ctx) +{ + AVFilterChannelLayouts *layouts; + AVFilterFormats *formats; + static const enum AVSampleFormat sample_fmts[] = { + AV_SAMPLE_FMT_FLTP, + AV_SAMPLE_FMT_NONE + }; + + layouts = ff_all_channel_layouts(); + if (!layouts) + return AVERROR(ENOMEM); + ff_set_common_channel_layouts(ctx, layouts); + + formats = ff_make_format_list(sample_fmts); + if (!formats) + return AVERROR(ENOMEM); + ff_set_common_formats(ctx, formats); + + formats = ff_all_samplerates(); + if (!formats) + return AVERROR(ENOMEM); + ff_set_common_samplerates(ctx, formats); + + return 0; +} + +static void count_items(char *item_str, int *nb_items) +{ + char *p; + + *nb_items = 1; + for (p = item_str; *p; p++) { + if (*p == '|') + (*nb_items)++; + } +} + +static void update_volume(ChanParam *cp, float in) +{ + float delta = in - cp->volume; + + if (delta > 0.0) + cp->volume += delta * cp->attack; + else + cp->volume += delta * cp->decay; +} + +static float get_volume(CompandContext *s, float in_lin) +{ + CompandSegment *cs; + float in_log, out_log; + int i; + + if (in_lin < s->in_min_lin) + return s->out_min_lin; + + in_log = logf(in_lin); + + for (i = 1; i < s->nb_segments; i++) + if (in_log <= s->segments[i].x) + break; + cs = &s->segments[i - 1]; + in_log -= cs->x; + out_log = cs->y + in_log * (cs->a * in_log + cs->b); + + return expf(out_log); +} + +static int compand_nodelay(AVFilterContext *ctx, AVFrame *frame) +{ + CompandContext *s = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + const int channels = s->nb_channels; + const int nb_samples = frame->nb_samples; + AVFrame *out_frame; + int chan, i; + int err; + + if (av_frame_is_writable(frame)) { + out_frame = frame; + } else { + out_frame = ff_get_audio_buffer(inlink, nb_samples); + if (!out_frame) { + av_frame_free(&frame); + return AVERROR(ENOMEM); + } + err = av_frame_copy_props(out_frame, frame); + if (err < 0) { + av_frame_free(&out_frame); + av_frame_free(&frame); + return err; + } + } + + for (chan = 0; chan < channels; chan++) { + const float *src = (float *)frame->extended_data[chan]; + float *dst = (float *)out_frame->extended_data[chan]; + ChanParam *cp = &s->channels[chan]; + + for (i = 0; i < nb_samples; i++) { + update_volume(cp, fabs(src[i])); + + dst[i] = av_clipf(src[i] * get_volume(s, cp->volume), -1.0f, 1.0f); + } + } + + if (frame != out_frame) + av_frame_free(&frame); + + return ff_filter_frame(ctx->outputs[0], out_frame); +} + +#define MOD(a, b) (((a) >= (b)) ? (a) - (b) : (a)) + +static int compand_delay(AVFilterContext *ctx, AVFrame *frame) +{ + CompandContext *s = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + const int channels = s->nb_channels; + const int nb_samples = frame->nb_samples; + int chan, i, dindex = 0, oindex, count = 0; + AVFrame *out_frame = NULL; + int err; + + if (s->pts == AV_NOPTS_VALUE) { + s->pts = (frame->pts == AV_NOPTS_VALUE) ? 0 : frame->pts; + } + + for (chan = 0; chan < channels; chan++) { + AVFrame *delay_frame = s->delay_frame; + const float *src = (float *)frame->extended_data[chan]; + float *dbuf = (float *)delay_frame->extended_data[chan]; + ChanParam *cp = &s->channels[chan]; + float *dst; + + count = s->delay_count; + dindex = s->delay_index; + for (i = 0, oindex = 0; i < nb_samples; i++) { + const float in = src[i]; + update_volume(cp, fabs(in)); + + if (count >= s->delay_samples) { + if (!out_frame) { + out_frame = ff_get_audio_buffer(inlink, nb_samples - i); + if (!out_frame) { + av_frame_free(&frame); + return AVERROR(ENOMEM); + } + err = av_frame_copy_props(out_frame, frame); + if (err < 0) { + av_frame_free(&out_frame); + av_frame_free(&frame); + return err; + } + out_frame->pts = s->pts; + s->pts += av_rescale_q(nb_samples - i, + (AVRational){ 1, inlink->sample_rate }, + inlink->time_base); + } + + dst = (float *)out_frame->extended_data[chan]; + dst[oindex++] = av_clipf(dbuf[dindex] * + get_volume(s, cp->volume), -1.0f, 1.0f); + } else { + count++; + } + + dbuf[dindex] = in; + dindex = MOD(dindex + 1, s->delay_samples); + } + } + + s->delay_count = count; + s->delay_index = dindex; + + av_frame_free(&frame); + return out_frame ? ff_filter_frame(ctx->outputs[0], out_frame) : 0; +} + +static int compand_drain(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + CompandContext *s = ctx->priv; + const int channels = s->nb_channels; + AVFrame *frame = NULL; + int chan, i, dindex; + + /* 2048 is to limit output frame size during drain */ + frame = ff_get_audio_buffer(outlink, FFMIN(2048, s->delay_count)); + if (!frame) + return AVERROR(ENOMEM); + frame->pts = s->pts; + s->pts += av_rescale_q(frame->nb_samples, + (AVRational){ 1, outlink->sample_rate }, outlink->time_base); + + for (chan = 0; chan < channels; chan++) { + AVFrame *delay_frame = s->delay_frame; + float *dbuf = (float *)delay_frame->extended_data[chan]; + float *dst = (float *)frame->extended_data[chan]; + ChanParam *cp = &s->channels[chan]; + + dindex = s->delay_index; + for (i = 0; i < frame->nb_samples; i++) { + dst[i] = av_clipf(dbuf[dindex] * get_volume(s, cp->volume), + -1.0f, 1.0f); + dindex = MOD(dindex + 1, s->delay_samples); + } + } + s->delay_count -= frame->nb_samples; + s->delay_index = dindex; + + return ff_filter_frame(outlink, frame); +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + CompandContext *s = ctx->priv; + const int sample_rate = outlink->sample_rate; + double radius = s->curve_dB * M_LN10 / 20.0; + char *p, *saveptr = NULL; + const int channels = + av_get_channel_layout_nb_channels(outlink->channel_layout); + int nb_attacks, nb_decays, nb_points; + int new_nb_items, num; + int i; + int err; + + + count_items(s->attacks, &nb_attacks); + count_items(s->decays, &nb_decays); + count_items(s->points, &nb_points); + + if (channels <= 0) { + av_log(ctx, AV_LOG_ERROR, "Invalid number of channels: %d\n", channels); + return AVERROR(EINVAL); + } + + if (nb_attacks > channels || nb_decays > channels) { + av_log(ctx, AV_LOG_ERROR, + "Number of attacks/decays bigger than number of channels.\n"); + return AVERROR(EINVAL); + } + + uninit(ctx); + + s->nb_channels = channels; + s->channels = av_mallocz_array(channels, sizeof(*s->channels)); + s->nb_segments = (nb_points + 4) * 2; + s->segments = av_mallocz_array(s->nb_segments, sizeof(*s->segments)); + + if (!s->channels || !s->segments) { + uninit(ctx); + return AVERROR(ENOMEM); + } + + p = s->attacks; + for (i = 0, new_nb_items = 0; i < nb_attacks; i++) { + char *tstr = strtok_r(p, "|", &saveptr); + p = NULL; + new_nb_items += sscanf(tstr, "%f", &s->channels[i].attack) == 1; + if (s->channels[i].attack < 0) { + uninit(ctx); + return AVERROR(EINVAL); + } + } + nb_attacks = new_nb_items; + + p = s->decays; + for (i = 0, new_nb_items = 0; i < nb_decays; i++) { + char *tstr = strtok_r(p, "|", &saveptr); + p = NULL; + new_nb_items += sscanf(tstr, "%f", &s->channels[i].decay) == 1; + if (s->channels[i].decay < 0) { + uninit(ctx); + return AVERROR(EINVAL); + } + } + nb_decays = new_nb_items; + + if (nb_attacks != nb_decays) { + av_log(ctx, AV_LOG_ERROR, + "Number of attacks %d differs from number of decays %d.\n", + nb_attacks, nb_decays); + uninit(ctx); + return AVERROR(EINVAL); + } + +#define S(x) s->segments[2 * ((x) + 1)] + p = s->points; + for (i = 0, new_nb_items = 0; i < nb_points; i++) { + char *tstr = strtok_r(p, "|", &saveptr); + p = NULL; + if (sscanf(tstr, "%f/%f", &S(i).x, &S(i).y) != 2) { + av_log(ctx, AV_LOG_ERROR, + "Invalid and/or missing input/output value.\n"); + uninit(ctx); + return AVERROR(EINVAL); + } + if (i && S(i - 1).x > S(i).x) { + av_log(ctx, AV_LOG_ERROR, + "Transfer function input values must be increasing.\n"); + uninit(ctx); + return AVERROR(EINVAL); + } + S(i).y -= S(i).x; + av_log(ctx, AV_LOG_DEBUG, "%d: x=%f y=%f\n", i, S(i).x, S(i).y); + new_nb_items++; + } + num = new_nb_items; + + /* Add 0,0 if necessary */ + if (num == 0 || S(num - 1).x) + num++; + +#undef S +#define S(x) s->segments[2 * (x)] + /* Add a tail off segment at the start */ + S(0).x = S(1).x - 2 * s->curve_dB; + S(0).y = S(1).y; + num++; + + /* Join adjacent colinear segments */ + for (i = 2; i < num; i++) { + double g1 = (S(i - 1).y - S(i - 2).y) * (S(i - 0).x - S(i - 1).x); + double g2 = (S(i - 0).y - S(i - 1).y) * (S(i - 1).x - S(i - 2).x); + int j; + + /* here we purposefully lose precision so that we can compare floats */ + if (fabs(g1 - g2)) + continue; + num--; + for (j = --i; j < num; j++) + S(j) = S(j + 1); + } + + for (i = 0; !i || s->segments[i - 2].x; i += 2) { + s->segments[i].y += s->gain_dB; + s->segments[i].x *= M_LN10 / 20; + s->segments[i].y *= M_LN10 / 20; + } + +#define L(x) s->segments[i - (x)] + for (i = 4; s->segments[i - 2].x; i += 2) { + double x, y, cx, cy, in1, in2, out1, out2, theta, len, r; + + L(4).a = 0; + L(4).b = (L(2).y - L(4).y) / (L(2).x - L(4).x); + + L(2).a = 0; + L(2).b = (L(0).y - L(2).y) / (L(0).x - L(2).x); + + theta = atan2(L(2).y - L(4).y, L(2).x - L(4).x); + len = sqrt(pow(L(2).x - L(4).x, 2.) + pow(L(2).y - L(4).y, 2.)); + r = FFMIN(radius, len); + L(3).x = L(2).x - r * cos(theta); + L(3).y = L(2).y - r * sin(theta); + + theta = atan2(L(0).y - L(2).y, L(0).x - L(2).x); + len = sqrt(pow(L(0).x - L(2).x, 2.) + pow(L(0).y - L(2).y, 2.)); + r = FFMIN(radius, len / 2); + x = L(2).x + r * cos(theta); + y = L(2).y + r * sin(theta); + + cx = (L(3).x + L(2).x + x) / 3; + cy = (L(3).y + L(2).y + y) / 3; + + L(2).x = x; + L(2).y = y; + + in1 = cx - L(3).x; + out1 = cy - L(3).y; + in2 = L(2).x - L(3).x; + out2 = L(2).y - L(3).y; + L(3).a = (out2 / in2 - out1 / in1) / (in2 - in1); + L(3).b = out1 / in1 - L(3).a * in1; + } + L(3).x = 0; + L(3).y = L(2).y; + + s->in_min_lin = exp(s->segments[1].x); + s->out_min_lin = exp(s->segments[1].y); + + for (i = 0; i < channels; i++) { + ChanParam *cp = &s->channels[i]; + + if (cp->attack > 1.0 / sample_rate) + cp->attack = 1.0 - exp(-1.0 / (sample_rate * cp->attack)); + else + cp->attack = 1.0; + if (cp->decay > 1.0 / sample_rate) + cp->decay = 1.0 - exp(-1.0 / (sample_rate * cp->decay)); + else + cp->decay = 1.0; + cp->volume = pow(10.0, s->initial_volume / 20); + } + + s->delay_samples = s->delay * sample_rate; + if (s->delay_samples <= 0) { + s->compand = compand_nodelay; + return 0; + } + + s->delay_frame = av_frame_alloc(); + if (!s->delay_frame) { + uninit(ctx); + return AVERROR(ENOMEM); + } + + s->delay_frame->format = outlink->format; + s->delay_frame->nb_samples = s->delay_samples; + s->delay_frame->channel_layout = outlink->channel_layout; + + err = av_frame_get_buffer(s->delay_frame, 32); + if (err) + return err; + + s->compand = compand_delay; + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + AVFilterContext *ctx = inlink->dst; + CompandContext *s = ctx->priv; + + return s->compand(ctx, frame); +} + +static int request_frame(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + CompandContext *s = ctx->priv; + int ret; + + ret = ff_request_frame(ctx->inputs[0]); + + if (ret == AVERROR_EOF && s->delay_count) + ret = compand_drain(outlink); + + return ret; +} + +static const AVFilterPad compand_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .filter_frame = filter_frame, + }, + { NULL } +}; + +static const AVFilterPad compand_outputs[] = { + { + .name = "default", + .request_frame = request_frame, + .config_props = config_output, + .type = AVMEDIA_TYPE_AUDIO, + }, + { NULL } +}; + + +AVFilter ff_af_compand = { + .name = "compand", + .description = NULL_IF_CONFIG_SMALL( + "Compress or expand audio dynamic range."), + .query_formats = query_formats, + .priv_size = sizeof(CompandContext), + .priv_class = &compand_class, + .init = init, + .uninit = uninit, + .inputs = compand_inputs, + .outputs = compand_outputs, +}; diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 9702a0a9cb..e47a22e6d6 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -54,6 +54,7 @@ void avfilter_register_all(void) REGISTER_FILTER(ATRIM, atrim, af); REGISTER_FILTER(CHANNELMAP, channelmap, af); REGISTER_FILTER(CHANNELSPLIT, channelsplit, af); + REGISTER_FILTER(COMPAND, compand, af); REGISTER_FILTER(JOIN, join, af); REGISTER_FILTER(RESAMPLE, resample, af); REGISTER_FILTER(VOLUME, volume, af); diff --git a/libavfilter/version.h b/libavfilter/version.h index 1684aa5e7b..f09b5012b1 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -30,7 +30,7 @@ #include "libavutil/version.h" #define LIBAVFILTER_VERSION_MAJOR 4 -#define LIBAVFILTER_VERSION_MINOR 1 +#define LIBAVFILTER_VERSION_MINOR 2 #define LIBAVFILTER_VERSION_MICRO 0 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ From 15ae305007c09a958db27e902e5fbff06753a01c Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Wed, 26 Feb 2014 13:44:53 +0100 Subject: [PATCH 2/5] af_compand: add a dependency on strtok_r (cherry picked from commit 291e49d4e7db4b982621d7a25e258f898cfc3217) --- configure | 1 + 1 file changed, 1 insertion(+) diff --git a/configure b/configure index 4c35ab9c29..8ece5eb7ea 100755 --- a/configure +++ b/configure @@ -2008,6 +2008,7 @@ unix_protocol_select="network" # filters blackframe_filter_deps="gpl" boxblur_filter_deps="gpl" +compand_filter_deps="strtok_r" cropdetect_filter_deps="gpl" delogo_filter_deps="gpl" drawtext_filter_deps="libfreetype" From 031d3b66c2ea3b338cb7ce437bce47a8a4930ebb Mon Sep 17 00:00:00 2001 From: Hendrik Leppkes Date: Fri, 23 Dec 2011 00:14:32 +0100 Subject: [PATCH 3/5] latm: Always reconfigure if no extradata was set previously AAC LOAS can have new audio config objects in the stream itself. Make sure the decoder reconfigures itself when the first one arrives midstream. Bug-Id: 644 Signed-off-by: Luca Barbato (cherry picked from commit 3aca10bf762a94d7de555cedf1ff0e4f6792bf41) --- libavcodec/aacdec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libavcodec/aacdec.c b/libavcodec/aacdec.c index a3652098db..e25838fca0 100644 --- a/libavcodec/aacdec.c +++ b/libavcodec/aacdec.c @@ -3030,7 +3030,8 @@ static int latm_decode_audio_specific_config(struct LATMContext *latmctx, if (bits_consumed < 0) return AVERROR_INVALIDDATA; - if (ac->oc[1].m4ac.sample_rate != m4ac.sample_rate || + if (!latmctx->initialized || + ac->oc[1].m4ac.sample_rate != m4ac.sample_rate || ac->oc[1].m4ac.chan_config != m4ac.chan_config) { av_log(avctx, AV_LOG_INFO, "audio config changed\n"); From 00d5ff64315adb18847ba5bd830c49f37a95df36 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Wed, 26 Feb 2014 22:37:06 +0100 Subject: [PATCH 4/5] af_compand: replace strtok_r() with av_get_token() (cherry picked from commit bc6461c2861b7d482a037d3b3e2b44ad48805fa0) --- configure | 1 - libavfilter/af_compand.c | 32 ++++++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/configure b/configure index 8ece5eb7ea..4c35ab9c29 100755 --- a/configure +++ b/configure @@ -2008,7 +2008,6 @@ unix_protocol_select="network" # filters blackframe_filter_deps="gpl" boxblur_filter_deps="gpl" -compand_filter_deps="strtok_r" cropdetect_filter_deps="gpl" delogo_filter_deps="gpl" drawtext_filter_deps="libfreetype" diff --git a/libavfilter/af_compand.c b/libavfilter/af_compand.c index 19065944f9..a6692bc37f 100644 --- a/libavfilter/af_compand.c +++ b/libavfilter/af_compand.c @@ -29,6 +29,7 @@ #include +#include "libavutil/avstring.h" #include "libavutil/channel_layout.h" #include "libavutil/common.h" #include "libavutil/mathematics.h" @@ -330,7 +331,7 @@ static int config_output(AVFilterLink *outlink) CompandContext *s = ctx->priv; const int sample_rate = outlink->sample_rate; double radius = s->curve_dB * M_LN10 / 20.0; - char *p, *saveptr = NULL; + const char *p; const int channels = av_get_channel_layout_nb_channels(outlink->channel_layout); int nb_attacks, nb_decays, nb_points; @@ -368,25 +369,34 @@ static int config_output(AVFilterLink *outlink) p = s->attacks; for (i = 0, new_nb_items = 0; i < nb_attacks; i++) { - char *tstr = strtok_r(p, "|", &saveptr); - p = NULL; + char *tstr = av_get_token(&p, "|"); + if (!tstr) + return AVERROR(ENOMEM); + new_nb_items += sscanf(tstr, "%f", &s->channels[i].attack) == 1; + av_freep(&tstr); if (s->channels[i].attack < 0) { uninit(ctx); return AVERROR(EINVAL); } + if (*p) + p++; } nb_attacks = new_nb_items; p = s->decays; for (i = 0, new_nb_items = 0; i < nb_decays; i++) { - char *tstr = strtok_r(p, "|", &saveptr); - p = NULL; + char *tstr = av_get_token(&p, "|"); + if (!tstr) + return AVERROR(ENOMEM); new_nb_items += sscanf(tstr, "%f", &s->channels[i].decay) == 1; + av_freep(&tstr); if (s->channels[i].decay < 0) { uninit(ctx); return AVERROR(EINVAL); } + if (*p) + p++; } nb_decays = new_nb_items; @@ -401,9 +411,13 @@ static int config_output(AVFilterLink *outlink) #define S(x) s->segments[2 * ((x) + 1)] p = s->points; for (i = 0, new_nb_items = 0; i < nb_points; i++) { - char *tstr = strtok_r(p, "|", &saveptr); - p = NULL; - if (sscanf(tstr, "%f/%f", &S(i).x, &S(i).y) != 2) { + char *tstr = av_get_token(&p, "|"); + if (!tstr) + return AVERROR(ENOMEM); + + err = sscanf(tstr, "%f/%f", &S(i).x, &S(i).y); + av_freep(&tstr); + if (err != 2) { av_log(ctx, AV_LOG_ERROR, "Invalid and/or missing input/output value.\n"); uninit(ctx); @@ -418,6 +432,8 @@ static int config_output(AVFilterLink *outlink) S(i).y -= S(i).x; av_log(ctx, AV_LOG_DEBUG, "%d: x=%f y=%f\n", i, S(i).x, S(i).y); new_nb_items++; + if (*p) + p++; } num = new_nb_items; From 5df52b0131d3d4d804ad6e221bc9a2cd8b201ef2 Mon Sep 17 00:00:00 2001 From: Keiji Costantini Date: Sat, 1 Mar 2014 18:17:04 +0000 Subject: [PATCH 5/5] ituh263: reject b-frame with pp_time = 0 Avoid a division by 0 in ff_mpeg4_set_one_direct_mv. Sample-Id: 00000168-google Reported-by: Mateusz "j00ru" Jurczyk and Gynvael Coldwind Signed-off-by: Vittorio Giovara (cherry picked from commit 9514440337875e0c63b409abcd616b68c518283f) --- libavcodec/ituh263dec.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libavcodec/ituh263dec.c b/libavcodec/ituh263dec.c index e36521062f..98c8cfc137 100644 --- a/libavcodec/ituh263dec.c +++ b/libavcodec/ituh263dec.c @@ -750,6 +750,8 @@ int ff_h263_decode_mb(MpegEncContext *s, } if(IS_DIRECT(mb_type)){ + if (!s->pp_time) + return AVERROR_INVALIDDATA; s->mv_dir = MV_DIR_FORWARD | MV_DIR_BACKWARD | MV_DIRECT; mb_type |= ff_mpeg4_set_direct_mv(s, 0, 0); }else{