[FFmpeg-devel] [PATCH] lavfi/volume: add dynamic expression evaluation
Stefano Sabatini
stefasab at gmail.com
Sat Feb 23 01:00:40 CET 2013
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;
+ }
/* 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
More information about the ffmpeg-devel
mailing list