diff mbox series

[FFmpeg-devel,2/2] lavfi/setpts: introduce rand() function in expression

Message ID 20231228003839.860894-1-stefasab@gmail.com
State New
Headers show
Series [FFmpeg-devel,1/2] lavfi/setpts: use macro to access variables array | expand

Checks

Context Check Description
yinshiyou/make_loongarch64 success Make finished
yinshiyou/make_fate_loongarch64 success Make fate finished
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Stefano Sabatini Dec. 28, 2023, 12:38 a.m. UTC
This is useful to simulate random jitter.
---
 Changelog            |  1 +
 doc/filters.texi     | 10 +++++++++-
 libavfilter/setpts.c | 39 +++++++++++++++++++++++++++++++++------
 3 files changed, 43 insertions(+), 7 deletions(-)

Comments

Andreas Rheinhardt Dec. 28, 2023, 11:02 a.m. UTC | #1
Stefano Sabatini:
> This is useful to simulate random jitter.
> ---
>  Changelog            |  1 +
>  doc/filters.texi     | 10 +++++++++-
>  libavfilter/setpts.c | 39 +++++++++++++++++++++++++++++++++------
>  3 files changed, 43 insertions(+), 7 deletions(-)
> 
> diff --git a/Changelog b/Changelog
> index 424bfc11af..ed01c53264 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -15,6 +15,7 @@ version <next>:
>  - tiltandshift filter
>  - qrencode filter and qrencodesrc source
>  - quirc filter
> +- lavfi/setpts: introduce rand() function in expression
>  
>  version 6.1:
>  - libaribcaption decoder
> diff --git a/doc/filters.texi b/doc/filters.texi
> index d1f95b9781..1d9a5d6c7d 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -30946,7 +30946,7 @@ The expression which is evaluated for each frame to construct its timestamp.
>  @end table
>  
>  The expression is evaluated through the eval API and can contain the following
> -constants:
> +constants and functions:
>  
>  @table @option
>  @item FRAME_RATE, FR
> @@ -31010,6 +31010,8 @@ The timebase of the input timestamps.
>  @item T_CHANGE
>  Time of the first frame after command was applied or time of the first frame if no commands.
>  
> +@item rand(min, max)
> +a random number included between min and max
>  @end table
>  
>  @subsection Examples
> @@ -31021,6 +31023,12 @@ Start counting PTS from zero
>  setpts=PTS-STARTPTS
>  @end example
>  
> +@item
> +Apply a random jitter effect of +/-100 TB units:
> +@example
> +setpts=PTS+100rand(-100\,100)
> +@end example
> +
>  @item
>  Apply fast motion effect:
>  @example
> diff --git a/libavfilter/setpts.c b/libavfilter/setpts.c
> index 88a8d6af86..0f24a900b3 100644
> --- a/libavfilter/setpts.c
> +++ b/libavfilter/setpts.c
> @@ -32,6 +32,8 @@
>  #include "libavutil/internal.h"
>  #include "libavutil/mathematics.h"
>  #include "libavutil/opt.h"
> +#include "libavutil/lfg.h"
> +#include "libavutil/random_seed.h"
>  #include "libavutil/time.h"
>  #include "audio.h"
>  #include "avfilter.h"
> @@ -101,18 +103,39 @@ typedef struct SetPTSContext {
>      AVExpr *expr;
>      double var_values[VAR_VARS_NB];
>      enum AVMediaType type;
> +    AVLFG lfg;
>  } SetPTSContext;
>  
>  #define V(name_) \
>      setpts->var_values[VAR_##name_]
>  
> +static double drand(void *ctx, double min, double max)
> +{
> +    SetPTSContext *setpts = ((AVFilterContext *)ctx)->priv;
> +
> +    return min + (max-min) / UINT_MAX * av_lfg_get(&setpts->lfg);
> +}
> +
> +typedef double (*eval_func2)(void *, double a, double b);
> +
> +static const eval_func2 fun2[] = {
> +    drand,
> +    NULL
> +};
> +
> +static const char *const fun2_names[] = {
> +    "rand"
> +};
> +
>  static av_cold int init(AVFilterContext *ctx)
>  {
>      SetPTSContext *setpts = ctx->priv;
>      int ret;
>  
> +    av_lfg_init(&setpts->lfg, av_get_random_seed());
> +
>      if ((ret = av_expr_parse(&setpts->expr, setpts->expr_str,
> -                             var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
> +                             var_names, NULL, NULL, fun2_names, fun2, 0, ctx)) < 0) {
>          av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n", setpts->expr_str);
>          return ret;
>      }
> @@ -126,6 +149,7 @@ static av_cold int init(AVFilterContext *ctx)
>      V(STARTPTS)    = NAN;
>      V(STARTT)      = NAN;
>      V(T_CHANGE)    = NAN;
> +
>      return 0;
>  }
>  
> @@ -159,8 +183,10 @@ static inline char *double2int64str(char *buf, double v)
>      return buf;
>  }
>  
> -static double eval_pts(SetPTSContext *setpts, AVFilterLink *inlink, AVFrame *frame, int64_t pts)
> +static double eval_pts(AVFilterContext *ctx, AVFilterLink *inlink, AVFrame *frame, int64_t pts)
>  {
> +    SetPTSContext *setpts = ctx->priv;
> +
>      if (isnan(V(STARTPTS))) {
>          V(STARTPTS) = TS2D(pts);
>          V(STARTT  ) = TS2T(pts, inlink->time_base);
> @@ -186,17 +212,18 @@ FF_ENABLE_DEPRECATION_WARNINGS
>          }
>      }
>  
> -    return av_expr_eval(setpts->expr, setpts->var_values, NULL);
> +    return av_expr_eval(setpts->expr, setpts->var_values, ctx);
>  }
>  #define d2istr(v) double2int64str((char[BUF_SIZE]){0}, v)
>  
>  static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
>  {
> -    SetPTSContext *setpts = inlink->dst->priv;
> +    AVFilterContext *ctx = inlink->dst;
> +    SetPTSContext *setpts = ctx->priv;
>      int64_t in_pts = frame->pts;
>      double d;
>  
> -    d = eval_pts(setpts, inlink, frame, frame->pts);
> +    d = eval_pts(ctx, inlink, frame, frame->pts);
>      frame->pts = D2TS(d);
>  
>      av_log(inlink->dst, AV_LOG_TRACE,
> @@ -250,7 +277,7 @@ static int activate(AVFilterContext *ctx)
>          return filter_frame(inlink, in);
>  
>      if (ff_inlink_acknowledge_status(inlink, &status, &pts)) {
> -        double d = eval_pts(setpts, inlink, NULL, pts);
> +        double d = eval_pts(ctx, inlink, NULL, pts);
>  
>          av_log(ctx, AV_LOG_TRACE, "N:EOF PTS:%s T:%f -> PTS:%s T:%f\n",
>                 d2istr(V(PTS)), V(T), d2istr(d), TS2T(d, inlink->time_base));

Why is this added here and not in lavu/eval so that it is available with
all expressions?

- Andreas
Stefano Sabatini Dec. 28, 2023, 3 p.m. UTC | #2
On date Thursday 2023-12-28 12:02:59 +0100, Andreas Rheinhardt wrote:
> Stefano Sabatini:
> > This is useful to simulate random jitter.
> > ---
> >  Changelog            |  1 +
> >  doc/filters.texi     | 10 +++++++++-
> >  libavfilter/setpts.c | 39 +++++++++++++++++++++++++++++++++------
> >  3 files changed, 43 insertions(+), 7 deletions(-)
> > 
> > diff --git a/Changelog b/Changelog
> > index 424bfc11af..ed01c53264 100644
> > --- a/Changelog
> > +++ b/Changelog
> > @@ -15,6 +15,7 @@ version <next>:
> >  - tiltandshift filter
> >  - qrencode filter and qrencodesrc source
> >  - quirc filter
> > +- lavfi/setpts: introduce rand() function in expression
> >  
> >  version 6.1:
> >  - libaribcaption decoder
> > diff --git a/doc/filters.texi b/doc/filters.texi
> > index d1f95b9781..1d9a5d6c7d 100644
> > --- a/doc/filters.texi
> > +++ b/doc/filters.texi
> > @@ -30946,7 +30946,7 @@ The expression which is evaluated for each frame to construct its timestamp.
> >  @end table
> >  
> >  The expression is evaluated through the eval API and can contain the following
> > -constants:
> > +constants and functions:
> >  
> >  @table @option
> >  @item FRAME_RATE, FR
> > @@ -31010,6 +31010,8 @@ The timebase of the input timestamps.
> >  @item T_CHANGE
> >  Time of the first frame after command was applied or time of the first frame if no commands.
> >  
> > +@item rand(min, max)
> > +a random number included between min and max
> >  @end table
> >  
> >  @subsection Examples
> > @@ -31021,6 +31023,12 @@ Start counting PTS from zero
> >  setpts=PTS-STARTPTS
> >  @end example
> >  
> > +@item
> > +Apply a random jitter effect of +/-100 TB units:
> > +@example
> > +setpts=PTS+100rand(-100\,100)
> > +@end example
> > +
> >  @item
> >  Apply fast motion effect:
> >  @example
> > diff --git a/libavfilter/setpts.c b/libavfilter/setpts.c
> > index 88a8d6af86..0f24a900b3 100644
> > --- a/libavfilter/setpts.c
> > +++ b/libavfilter/setpts.c
> > @@ -32,6 +32,8 @@
> >  #include "libavutil/internal.h"
> >  #include "libavutil/mathematics.h"
> >  #include "libavutil/opt.h"
> > +#include "libavutil/lfg.h"
> > +#include "libavutil/random_seed.h"
> >  #include "libavutil/time.h"
> >  #include "audio.h"
> >  #include "avfilter.h"
> > @@ -101,18 +103,39 @@ typedef struct SetPTSContext {
> >      AVExpr *expr;
> >      double var_values[VAR_VARS_NB];
> >      enum AVMediaType type;
> > +    AVLFG lfg;
> >  } SetPTSContext;
> >  
> >  #define V(name_) \
> >      setpts->var_values[VAR_##name_]
> >  
> > +static double drand(void *ctx, double min, double max)
> > +{
> > +    SetPTSContext *setpts = ((AVFilterContext *)ctx)->priv;
> > +
> > +    return min + (max-min) / UINT_MAX * av_lfg_get(&setpts->lfg);
> > +}
> > +
[...]
> 

> Why is this added here and not in lavu/eval so that it is available with
> all expressions?

There is no specific reason. Sometimes you need to have a control over
the seed, in this case you need to provide the PRNG context.

For the general case, probably we can place a global LFG in the eval
module and fetch its generated values.
Stefano Sabatini Dec. 28, 2023, 6:49 p.m. UTC | #3
On date Thursday 2023-12-28 16:00:57 +0100, Stefano Sabatini wrote:
> On date Thursday 2023-12-28 12:02:59 +0100, Andreas Rheinhardt wrote:
> > Stefano Sabatini:
> > > This is useful to simulate random jitter.
> > > ---
> > >  Changelog            |  1 +
> > >  doc/filters.texi     | 10 +++++++++-
> > >  libavfilter/setpts.c | 39 +++++++++++++++++++++++++++++++++------
> > >  3 files changed, 43 insertions(+), 7 deletions(-)
[...]
> > Why is this added here and not in lavu/eval so that it is available with
> > all expressions?
> 
> There is no specific reason. Sometimes you need to have a control over
> the seed, in this case you need to provide the PRNG context.
> 
> For the general case, probably we can place a global LFG in the eval
> module and fetch its generated values.

Another possible solution in attachment, leveraging the same affine
PRNG used in random(). For other use cases you might need to use a
high-quality PRNG and this might not be good enough.
Michael Niedermayer Dec. 28, 2023, 9:25 p.m. UTC | #4
On Thu, Dec 28, 2023 at 07:49:18PM +0100, Stefano Sabatini wrote:
> On date Thursday 2023-12-28 16:00:57 +0100, Stefano Sabatini wrote:
> > On date Thursday 2023-12-28 12:02:59 +0100, Andreas Rheinhardt wrote:
> > > Stefano Sabatini:
> > > > This is useful to simulate random jitter.
> > > > ---
> > > >  Changelog            |  1 +
> > > >  doc/filters.texi     | 10 +++++++++-
> > > >  libavfilter/setpts.c | 39 +++++++++++++++++++++++++++++++++------
> > > >  3 files changed, 43 insertions(+), 7 deletions(-)
> [...]
> > > Why is this added here and not in lavu/eval so that it is available with
> > > all expressions?
> > 
> > There is no specific reason. Sometimes you need to have a control over
> > the seed, in this case you need to provide the PRNG context.
> > 
> > For the general case, probably we can place a global LFG in the eval
> > module and fetch its generated values.
> 
> Another possible solution in attachment, leveraging the same affine
> PRNG used in random(). For other use cases you might need to use a
> high-quality PRNG and this might not be good enough.

>  eval.c |   18 +++++++++++++++---
>  1 file changed, 15 insertions(+), 3 deletions(-)
> 94413dcc88e3046b4987715f6c152aeb1c24c703  0001-lavu-eval-add-randomi-function-to-compute-random-val.patch
> From 4dc5213b0913a2585f75dfd03b497ca2efc093de Mon Sep 17 00:00:00 2001
> From: Stefano Sabatini <stefasab@gmail.com>
> Date: Thu, 28 Dec 2023 19:09:22 +0100
> Subject: [PATCH] lavu/eval: add randomi function to compute random value in
>  interval

probably ok if this is usefull

thx

[...]
Stefano Sabatini Dec. 29, 2023, 11:51 a.m. UTC | #5
On date Thursday 2023-12-28 22:25:08 +0100, Michael Niedermayer wrote:
> On Thu, Dec 28, 2023 at 07:49:18PM +0100, Stefano Sabatini wrote:
> > On date Thursday 2023-12-28 16:00:57 +0100, Stefano Sabatini wrote:
> > > On date Thursday 2023-12-28 12:02:59 +0100, Andreas Rheinhardt wrote:
> > > > Stefano Sabatini:
> > > > > This is useful to simulate random jitter.
> > > > > ---
> > > > >  Changelog            |  1 +
> > > > >  doc/filters.texi     | 10 +++++++++-
> > > > >  libavfilter/setpts.c | 39 +++++++++++++++++++++++++++++++++------
> > > > >  3 files changed, 43 insertions(+), 7 deletions(-)
> > [...]
> > > > Why is this added here and not in lavu/eval so that it is available with
> > > > all expressions?
> > > 
> > > There is no specific reason. Sometimes you need to have a control over
> > > the seed, in this case you need to provide the PRNG context.
> > > 
> > > For the general case, probably we can place a global LFG in the eval
> > > module and fetch its generated values.
> > 
> > Another possible solution in attachment, leveraging the same affine
> > PRNG used in random(). For other use cases you might need to use a
> > high-quality PRNG and this might not be good enough.
> 
> >  eval.c |   18 +++++++++++++++---
> >  1 file changed, 15 insertions(+), 3 deletions(-)
> > 94413dcc88e3046b4987715f6c152aeb1c24c703  0001-lavu-eval-add-randomi-function-to-compute-random-val.patch
> > From 4dc5213b0913a2585f75dfd03b497ca2efc093de Mon Sep 17 00:00:00 2001
> > From: Stefano Sabatini <stefasab@gmail.com>
> > Date: Thu, 28 Dec 2023 19:09:22 +0100
> > Subject: [PATCH] lavu/eval: add randomi function to compute random value in
> >  interval
> 
> probably ok if this is usefull
> 
> thx

Will apply the edited patch in a few days if I see no comments.
Stefano Sabatini Jan. 2, 2024, 9:12 p.m. UTC | #6
On date Friday 2023-12-29 12:51:10 +0100, Stefano Sabatini wrote:
> On date Thursday 2023-12-28 22:25:08 +0100, Michael Niedermayer wrote:
> > On Thu, Dec 28, 2023 at 07:49:18PM +0100, Stefano Sabatini wrote:
> > > On date Thursday 2023-12-28 16:00:57 +0100, Stefano Sabatini wrote:
> > > > On date Thursday 2023-12-28 12:02:59 +0100, Andreas Rheinhardt wrote:
> > > > > Stefano Sabatini:
> > > > > > This is useful to simulate random jitter.
> > > > > > ---
> > > > > >  Changelog            |  1 +
> > > > > >  doc/filters.texi     | 10 +++++++++-
> > > > > >  libavfilter/setpts.c | 39 +++++++++++++++++++++++++++++++++------
> > > > > >  3 files changed, 43 insertions(+), 7 deletions(-)
> > > [...]
> > > > > Why is this added here and not in lavu/eval so that it is available with
> > > > > all expressions?
> > > > 
> > > > There is no specific reason. Sometimes you need to have a control over
> > > > the seed, in this case you need to provide the PRNG context.
> > > > 
> > > > For the general case, probably we can place a global LFG in the eval
> > > > module and fetch its generated values.
> > > 
> > > Another possible solution in attachment, leveraging the same affine
> > > PRNG used in random(). For other use cases you might need to use a
> > > high-quality PRNG and this might not be good enough.
> > 
> > >  eval.c |   18 +++++++++++++++---
> > >  1 file changed, 15 insertions(+), 3 deletions(-)
> > > 94413dcc88e3046b4987715f6c152aeb1c24c703  0001-lavu-eval-add-randomi-function-to-compute-random-val.patch
> > > From 4dc5213b0913a2585f75dfd03b497ca2efc093de Mon Sep 17 00:00:00 2001
> > > From: Stefano Sabatini <stefasab@gmail.com>
> > > Date: Thu, 28 Dec 2023 19:09:22 +0100
> > > Subject: [PATCH] lavu/eval: add randomi function to compute random value in
> > >  interval
> > 
> > probably ok if this is usefull
> > 
> > thx
> 
> Will apply the edited patch in a few days if I see no comments.

Applied.
diff mbox series

Patch

diff --git a/Changelog b/Changelog
index 424bfc11af..ed01c53264 100644
--- a/Changelog
+++ b/Changelog
@@ -15,6 +15,7 @@  version <next>:
 - tiltandshift filter
 - qrencode filter and qrencodesrc source
 - quirc filter
+- lavfi/setpts: introduce rand() function in expression
 
 version 6.1:
 - libaribcaption decoder
diff --git a/doc/filters.texi b/doc/filters.texi
index d1f95b9781..1d9a5d6c7d 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -30946,7 +30946,7 @@  The expression which is evaluated for each frame to construct its timestamp.
 @end table
 
 The expression is evaluated through the eval API and can contain the following
-constants:
+constants and functions:
 
 @table @option
 @item FRAME_RATE, FR
@@ -31010,6 +31010,8 @@  The timebase of the input timestamps.
 @item T_CHANGE
 Time of the first frame after command was applied or time of the first frame if no commands.
 
+@item rand(min, max)
+a random number included between min and max
 @end table
 
 @subsection Examples
@@ -31021,6 +31023,12 @@  Start counting PTS from zero
 setpts=PTS-STARTPTS
 @end example
 
+@item
+Apply a random jitter effect of +/-100 TB units:
+@example
+setpts=PTS+100rand(-100\,100)
+@end example
+
 @item
 Apply fast motion effect:
 @example
diff --git a/libavfilter/setpts.c b/libavfilter/setpts.c
index 88a8d6af86..0f24a900b3 100644
--- a/libavfilter/setpts.c
+++ b/libavfilter/setpts.c
@@ -32,6 +32,8 @@ 
 #include "libavutil/internal.h"
 #include "libavutil/mathematics.h"
 #include "libavutil/opt.h"
+#include "libavutil/lfg.h"
+#include "libavutil/random_seed.h"
 #include "libavutil/time.h"
 #include "audio.h"
 #include "avfilter.h"
@@ -101,18 +103,39 @@  typedef struct SetPTSContext {
     AVExpr *expr;
     double var_values[VAR_VARS_NB];
     enum AVMediaType type;
+    AVLFG lfg;
 } SetPTSContext;
 
 #define V(name_) \
     setpts->var_values[VAR_##name_]
 
+static double drand(void *ctx, double min, double max)
+{
+    SetPTSContext *setpts = ((AVFilterContext *)ctx)->priv;
+
+    return min + (max-min) / UINT_MAX * av_lfg_get(&setpts->lfg);
+}
+
+typedef double (*eval_func2)(void *, double a, double b);
+
+static const eval_func2 fun2[] = {
+    drand,
+    NULL
+};
+
+static const char *const fun2_names[] = {
+    "rand"
+};
+
 static av_cold int init(AVFilterContext *ctx)
 {
     SetPTSContext *setpts = ctx->priv;
     int ret;
 
+    av_lfg_init(&setpts->lfg, av_get_random_seed());
+
     if ((ret = av_expr_parse(&setpts->expr, setpts->expr_str,
-                             var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
+                             var_names, NULL, NULL, fun2_names, fun2, 0, ctx)) < 0) {
         av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n", setpts->expr_str);
         return ret;
     }
@@ -126,6 +149,7 @@  static av_cold int init(AVFilterContext *ctx)
     V(STARTPTS)    = NAN;
     V(STARTT)      = NAN;
     V(T_CHANGE)    = NAN;
+
     return 0;
 }
 
@@ -159,8 +183,10 @@  static inline char *double2int64str(char *buf, double v)
     return buf;
 }
 
-static double eval_pts(SetPTSContext *setpts, AVFilterLink *inlink, AVFrame *frame, int64_t pts)
+static double eval_pts(AVFilterContext *ctx, AVFilterLink *inlink, AVFrame *frame, int64_t pts)
 {
+    SetPTSContext *setpts = ctx->priv;
+
     if (isnan(V(STARTPTS))) {
         V(STARTPTS) = TS2D(pts);
         V(STARTT  ) = TS2T(pts, inlink->time_base);
@@ -186,17 +212,18 @@  FF_ENABLE_DEPRECATION_WARNINGS
         }
     }
 
-    return av_expr_eval(setpts->expr, setpts->var_values, NULL);
+    return av_expr_eval(setpts->expr, setpts->var_values, ctx);
 }
 #define d2istr(v) double2int64str((char[BUF_SIZE]){0}, v)
 
 static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
 {
-    SetPTSContext *setpts = inlink->dst->priv;
+    AVFilterContext *ctx = inlink->dst;
+    SetPTSContext *setpts = ctx->priv;
     int64_t in_pts = frame->pts;
     double d;
 
-    d = eval_pts(setpts, inlink, frame, frame->pts);
+    d = eval_pts(ctx, inlink, frame, frame->pts);
     frame->pts = D2TS(d);
 
     av_log(inlink->dst, AV_LOG_TRACE,
@@ -250,7 +277,7 @@  static int activate(AVFilterContext *ctx)
         return filter_frame(inlink, in);
 
     if (ff_inlink_acknowledge_status(inlink, &status, &pts)) {
-        double d = eval_pts(setpts, inlink, NULL, pts);
+        double d = eval_pts(ctx, inlink, NULL, pts);
 
         av_log(ctx, AV_LOG_TRACE, "N:EOF PTS:%s T:%f -> PTS:%s T:%f\n",
                d2istr(V(PTS)), V(T), d2istr(d), TS2T(d, inlink->time_base));