diff mbox series

[FFmpeg-devel] lsws/swscale.h: introduce sws_get_gaussian_vec

Message ID 20230826122328.95416-1-stefasab@gmail.com
State New
Headers show
Series [FFmpeg-devel] lsws/swscale.h: introduce sws_get_gaussian_vec | 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 Aug. 26, 2023, 12:23 p.m. UTC
Use in place of sws_getGaussianVec.

The new function enable better log handling, and provide better naming
for the variance variable, now named standard_deviation to reflect the
meaning of the parameter.
---
 doc/APIchanges             |  3 +++
 libswscale/swscale.h       | 21 ++++++++++++++++++-
 libswscale/utils.c         | 41 +++++++++++++++++++++++++++++---------
 libswscale/version.h       |  2 +-
 libswscale/version_major.h |  4 ++++
 5 files changed, 60 insertions(+), 11 deletions(-)

Comments

Andreas Rheinhardt Aug. 26, 2023, 3:15 p.m. UTC | #1
Stefano Sabatini:
> Use in place of sws_getGaussianVec.
> 
> The new function enable better log handling, and provide better naming

Better log handling? Why?

> for the variance variable, now named standard_deviation to reflect the
> meaning of the parameter.
> ---
>  doc/APIchanges             |  3 +++
>  libswscale/swscale.h       | 21 ++++++++++++++++++-
>  libswscale/utils.c         | 41 +++++++++++++++++++++++++++++---------
>  libswscale/version.h       |  2 +-
>  libswscale/version_major.h |  4 ++++
>  5 files changed, 60 insertions(+), 11 deletions(-)
> 
> diff --git a/doc/APIchanges b/doc/APIchanges
> index ad1efe708d..bad2d61027 100644
> --- a/doc/APIchanges
> +++ b/doc/APIchanges
> @@ -2,6 +2,9 @@ The last version increases of all libraries were on 2023-02-09
>  
>  API changes, most recent first:
>  
> +2023-08-26 - xxxxxxxxxx - lsws 7.4.100 - swscale.h
> +  Introduce sws_get_gaussian_vec, use in place of sws_getGaussianVec.
> +
>  2023-08-18 - xxxxxxxxxx - lavu 58.17.100 - channel_layout.h
>    All AV_CHANNEL_LAYOUT_* macros are now compatible with C++ 17 and older.
>  
> diff --git a/libswscale/swscale.h b/libswscale/swscale.h
> index 9d4612aaf3..f002b5c7d2 100644
> --- a/libswscale/swscale.h
> +++ b/libswscale/swscale.h
> @@ -355,11 +355,30 @@ int sws_getColorspaceDetails(struct SwsContext *c, int **inv_table,
>   */
>  SwsVector *sws_allocVec(int length);
>  
> +#if FF_API_SWS_GET_GAUSSIAN_VEC
>  /**
> - * Return a normalized Gaussian curve used to filter stuff
> + * Return a normalized Gaussian curve used to filter stuff.
> + *
>   * quality = 3 is high quality, lower is lower quality.
>   */
>  SwsVector *sws_getGaussianVec(double variance, double quality);

Missing attribute_deprecated as well as the @deprecated doxygen thing
refering to its replacement.

> +#endif
> +
> +/**
> + * Compute and return a normalized Gaussian vector.
> + *
> + * @param vecp: pointer where the computed vector is put in case of
> + *        success
> + * @param standard_deviation the standard deviation used to generate
> + *        the Gaussian vector, must be a non-negative value
> + * @param quality the quality of the generated Gaussian vector, must
> + *        be a non-negative value. It affects the lenght of the generated
> + *        vector. A value equal to 3 corresponds to high quality.
> + *
> + * @return a negative error code on error, non negative otherwise
> + */
> +int sws_get_gaussian_vec(SwsVector **vecp,
> +                         double standard_deviation, double quality);
>  
>  /**
>   * Scale all the coefficients of a by the scalar value.
> diff --git a/libswscale/utils.c b/libswscale/utils.c
> index 8e74c6603e..96034af1e0 100644
> --- a/libswscale/utils.c
> +++ b/libswscale/utils.c
> @@ -2139,31 +2139,54 @@ SwsVector *sws_allocVec(int length)
>      return vec;
>  }
>  
> -SwsVector *sws_getGaussianVec(double variance, double quality)
> +int sws_get_gaussian_vec(SwsVector **vecp,
> +                         double standard_deviation, double quality)
>  {
> -    const int length = (int)(variance * quality + 0.5) | 1;
> +    const int length = (int)(standard_deviation * quality + 0.5) | 1;
>      int i;
>      double middle  = (length - 1) * 0.5;
>      SwsVector *vec;
> +    const double variance = standard_deviation * standard_deviation;
>  
> -    if(variance < 0 || quality < 0)
> -        return NULL;
> +    if (standard_deviation < 0 || quality < 0) {
> +        av_log(NULL, AV_LOG_ERROR,
> +               "Invalid negative standard deviation %f or quality %f provided as input to the sws_get_gaussian_vec function\n",
> +               standard_deviation, quality);
> +        return AVERROR(EINVAL);
> +    }
>  
>      vec = sws_allocVec(length);
> -
> -    if (!vec)
> -        return NULL;
> +    if (!vec) {
> +        av_log(NULL, AV_LOG_ERROR,
> +               "Could not allocate vector for the sws_get_gaussian_vec function\n");
> +        return AVERROR(ENOMEM);
> +    }
>  
>      for (i = 0; i < length; i++) {
>          double dist = i - middle;
> -        vec->coeff[i] = exp(-dist * dist / (2 * variance * variance)) /
> -                        sqrt(2 * variance * M_PI);
> +        vec->coeff[i] = exp(-dist * dist / (2 * variance)) /
> +                        sqrt(2 * standard_deviation * M_PI);
>      }
>  
>      sws_normalizeVec(vec, 1.0);
> +    *vecp = vec;
>  
> +    return 0;
> +}
> +
> +#if FF_API_SWS_GET_GAUSSIAN_VEC
> +SwsVector *sws_getGaussianVec(double variance, double quality)
> +{
> +    SwsVector *vec;
> +    int ret;
> +
> +    ret = sws_get_gaussian_vec(&vec, variance, quality);
> +    if (ret < 0) {
> +        return NULL;
> +    }
>      return vec;
>  }
> +#endif // FF_API_SWS_GET_GAUSSIAN_VEC
>  
>  /**
>   * Allocate and return a vector with length coefficients, all
> diff --git a/libswscale/version.h b/libswscale/version.h
> index 51eb013a29..12412bd538 100644
> --- a/libswscale/version.h
> +++ b/libswscale/version.h
> @@ -28,7 +28,7 @@
>  
>  #include "version_major.h"
>  
> -#define LIBSWSCALE_VERSION_MINOR   3
> +#define LIBSWSCALE_VERSION_MINOR   4
>  #define LIBSWSCALE_VERSION_MICRO 100
>  
>  #define LIBSWSCALE_VERSION_INT  AV_VERSION_INT(LIBSWSCALE_VERSION_MAJOR, \
> diff --git a/libswscale/version_major.h b/libswscale/version_major.h
> index 88577a2b42..aa0baef7c6 100644
> --- a/libswscale/version_major.h
> +++ b/libswscale/version_major.h
> @@ -32,4 +32,8 @@
>   * the public API and may change, break or disappear at any time.
>   */
>  
> +#ifndef FF_API_SWS_GET_GAUSSIAN_VEC
> +#define FF_API_SWS_GET_GAUSSIAN_VEC       (LIBSWSCALE_VERSION_MAJOR < 8)
> +#endif
> +
>  #endif /* SWSCALE_VERSION_MAJOR_H */
Anton Khirnov Aug. 26, 2023, 3:15 p.m. UTC | #2
Quoting Stefano Sabatini (2023-08-26 14:23:28)
> Use in place of sws_getGaussianVec.
> 
> The new function enable better log handling, and provide better naming
> for the variance variable, now named standard_deviation to reflect the
> meaning of the parameter.

Logging to NULL does not seem like an improvement to me.

Renaming a function parameter does not require an API break.
Stefano Sabatini Aug. 31, 2023, 3:06 p.m. UTC | #3
On date Saturday 2023-08-26 17:15:36 +0200, Anton Khirnov wrote:
> Quoting Stefano Sabatini (2023-08-26 14:23:28)
> > Use in place of sws_getGaussianVec.
> > 
> > The new function enable better log handling, and provide better naming
> > for the variance variable, now named standard_deviation to reflect the
> > meaning of the parameter.
> 

> Logging to NULL does not seem like an improvement to me.

Adding the log_ctx.

> Renaming a function parameter does not require an API break.

The main point was improving the naming of the variable, but while at
it I'm also adding the logging context and providing a return code to
specify an error failure, and moving to snake_case convention which is
the one used by the new API additions.
Stefano Sabatini Aug. 31, 2023, 3:32 p.m. UTC | #4
On date Saturday 2023-08-26 17:15:27 +0200, Andreas Rheinhardt wrote:
> Stefano Sabatini:
> > Use in place of sws_getGaussianVec.
> > 
> > The new function enable better log handling, and provide better naming
> 
> Better log handling? Why?
> 
> > for the variance variable, now named standard_deviation to reflect the
> > meaning of the parameter.
> > ---
> >  doc/APIchanges             |  3 +++
> >  libswscale/swscale.h       | 21 ++++++++++++++++++-
> >  libswscale/utils.c         | 41 +++++++++++++++++++++++++++++---------
> >  libswscale/version.h       |  2 +-
> >  libswscale/version_major.h |  4 ++++
> >  5 files changed, 60 insertions(+), 11 deletions(-)
> > 
> > diff --git a/doc/APIchanges b/doc/APIchanges
> > index ad1efe708d..bad2d61027 100644
> > --- a/doc/APIchanges
> > +++ b/doc/APIchanges
> > @@ -2,6 +2,9 @@ The last version increases of all libraries were on 2023-02-09
> >  
> >  API changes, most recent first:
> >  
> > +2023-08-26 - xxxxxxxxxx - lsws 7.4.100 - swscale.h
> > +  Introduce sws_get_gaussian_vec, use in place of sws_getGaussianVec.
> > +
> >  2023-08-18 - xxxxxxxxxx - lavu 58.17.100 - channel_layout.h
> >    All AV_CHANNEL_LAYOUT_* macros are now compatible with C++ 17 and older.
> >  
> > diff --git a/libswscale/swscale.h b/libswscale/swscale.h
> > index 9d4612aaf3..f002b5c7d2 100644
> > --- a/libswscale/swscale.h
> > +++ b/libswscale/swscale.h
> > @@ -355,11 +355,30 @@ int sws_getColorspaceDetails(struct SwsContext *c, int **inv_table,
> >   */
> >  SwsVector *sws_allocVec(int length);
> >  
> > +#if FF_API_SWS_GET_GAUSSIAN_VEC
> >  /**
> > - * Return a normalized Gaussian curve used to filter stuff
> > + * Return a normalized Gaussian curve used to filter stuff.
> > + *
> >   * quality = 3 is high quality, lower is lower quality.
> >   */
> >  SwsVector *sws_getGaussianVec(double variance, double quality);
> 
> Missing attribute_deprecated as well as the @deprecated doxygen thing
> refering to its replacement.

Updated.
Andreas Rheinhardt Aug. 31, 2023, 4:51 p.m. UTC | #5
Stefano Sabatini:
> +int sws_get_gaussian_vec(SwsVector **vecp,
> +                         AVClass *log_ctx,
> +                         double standard_deviation, double quality);
>  

Seriously? A pointer to an AVClass as log_ctx? It is actually AVClass**
(the logcontext must have a pointer to an AVClass as its first member),
but we always use NULL.

Apart from that: I am not really convinced that the improvement is worth
the hassle.

> 
> +    if (standard_deviation < 0 || quality < 0) {
> +        av_log(NULL, AV_LOG_ERROR,
> +               "Invalid negative standard deviation %f or quality %f provided as input to the sws_get_gaussian_vec function\n",
> +               standard_deviation, quality);

Here you are not even using the logctx.

- Andreas
Stefano Sabatini Aug. 31, 2023, 5:16 p.m. UTC | #6
On date Thursday 2023-08-31 18:51:52 +0200, Andreas Rheinhardt wrote:
> Stefano Sabatini:
> > +int sws_get_gaussian_vec(SwsVector **vecp,
> > +                         AVClass *log_ctx,
> > +                         double standard_deviation, double quality);
> >  
> 
> Seriously? A pointer to an AVClass as log_ctx? It is actually AVClass**
> (the logcontext must have a pointer to an AVClass as its first member),
> but we always use NULL.

Sorry, sloppy editing on my side.

> Apart from that: I am not really convinced that the improvement is worth
> the hassle.

This is not a high-profile function (probably it's never used outside
lavfi) and provides an opportunity to move the API to the correct
direction.
Anton Khirnov Sept. 1, 2023, 3:50 p.m. UTC | #7
Quoting Stefano Sabatini (2023-08-31 17:06:06)
> On date Saturday 2023-08-26 17:15:36 +0200, Anton Khirnov wrote:
> > Quoting Stefano Sabatini (2023-08-26 14:23:28)
> > > Use in place of sws_getGaussianVec.
> > > 
> > > The new function enable better log handling, and provide better naming
> > > for the variance variable, now named standard_deviation to reflect the
> > > meaning of the parameter.
> > 
> 
> > Logging to NULL does not seem like an improvement to me.
> 
> Adding the log_ctx.
> 
> > Renaming a function parameter does not require an API break.
> 
> The main point was improving the naming of the variable, but while at
> it I'm also adding the logging context and providing a return code to
> specify an error failure, and moving to snake_case convention which is
> the one used by the new API additions.

As I already said above - function parameter names in a prototype are
purely cosmetic and have no effect on anything besides doxygen. You can
change them at will and even remove them entirely without breaking API
or ABI.

The other reasons do not strike me as strong enough to warrant an API
break.
Michael Niedermayer Sept. 1, 2023, 4:54 p.m. UTC | #8
On Thu, Aug 31, 2023 at 07:16:20PM +0200, Stefano Sabatini wrote:
> On date Thursday 2023-08-31 18:51:52 +0200, Andreas Rheinhardt wrote:
> > Stefano Sabatini:
> > > +int sws_get_gaussian_vec(SwsVector **vecp,
> > > +                         AVClass *log_ctx,
> > > +                         double standard_deviation, double quality);
> > >  
> > 
> > Seriously? A pointer to an AVClass as log_ctx? It is actually AVClass**
> > (the logcontext must have a pointer to an AVClass as its first member),
> > but we always use NULL.
> 
> Sorry, sloppy editing on my side.
> 
> > Apart from that: I am not really convinced that the improvement is worth
> > the hassle.
> 
> This is not a high-profile function (probably it's never used outside
> lavfi) and provides an opportunity to move the API to the correct
> direction.

>  doc/APIchanges             |    3 +++
>  libswscale/swscale.h       |   27 ++++++++++++++++++++++++++-
>  libswscale/utils.c         |   42 +++++++++++++++++++++++++++++++++---------
>  libswscale/version.h       |    2 +-
>  libswscale/version_major.h |    4 ++++
>  5 files changed, 67 insertions(+), 11 deletions(-)
> b91b721cea2752b28a51aaeab2a464b2699dfb49  0001-lsws-swscale.h-introduce-sws_get_gaussian_vec.patch
> From 69c33f62e15de3d199d54187d38c0856418f0981 Mon Sep 17 00:00:00 2001
> From: Stefano Sabatini <stefasab@gmail.com>
> Date: Sat, 26 Aug 2023 14:20:35 +0200
> Subject: [PATCH 1/2] lsws/swscale.h: introduce sws_get_gaussian_vec
> 
> Use in place of sws_getGaussianVec.
> 
> The new function enable better error handling, and provide better naming
> for the variance variable, now named standard_deviation to reflect the
> meaning of the parameter.
> ---
>  doc/APIchanges             |  3 +++
>  libswscale/swscale.h       | 27 +++++++++++++++++++++++-
>  libswscale/utils.c         | 42 ++++++++++++++++++++++++++++++--------
>  libswscale/version.h       |  2 +-
>  libswscale/version_major.h |  4 ++++
>  5 files changed, 67 insertions(+), 11 deletions(-)
> 
> diff --git a/doc/APIchanges b/doc/APIchanges
> index ad1efe708d..bad2d61027 100644
> --- a/doc/APIchanges
> +++ b/doc/APIchanges
> @@ -2,6 +2,9 @@ The last version increases of all libraries were on 2023-02-09
>  
>  API changes, most recent first:
>  
> +2023-08-26 - xxxxxxxxxx - lsws 7.4.100 - swscale.h
> +  Introduce sws_get_gaussian_vec, use in place of sws_getGaussianVec.
> +
>  2023-08-18 - xxxxxxxxxx - lavu 58.17.100 - channel_layout.h
>    All AV_CHANNEL_LAYOUT_* macros are now compatible with C++ 17 and older.
>  
> diff --git a/libswscale/swscale.h b/libswscale/swscale.h
> index 9d4612aaf3..55f2fc4a48 100644
> --- a/libswscale/swscale.h
> +++ b/libswscale/swscale.h
> @@ -355,11 +355,36 @@ int sws_getColorspaceDetails(struct SwsContext *c, int **inv_table,
>   */
>  SwsVector *sws_allocVec(int length);
>  
> +#if FF_API_SWS_GET_GAUSSIAN_VEC
>  /**
> - * Return a normalized Gaussian curve used to filter stuff
> + * Return a normalized Gaussian curve used to filter stuff.
> + *
>   * quality = 3 is high quality, lower is lower quality.
> + * @deprecated use sws_get_gaussian_vector()
>   */
> +attribute_deprecated
>  SwsVector *sws_getGaussianVec(double variance, double quality);
> +#endif
> +
> +/**
> + * Compute and return a normalized Gaussian vector.
> + *
> + * @param vecp: pointer where the computed vector is put in case of
> + *        success
> + * @param standard_deviation the standard deviation used to generate
> + *        the Gaussian vector, must be a non-negative value
> + * @param quality the quality of the generated Gaussian vector, must
> + *        be a non-negative value. It affects the lenght of the generated
> + *        vector. A value equal to 3 corresponds to high quality.
> + * @param log_ctx a pointer to an arbitrary struct of which the first
> + *        field is a pointer to an AVClass struct (used for av_log)
> + *        used for logging, can be NULL
> + *
> + * @return a negative error code on error, non negative otherwise
> + */
> +int sws_get_gaussian_vec(SwsVector **vecp,
> +                         double standard_deviation, double quality,
> +                         void *log_ctx);

which of the two do you consider better?

First, here the central part we return is the vector

SwsVector *gaus_vec = sws_getGaussianVec(NULL, 1, 2);
SwsVector *temp_vec = sws_ConvolveVec(NULL, in_vec, gaus_vec);
sws_averageVec(temp_vec, temp_vec, in_vec);

av_free(gaus_vec);
return temp_vec; // Error checking here happens by temp_vec being NULL in all cases of error

vs.

Second, here the central part we return is the error code

SwsVector *gaus_vec = NULL;
SwsVector *temp_vec = NULL;
int err = sws_getGaussianVec(&gaus_vec, 1, 2);
if (err<0)
    goto fail;

err = sws_ConvolveVec(&temp_vec, in_vec, gaus_vec);
if (err<0)
    goto fail;

err = sws_averageVec(&temp_vec, temp_vec, in_vec);
if (err<0)
    goto fail;

*ret_argument = temp_vec
return 0;
fail:
    av_free(gaus_vec)
    av_free(temp_vec)
return ret;

thx

[...]
Stefano Sabatini Sept. 1, 2023, 6:28 p.m. UTC | #9
On date Friday 2023-09-01 17:50:56 +0200, Anton Khirnov wrote:
> Quoting Stefano Sabatini (2023-08-31 17:06:06)
> > On date Saturday 2023-08-26 17:15:36 +0200, Anton Khirnov wrote:
> > > Quoting Stefano Sabatini (2023-08-26 14:23:28)
> > > > Use in place of sws_getGaussianVec.
> > > > 
> > > > The new function enable better log handling, and provide better naming
> > > > for the variance variable, now named standard_deviation to reflect the
> > > > meaning of the parameter.
> > > 
> > 
> > > Logging to NULL does not seem like an improvement to me.
> > 
> > Adding the log_ctx.
> > 
> > > Renaming a function parameter does not require an API break.
> > 
> > The main point was improving the naming of the variable, but while at
> > it I'm also adding the logging context and providing a return code to
> > specify an error failure, and moving to snake_case convention which is
> > the one used by the new API additions.
> 
> As I already said above - function parameter names in a prototype are
> purely cosmetic and have no effect on anything besides doxygen. You can
> change them at will and even remove them entirely without breaking API
> or ABI.
> 

> The other reasons do not strike me as strong enough to warrant an API
> break.

I disagree on this: the function is probably only used internally and
by libavfilter, and the migration is trivial enough so should cause no
disruption anyway.
Stefano Sabatini Sept. 1, 2023, 6:38 p.m. UTC | #10
On date Friday 2023-09-01 18:54:40 +0200, Michael Niedermayer wrote:
> On Thu, Aug 31, 2023 at 07:16:20PM +0200, Stefano Sabatini wrote:
[...]
> > +/**
> > + * Compute and return a normalized Gaussian vector.
> > + *
> > + * @param vecp: pointer where the computed vector is put in case of
> > + *        success
> > + * @param standard_deviation the standard deviation used to generate
> > + *        the Gaussian vector, must be a non-negative value
> > + * @param quality the quality of the generated Gaussian vector, must
> > + *        be a non-negative value. It affects the lenght of the generated
> > + *        vector. A value equal to 3 corresponds to high quality.
> > + * @param log_ctx a pointer to an arbitrary struct of which the first
> > + *        field is a pointer to an AVClass struct (used for av_log)
> > + *        used for logging, can be NULL
> > + *
> > + * @return a negative error code on error, non negative otherwise
> > + */
> > +int sws_get_gaussian_vec(SwsVector **vecp,
> > +                         double standard_deviation, double quality,
> > +                         void *log_ctx);
> 
> which of the two do you consider better?
> 
> First, here the central part we return is the vector
> 
> SwsVector *gaus_vec = sws_getGaussianVec(NULL, 1, 2);
> SwsVector *temp_vec = sws_ConvolveVec(NULL, in_vec, gaus_vec);
> sws_averageVec(temp_vec, temp_vec, in_vec);
> 
> av_free(gaus_vec);
> return temp_vec; // Error checking here happens by temp_vec being NULL in all cases of error
> 
> vs.
> 
> Second, here the central part we return is the error code
> 
> SwsVector *gaus_vec = NULL;
> SwsVector *temp_vec = NULL;
> int err = sws_getGaussianVec(&gaus_vec, 1, 2);
> if (err<0)
>     goto fail;
> 
> err = sws_ConvolveVec(&temp_vec, in_vec, gaus_vec);
> if (err<0)
>     goto fail;
> 
> err = sws_averageVec(&temp_vec, temp_vec, in_vec);
> if (err<0)
>     goto fail;

The latter pattern enables differentiation between error codes (ENOMEM
or EINVAL) and provides feedback in the log message. With the former
you only know if it fails, but you don't know why (relevant in case
e.g. we make the parameter tunable by a filter and we don't want to
add additional validation and logging at the filter level).
Michael Niedermayer Sept. 2, 2023, 8:07 p.m. UTC | #11
On Fri, Sep 01, 2023 at 08:38:26PM +0200, Stefano Sabatini wrote:
> On date Friday 2023-09-01 18:54:40 +0200, Michael Niedermayer wrote:
> > On Thu, Aug 31, 2023 at 07:16:20PM +0200, Stefano Sabatini wrote:
> [...]
> > > +/**
> > > + * Compute and return a normalized Gaussian vector.
> > > + *
> > > + * @param vecp: pointer where the computed vector is put in case of
> > > + *        success
> > > + * @param standard_deviation the standard deviation used to generate
> > > + *        the Gaussian vector, must be a non-negative value
> > > + * @param quality the quality of the generated Gaussian vector, must
> > > + *        be a non-negative value. It affects the lenght of the generated
> > > + *        vector. A value equal to 3 corresponds to high quality.
> > > + * @param log_ctx a pointer to an arbitrary struct of which the first
> > > + *        field is a pointer to an AVClass struct (used for av_log)
> > > + *        used for logging, can be NULL
> > > + *
> > > + * @return a negative error code on error, non negative otherwise
> > > + */
> > > +int sws_get_gaussian_vec(SwsVector **vecp,
> > > +                         double standard_deviation, double quality,
> > > +                         void *log_ctx);
> > 
> > which of the two do you consider better?
> > 
> > First, here the central part we return is the vector
> > 
> > SwsVector *gaus_vec = sws_getGaussianVec(NULL, 1, 2);
> > SwsVector *temp_vec = sws_ConvolveVec(NULL, in_vec, gaus_vec);
> > sws_averageVec(temp_vec, temp_vec, in_vec);
> > 
> > av_free(gaus_vec);
> > return temp_vec; // Error checking here happens by temp_vec being NULL in all cases of error
> > 
> > vs.
> > 
> > Second, here the central part we return is the error code
> > 
> > SwsVector *gaus_vec = NULL;
> > SwsVector *temp_vec = NULL;
> > int err = sws_getGaussianVec(&gaus_vec, 1, 2);
> > if (err<0)
> >     goto fail;
> > 
> > err = sws_ConvolveVec(&temp_vec, in_vec, gaus_vec);
> > if (err<0)
> >     goto fail;
> > 
> > err = sws_averageVec(&temp_vec, temp_vec, in_vec);
> > if (err<0)
> >     goto fail;
> 
> The latter pattern enables differentiation between error codes (ENOMEM
> or EINVAL) and provides feedback in the log message. With the former
> you only know if it fails, but you don't know why (relevant in case
> e.g. we make the parameter tunable by a filter and we don't want to
> add additional validation and logging at the filter level).

can the API be designed so that optionally the user could choose to
only check the error code after several steps ?
(this would avoid the need for 1 check per call where the fine grained
 information is not needed)
I mean similar to the concept of NAN in floating point so that a failure
can be propagated and only at the end checked.

thx

[...]
Stefano Sabatini Sept. 3, 2023, 12:25 a.m. UTC | #12
On date Saturday 2023-09-02 22:07:53 +0200, Michael Niedermayer wrote:
> On Fri, Sep 01, 2023 at 08:38:26PM +0200, Stefano Sabatini wrote:
> > On date Friday 2023-09-01 18:54:40 +0200, Michael Niedermayer wrote:
> > > On Thu, Aug 31, 2023 at 07:16:20PM +0200, Stefano Sabatini wrote:
> > [...]
> > > > +/**
> > > > + * Compute and return a normalized Gaussian vector.
> > > > + *
> > > > + * @param vecp: pointer where the computed vector is put in case of
> > > > + *        success
> > > > + * @param standard_deviation the standard deviation used to generate
> > > > + *        the Gaussian vector, must be a non-negative value
> > > > + * @param quality the quality of the generated Gaussian vector, must
> > > > + *        be a non-negative value. It affects the lenght of the generated
> > > > + *        vector. A value equal to 3 corresponds to high quality.
> > > > + * @param log_ctx a pointer to an arbitrary struct of which the first
> > > > + *        field is a pointer to an AVClass struct (used for av_log)
> > > > + *        used for logging, can be NULL
> > > > + *
> > > > + * @return a negative error code on error, non negative otherwise
> > > > + */
> > > > +int sws_get_gaussian_vec(SwsVector **vecp,
> > > > +                         double standard_deviation, double quality,
> > > > +                         void *log_ctx);
> > > 
> > > which of the two do you consider better?
> > > 
> > > First, here the central part we return is the vector
> > > 
> > > SwsVector *gaus_vec = sws_getGaussianVec(NULL, 1, 2);
> > > SwsVector *temp_vec = sws_ConvolveVec(NULL, in_vec, gaus_vec);
> > > sws_averageVec(temp_vec, temp_vec, in_vec);
> > > 
> > > av_free(gaus_vec);
> > > return temp_vec; // Error checking here happens by temp_vec being NULL in all cases of error
> > > 
> > > vs.
> > > 
> > > Second, here the central part we return is the error code
> > > 
> > > SwsVector *gaus_vec = NULL;
> > > SwsVector *temp_vec = NULL;
> > > int err = sws_getGaussianVec(&gaus_vec, 1, 2);
> > > if (err<0)
> > >     goto fail;
> > > 
> > > err = sws_ConvolveVec(&temp_vec, in_vec, gaus_vec);
> > > if (err<0)
> > >     goto fail;
> > > 
> > > err = sws_averageVec(&temp_vec, temp_vec, in_vec);
> > > if (err<0)
> > >     goto fail;
> > 
> > The latter pattern enables differentiation between error codes (ENOMEM
> > or EINVAL) and provides feedback in the log message. With the former
> > you only know if it fails, but you don't know why (relevant in case
> > e.g. we make the parameter tunable by a filter and we don't want to
> > add additional validation and logging at the filter level).
> 

> can the API be designed so that optionally the user could choose to
> only check the error code after several steps ?
> (this would avoid the need for 1 check per call where the fine grained
>  information is not needed)
> I mean similar to the concept of NAN in floating point so that a failure
> can be propagated and only at the end checked.

Well, with the new approach you can do:

SwsVector *gaus_vec, *temp_vec, *avg_vec;

sws_get_gaussian_vec(&gaus_vec, 1, 2);
sws_get_convolution_vec(&temp_vec, in_vec, gaus_vec);
sws_get_average_vec(&avg_vec, temp_vec, in_vec);
 
av_free(gaus_vec);
av_free(temp_vec);
return avg_vec; // Error checking here happens by avg_vec being NULL in all cases of error

If you want to disable the log we could add a log_ctx_offset parameter.
Michael Niedermayer Sept. 3, 2023, 4:34 p.m. UTC | #13
On Sun, Sep 03, 2023 at 02:25:07AM +0200, Stefano Sabatini wrote:
> On date Saturday 2023-09-02 22:07:53 +0200, Michael Niedermayer wrote:
> > On Fri, Sep 01, 2023 at 08:38:26PM +0200, Stefano Sabatini wrote:
> > > On date Friday 2023-09-01 18:54:40 +0200, Michael Niedermayer wrote:
> > > > On Thu, Aug 31, 2023 at 07:16:20PM +0200, Stefano Sabatini wrote:
> > > [...]
> > > > > +/**
> > > > > + * Compute and return a normalized Gaussian vector.
> > > > > + *
> > > > > + * @param vecp: pointer where the computed vector is put in case of
> > > > > + *        success
> > > > > + * @param standard_deviation the standard deviation used to generate
> > > > > + *        the Gaussian vector, must be a non-negative value
> > > > > + * @param quality the quality of the generated Gaussian vector, must
> > > > > + *        be a non-negative value. It affects the lenght of the generated
> > > > > + *        vector. A value equal to 3 corresponds to high quality.
> > > > > + * @param log_ctx a pointer to an arbitrary struct of which the first
> > > > > + *        field is a pointer to an AVClass struct (used for av_log)
> > > > > + *        used for logging, can be NULL
> > > > > + *
> > > > > + * @return a negative error code on error, non negative otherwise
> > > > > + */
> > > > > +int sws_get_gaussian_vec(SwsVector **vecp,
> > > > > +                         double standard_deviation, double quality,
> > > > > +                         void *log_ctx);
> > > > 
> > > > which of the two do you consider better?
> > > > 
> > > > First, here the central part we return is the vector
> > > > 
> > > > SwsVector *gaus_vec = sws_getGaussianVec(NULL, 1, 2);
> > > > SwsVector *temp_vec = sws_ConvolveVec(NULL, in_vec, gaus_vec);
> > > > sws_averageVec(temp_vec, temp_vec, in_vec);
> > > > 
> > > > av_free(gaus_vec);
> > > > return temp_vec; // Error checking here happens by temp_vec being NULL in all cases of error
> > > > 
> > > > vs.
> > > > 
> > > > Second, here the central part we return is the error code
> > > > 
> > > > SwsVector *gaus_vec = NULL;
> > > > SwsVector *temp_vec = NULL;
> > > > int err = sws_getGaussianVec(&gaus_vec, 1, 2);
> > > > if (err<0)
> > > >     goto fail;
> > > > 
> > > > err = sws_ConvolveVec(&temp_vec, in_vec, gaus_vec);
> > > > if (err<0)
> > > >     goto fail;
> > > > 
> > > > err = sws_averageVec(&temp_vec, temp_vec, in_vec);
> > > > if (err<0)
> > > >     goto fail;
> > > 
> > > The latter pattern enables differentiation between error codes (ENOMEM
> > > or EINVAL) and provides feedback in the log message. With the former
> > > you only know if it fails, but you don't know why (relevant in case
> > > e.g. we make the parameter tunable by a filter and we don't want to
> > > add additional validation and logging at the filter level).
> > 
> 
> > can the API be designed so that optionally the user could choose to
> > only check the error code after several steps ?
> > (this would avoid the need for 1 check per call where the fine grained
> >  information is not needed)
> > I mean similar to the concept of NAN in floating point so that a failure
> > can be propagated and only at the end checked.
> 
> Well, with the new approach you can do:
> 
> SwsVector *gaus_vec, *temp_vec, *avg_vec;
> 
> sws_get_gaussian_vec(&gaus_vec, 1, 2);
> sws_get_convolution_vec(&temp_vec, in_vec, gaus_vec);
> sws_get_average_vec(&avg_vec, temp_vec, in_vec);
>  
> av_free(gaus_vec);
> av_free(temp_vec);
> return avg_vec; // Error checking here happens by avg_vec being NULL in all cases of error

ok

> 
> If you want to disable the log we could add a log_ctx_offset parameter.

as long as theres a pointer to a log context that should be enough.
A log context can modify the log level

passing a seperate and mandatory level offset per call would be a bit ugly

thx

[...]
Anton Khirnov Sept. 5, 2023, 11:19 a.m. UTC | #14
Quoting Stefano Sabatini (2023-09-01 20:28:33)
> On date Friday 2023-09-01 17:50:56 +0200, Anton Khirnov wrote:
> > Quoting Stefano Sabatini (2023-08-31 17:06:06)
> > > On date Saturday 2023-08-26 17:15:36 +0200, Anton Khirnov wrote:
> > > > Quoting Stefano Sabatini (2023-08-26 14:23:28)
> > > > > Use in place of sws_getGaussianVec.
> > > > > 
> > > > > The new function enable better log handling, and provide better naming
> > > > > for the variance variable, now named standard_deviation to reflect the
> > > > > meaning of the parameter.
> > > > 
> > > 
> > > > Logging to NULL does not seem like an improvement to me.
> > > 
> > > Adding the log_ctx.
> > > 
> > > > Renaming a function parameter does not require an API break.
> > > 
> > > The main point was improving the naming of the variable, but while at
> > > it I'm also adding the logging context and providing a return code to
> > > specify an error failure, and moving to snake_case convention which is
> > > the one used by the new API additions.
> > 
> > As I already said above - function parameter names in a prototype are
> > purely cosmetic and have no effect on anything besides doxygen. You can
> > change them at will and even remove them entirely without breaking API
> > or ABI.
> > 
> 
> > The other reasons do not strike me as strong enough to warrant an API
> > break.
> 
> I disagree on this: the function is probably only used internally and
> by libavfilter, and the migration is trivial enough so should cause no
> disruption anyway.

The migration is not trivial for someone who is not familiar with the
code (such as a distro package maintainer), since the new function has a
different signature. I really do not think we should break APIs for
frivolous reasons, which includes cosmetic ones.
Stefano Sabatini Sept. 5, 2023, 10:59 p.m. UTC | #15
On date Tuesday 2023-09-05 13:19:00 +0200, Anton Khirnov wrote:
> Quoting Stefano Sabatini (2023-09-01 20:28:33)
> > On date Friday 2023-09-01 17:50:56 +0200, Anton Khirnov wrote:
> > > Quoting Stefano Sabatini (2023-08-31 17:06:06)
> > > > On date Saturday 2023-08-26 17:15:36 +0200, Anton Khirnov wrote:
> > > > > Quoting Stefano Sabatini (2023-08-26 14:23:28)
> > > > > > Use in place of sws_getGaussianVec.
> > > > > > 
> > > > > > The new function enable better log handling, and provide better naming
> > > > > > for the variance variable, now named standard_deviation to reflect the
> > > > > > meaning of the parameter.
> > > > > 
> > > > 
> > > > > Logging to NULL does not seem like an improvement to me.
> > > > 
> > > > Adding the log_ctx.
> > > > 
> > > > > Renaming a function parameter does not require an API break.
> > > > 
> > > > The main point was improving the naming of the variable, but while at
> > > > it I'm also adding the logging context and providing a return code to
> > > > specify an error failure, and moving to snake_case convention which is
> > > > the one used by the new API additions.
> > > 
> > > As I already said above - function parameter names in a prototype are
> > > purely cosmetic and have no effect on anything besides doxygen. You can
> > > change them at will and even remove them entirely without breaking API
> > > or ABI.
> > > 
> > 
> > > The other reasons do not strike me as strong enough to warrant an API
> > > break.
> > 

> > I disagree on this: the function is probably only used internally and
> > by libavfilter, and the migration is trivial enough so should cause no
> > disruption anyway.
> 

> The migration is not trivial for someone who is not familiar with the
> code (such as a distro package maintainer), since the new function has a
> different signature. I really do not think we should break APIs for
> frivolous reasons, which includes cosmetic ones.

Following this logic every API change should be considered not trivial
for someone not familiar with the code, and therefore should not be
committed. Also there is no evidence that external components are
using this function.

Besides the naming change, there are ergonomic and functional changes
making the behavior of the code more correct.

(Also probably it would be worth moving the naming of all the
remaining functions to snake_case to provide a consistent API - and
reduce the cognitive overload on the API user).
Anton Khirnov Sept. 6, 2023, 11:13 a.m. UTC | #16
Quoting Stefano Sabatini (2023-09-06 00:59:44)
> > > > As I already said above - function parameter names in a prototype are
> > > > purely cosmetic and have no effect on anything besides doxygen. You can
> > > > change them at will and even remove them entirely without breaking API
> > > > or ABI.
> > > > 
> > > 
> > > > The other reasons do not strike me as strong enough to warrant an API
> > > > break.
> > > 
> 
> > > I disagree on this: the function is probably only used internally and
> > > by libavfilter, and the migration is trivial enough so should cause no
> > > disruption anyway.
> > 
> 
> > The migration is not trivial for someone who is not familiar with the
> > code (such as a distro package maintainer), since the new function has a
> > different signature. I really do not think we should break APIs for
> > frivolous reasons, which includes cosmetic ones.
> 
> Following this logic every API change should be considered not trivial
> for someone not familiar with the code,

A simple rename is a trivial API change. Almost everything else is not.

> and therefore should not be committed.

Yes, the baseline for every API change is that it is undesirable and you
must supply sufficiently strong arguments to overcome that.

> Also there is no evidence that external components are using this
> function.
>
> Besides the naming change, there are ergonomic and functional changes
> making the behavior of the code more correct.

I do not see the code being made more correct, but Michael observed in
this thread that it becomes longer and more convoluted.

I am not convinced that adding logging to this function is an
improvement. You have to pass an extra parameter to every call, making
the code longer and less readable. We do not need a dedicated error
message for every malloc.
Stefano Sabatini Nov. 4, 2023, 9:38 p.m. UTC | #17
On date Wednesday 2023-09-06 13:13:25 +0200, Anton Khirnov wrote:
> Quoting Stefano Sabatini (2023-09-06 00:59:44)
[...]
> A simple rename is a trivial API change. Almost everything else is not.
> 
> > and therefore should not be committed.
> 
> Yes, the baseline for every API change is that it is undesirable and you
> must supply sufficiently strong arguments to overcome that.
> 
> > Also there is no evidence that external components are using this
> > function.
> >

> > Besides the naming change, there are ergonomic and functional changes
> > making the behavior of the code more correct.
> 

> I do not see the code being made more correct, but Michael observed in
> this thread that it becomes longer and more convoluted.

It is a different construct, not necessarily more convoluted. Also
keep in mind this is needed in order to achive two goals:

1. return a proper error code since we can have two types of failures
here (memalloc or invalid argument)

2. log the invalid argument reason so that the failure reason is in
the log, without having to repeat the validation logic in the caller

> I am not convinced that adding logging to this function is an
> improvement. You have to pass an extra parameter to every call, making
> the code longer and less readable. We do not need a dedicated error
> message for every malloc.

See above, in this case we can have ENOMEM/EINVAL and it should be
possible to distinguish between the two.
diff mbox series

Patch

diff --git a/doc/APIchanges b/doc/APIchanges
index ad1efe708d..bad2d61027 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -2,6 +2,9 @@  The last version increases of all libraries were on 2023-02-09
 
 API changes, most recent first:
 
+2023-08-26 - xxxxxxxxxx - lsws 7.4.100 - swscale.h
+  Introduce sws_get_gaussian_vec, use in place of sws_getGaussianVec.
+
 2023-08-18 - xxxxxxxxxx - lavu 58.17.100 - channel_layout.h
   All AV_CHANNEL_LAYOUT_* macros are now compatible with C++ 17 and older.
 
diff --git a/libswscale/swscale.h b/libswscale/swscale.h
index 9d4612aaf3..f002b5c7d2 100644
--- a/libswscale/swscale.h
+++ b/libswscale/swscale.h
@@ -355,11 +355,30 @@  int sws_getColorspaceDetails(struct SwsContext *c, int **inv_table,
  */
 SwsVector *sws_allocVec(int length);
 
+#if FF_API_SWS_GET_GAUSSIAN_VEC
 /**
- * Return a normalized Gaussian curve used to filter stuff
+ * Return a normalized Gaussian curve used to filter stuff.
+ *
  * quality = 3 is high quality, lower is lower quality.
  */
 SwsVector *sws_getGaussianVec(double variance, double quality);
+#endif
+
+/**
+ * Compute and return a normalized Gaussian vector.
+ *
+ * @param vecp: pointer where the computed vector is put in case of
+ *        success
+ * @param standard_deviation the standard deviation used to generate
+ *        the Gaussian vector, must be a non-negative value
+ * @param quality the quality of the generated Gaussian vector, must
+ *        be a non-negative value. It affects the lenght of the generated
+ *        vector. A value equal to 3 corresponds to high quality.
+ *
+ * @return a negative error code on error, non negative otherwise
+ */
+int sws_get_gaussian_vec(SwsVector **vecp,
+                         double standard_deviation, double quality);
 
 /**
  * Scale all the coefficients of a by the scalar value.
diff --git a/libswscale/utils.c b/libswscale/utils.c
index 8e74c6603e..96034af1e0 100644
--- a/libswscale/utils.c
+++ b/libswscale/utils.c
@@ -2139,31 +2139,54 @@  SwsVector *sws_allocVec(int length)
     return vec;
 }
 
-SwsVector *sws_getGaussianVec(double variance, double quality)
+int sws_get_gaussian_vec(SwsVector **vecp,
+                         double standard_deviation, double quality)
 {
-    const int length = (int)(variance * quality + 0.5) | 1;
+    const int length = (int)(standard_deviation * quality + 0.5) | 1;
     int i;
     double middle  = (length - 1) * 0.5;
     SwsVector *vec;
+    const double variance = standard_deviation * standard_deviation;
 
-    if(variance < 0 || quality < 0)
-        return NULL;
+    if (standard_deviation < 0 || quality < 0) {
+        av_log(NULL, AV_LOG_ERROR,
+               "Invalid negative standard deviation %f or quality %f provided as input to the sws_get_gaussian_vec function\n",
+               standard_deviation, quality);
+        return AVERROR(EINVAL);
+    }
 
     vec = sws_allocVec(length);
-
-    if (!vec)
-        return NULL;
+    if (!vec) {
+        av_log(NULL, AV_LOG_ERROR,
+               "Could not allocate vector for the sws_get_gaussian_vec function\n");
+        return AVERROR(ENOMEM);
+    }
 
     for (i = 0; i < length; i++) {
         double dist = i - middle;
-        vec->coeff[i] = exp(-dist * dist / (2 * variance * variance)) /
-                        sqrt(2 * variance * M_PI);
+        vec->coeff[i] = exp(-dist * dist / (2 * variance)) /
+                        sqrt(2 * standard_deviation * M_PI);
     }
 
     sws_normalizeVec(vec, 1.0);
+    *vecp = vec;
 
+    return 0;
+}
+
+#if FF_API_SWS_GET_GAUSSIAN_VEC
+SwsVector *sws_getGaussianVec(double variance, double quality)
+{
+    SwsVector *vec;
+    int ret;
+
+    ret = sws_get_gaussian_vec(&vec, variance, quality);
+    if (ret < 0) {
+        return NULL;
+    }
     return vec;
 }
+#endif // FF_API_SWS_GET_GAUSSIAN_VEC
 
 /**
  * Allocate and return a vector with length coefficients, all
diff --git a/libswscale/version.h b/libswscale/version.h
index 51eb013a29..12412bd538 100644
--- a/libswscale/version.h
+++ b/libswscale/version.h
@@ -28,7 +28,7 @@ 
 
 #include "version_major.h"
 
-#define LIBSWSCALE_VERSION_MINOR   3
+#define LIBSWSCALE_VERSION_MINOR   4
 #define LIBSWSCALE_VERSION_MICRO 100
 
 #define LIBSWSCALE_VERSION_INT  AV_VERSION_INT(LIBSWSCALE_VERSION_MAJOR, \
diff --git a/libswscale/version_major.h b/libswscale/version_major.h
index 88577a2b42..aa0baef7c6 100644
--- a/libswscale/version_major.h
+++ b/libswscale/version_major.h
@@ -32,4 +32,8 @@ 
  * the public API and may change, break or disappear at any time.
  */
 
+#ifndef FF_API_SWS_GET_GAUSSIAN_VEC
+#define FF_API_SWS_GET_GAUSSIAN_VEC       (LIBSWSCALE_VERSION_MAJOR < 8)
+#endif
+
 #endif /* SWSCALE_VERSION_MAJOR_H */