[FFmpeg-devel] [PATCH] lavfi/volume: add dynamic expression evaluation
Paul B Mahol
onemda at gmail.com
Sat Feb 23 12:12:21 CET 2013
On 2/23/13, Stefano Sabatini <stefasab at gmail.com> wrote:
> TODO: update docs, bump micro
> ---
> libavfilter/af_volume.c | 120
> ++++++++++++++++++++++++++++++++++++-----------
> libavfilter/af_volume.h | 20 ++++++++
> 2 files changed, 113 insertions(+), 27 deletions(-)
>
> diff --git a/libavfilter/af_volume.c b/libavfilter/af_volume.c
> index 5ffa1fe..87898c8 100644
> --- a/libavfilter/af_volume.c
> +++ b/libavfilter/af_volume.c
> @@ -29,6 +29,7 @@
> #include "libavutil/eval.h"
> #include "libavutil/float_dsp.h"
> #include "libavutil/opt.h"
> +#include "libavutil/time.h"
> #include "audio.h"
> #include "avfilter.h"
> #include "formats.h"
> @@ -39,13 +40,28 @@ static const char *precision_str[] = {
> "fixed", "float", "double"
> };
>
> +static const char *const var_names[] = {
> + "N", ///< frame number (starting at zero)
> + "NB_CONSUMED_SAMPLES", ///< number of samples consumed by the filter
> (only audio)
> + "NB_SAMPLES", ///< number of samples in the current frame (only
> audio)
> + "POS", ///< original position in the file of the frame
> + "PTS", ///< original pts in the file of the frame
> + "RTC_STARTT", ///< start wallclock (RTC) time in micro seconds
> + "SAMPLE_RATE", ///< sample rate (only audio)
> + "STARTPTS", ///< PTS at start of stream
> + "STARTT", ///< time at start of stream
> + "T", ///< original time in the file of the frame
> + "TB", ///< timebase
> + NULL
> +};
> +
> #define OFFSET(x) offsetof(VolumeContext, x)
> #define A AV_OPT_FLAG_AUDIO_PARAM
> #define F AV_OPT_FLAG_FILTERING_PARAM
>
> static const AVOption volume_options[] = {
> - { "volume", "set volume adjustment",
> - OFFSET(volume), AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 }, 0,
> 0x7fffff, A|F },
> + { "volume", "set volume adjustment expression",
> + OFFSET(volume_expr), AV_OPT_TYPE_STRING, { .str = "1.0" },
> .flags = A|F },
> { "precision", "select mathematical precision",
> OFFSET(precision), AV_OPT_TYPE_INT, { .i64 = PRECISION_FLOAT },
> PRECISION_FIXED, PRECISION_DOUBLE, A|F, "precision" },
> { "fixed", "select 8-bit fixed-point", 0, AV_OPT_TYPE_CONST, {
> .i64 = PRECISION_FIXED }, INT_MIN, INT_MAX, A|F, "precision" },
> @@ -68,21 +84,25 @@ static av_cold int init(AVFilterContext *ctx, const char
> *args)
> if ((ret = av_opt_set_from_string(vol, args, shorthand, "=", ":")) <
> 0)
> return ret;
>
> - if (vol->precision == PRECISION_FIXED) {
> - vol->volume_i = (int)(vol->volume * 256 + 0.5);
> - vol->volume = vol->volume_i / 256.0;
> - av_log(ctx, AV_LOG_VERBOSE, "volume:(%d/256)(%f)(%1.2fdB)
> precision:fixed\n",
> - vol->volume_i, vol->volume, 20.0*log(vol->volume)/M_LN10);
> - } else {
> - av_log(ctx, AV_LOG_VERBOSE, "volume:(%f)(%1.2fdB) precision:%s\n",
> - vol->volume, 20.0*log(vol->volume)/M_LN10,
> - precision_str[vol->precision]);
> + if ((ret = av_expr_parse(&vol->volume_pexpr,vol->volume_expr,
> + var_names, NULL, NULL, NULL, NULL, 0, ctx)) <
> 0) {
> + av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n",
> vol->volume_expr);
> + return ret;
> }
>
> - av_opt_free(vol);
> + vol->var_values[VAR_N ] = 0.0;
> + vol->var_values[VAR_STARTPTS] = vol->var_values[VAR_STARTT] = NAN;
> +
> return ret;
> }
>
> +static av_cold void uninit(AVFilterContext *ctx)
> +{
> + VolumeContext *vol = ctx->priv;
> + av_expr_free(vol->volume_pexpr);
> + av_opt_free(vol);
> +}
> +
> static int query_formats(AVFilterContext *ctx)
> {
> VolumeContext *vol = ctx->priv;
> @@ -182,18 +202,6 @@ static void volume_init(VolumeContext *vol)
> vol->samples_align = 1;
>
> switch (av_get_packed_sample_fmt(vol->sample_fmt)) {
> - case AV_SAMPLE_FMT_U8:
> - if (vol->volume_i < 0x1000000)
> - vol->scale_samples = scale_samples_u8_small;
> - else
> - vol->scale_samples = scale_samples_u8;
> - break;
> - case AV_SAMPLE_FMT_S16:
> - if (vol->volume_i < 0x10000)
> - vol->scale_samples = scale_samples_s16_small;
> - else
> - vol->scale_samples = scale_samples_s16;
> - break;
> case AV_SAMPLE_FMT_S32:
> vol->scale_samples = scale_samples_s32;
> break;
> @@ -221,20 +229,73 @@ static int config_output(AVFilterLink *outlink)
> vol->channels =
> av_get_channel_layout_nb_channels(inlink->channel_layout);
> vol->planes = av_sample_fmt_is_planar(inlink->format) ?
> vol->channels : 1;
>
> - volume_init(vol);
> + vol->var_values[VAR_TB] = av_q2d(inlink->time_base);
> + vol->var_values[VAR_RTC_STARTT] = av_gettime();
> + vol->var_values[VAR_SAMPLE_RATE] = inlink->sample_rate;
> +
> + av_log(inlink->src, AV_LOG_VERBOSE, "TB:%f SAMPLE_RATE:%f
> RTC_STARTT:%f\n",
> + vol->var_values[VAR_TB],
> + vol->var_values[VAR_SAMPLE_RATE],
> + vol->var_values[VAR_RTC_STARTT]);
>
> + volume_init(vol);
> return 0;
> }
>
> +#define D2TS(d) (isnan(d) ? AV_NOPTS_VALUE : (int64_t)(d))
> +#define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts))
> +#define TS2T(ts, tb) ((ts) == AV_NOPTS_VALUE ? NAN :
> (double)(ts)*av_q2d(tb))
> +
> static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *buf)
> {
> + AVFilterContext *ctx = inlink->dst;
> VolumeContext *vol = inlink->dst->priv;
> AVFilterLink *outlink = inlink->dst->outputs[0];
> int nb_samples = buf->audio->nb_samples;
> AVFilterBufferRef *out_buf;
>
> - if (vol->volume == 1.0 || vol->volume_i == 256)
> - return ff_filter_frame(outlink, buf);
> + if (isnan(vol->var_values[VAR_STARTPTS])) {
> + vol->var_values[VAR_STARTPTS] = TS2D(buf->pts);
> + vol->var_values[VAR_STARTT ] = TS2T(buf->pts, inlink->time_base);
> + }
> + vol->var_values[VAR_PTS] = TS2D(buf->pts);
> + vol->var_values[VAR_T ] = TS2T(buf->pts, inlink->time_base);
> + vol->var_values[VAR_POS] = buf->pos == -1 ? NAN : buf->pos;
> + vol->volume = av_expr_eval(vol->volume_pexpr, vol->var_values, NULL);
> + if (isnan(vol->volume)) {
> + av_log(ctx, AV_LOG_WARNING, "Invalid value NaN for volume, setting
> to 0\n");
> + vol->volume = 0;
> + }
> +
> + if (vol->precision == PRECISION_FIXED) {
> + vol->volume_i = (int)(vol->volume * 256 + 0.5);
> + vol->volume = vol->volume_i / 256.0;
> + av_log(ctx, AV_LOG_DEBUG, "volume_i:%d/256 volume:%f
> volume_db:%1.2f\n",
> + vol->volume_i, vol->volume, 20.0*log(vol->volume)/M_LN10);
> + } else {
> + av_log(ctx, AV_LOG_DEBUG, "volume:%f volume_db:%1.2f\n",
> + vol->volume, 20.0*log(vol->volume)/M_LN10);
> + }
> +
> + if (vol->volume == 1.0 || vol->volume_i == 256) {
> + out_buf = buf;
> + goto end;
> + }
> +
> + switch (av_get_packed_sample_fmt(vol->sample_fmt)) {
> + case AV_SAMPLE_FMT_U8:
> + if (vol->volume_i < 0x1000000)
> + vol->scale_samples = scale_samples_u8_small;
> + else
> + vol->scale_samples = scale_samples_u8;
> + break;
> + case AV_SAMPLE_FMT_S16:
> + if (vol->volume_i < 0x10000)
> + vol->scale_samples = scale_samples_s16_small;
> + else
> + vol->scale_samples = scale_samples_s16;
> + break;
> + }
What about other sample formats?
>
> /* do volume scaling in-place if input buffer is writable */
> if (buf->perms & AV_PERM_WRITE) {
> @@ -278,6 +339,10 @@ static int filter_frame(AVFilterLink *inlink,
> AVFilterBufferRef *buf)
> if (buf != out_buf)
> avfilter_unref_buffer(buf);
>
> +end:
> + vol->var_values[VAR_N] += 1.0;
> + vol->var_values[VAR_NB_CONSUMED_SAMPLES] += buf->audio->nb_samples;
> +
> return ff_filter_frame(outlink, out_buf);
> }
>
> @@ -305,6 +370,7 @@ AVFilter avfilter_af_volume = {
> .query_formats = query_formats,
> .priv_size = sizeof(VolumeContext),
> .init = init,
> + .uninit = uninit,
> .inputs = avfilter_af_volume_inputs,
> .outputs = avfilter_af_volume_outputs,
> .priv_class = &volume_class,
> diff --git a/libavfilter/af_volume.h b/libavfilter/af_volume.h
> index bd7932e..38e43a8 100644
> --- a/libavfilter/af_volume.h
> +++ b/libavfilter/af_volume.h
> @@ -25,6 +25,7 @@
> #define AVFILTER_AF_VOLUME_H
>
> #include "libavutil/common.h"
> +#include "libavutil/eval.h"
> #include "libavutil/float_dsp.h"
> #include "libavutil/opt.h"
> #include "libavutil/samplefmt.h"
> @@ -35,10 +36,29 @@ enum PrecisionType {
> PRECISION_DOUBLE,
> };
>
> +enum VolumeVarName {
> + VAR_N,
> + VAR_NB_CONSUMED_SAMPLES,
> + VAR_NB_SAMPLES,
> + VAR_POS,
> + VAR_PTS,
> + VAR_RTC_STARTT,
> + VAR_SAMPLE_RATE,
> + VAR_STARTPTS,
> + VAR_STARTT,
> + VAR_T,
> + VAR_TB,
> + VAR_VARS_NB
> +};
> +
> typedef struct VolumeContext {
> const AVClass *class;
> AVFloatDSPContext fdsp;
> enum PrecisionType precision;
> + const char *volume_expr;
> + AVExpr *volume_pexpr;
> + double var_values[VAR_VARS_NB];
> +
> double volume;
> int volume_i;
> int channels;
> --
> 1.7.9.5
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
More information about the ffmpeg-devel
mailing list