[vlc-devel] [PATCH 4/4] filters: add an operations structure to set the callbacks

Thomas Guillem thomas at gllm.fr
Tue Oct 6 09:14:08 CEST 2020


This patch should be named: "VLC filters : A New Ops"

OK, sorrry, real review below...

On Mon, Oct 5, 2020, at 17:03, Steve Lhomme wrote:
> From: Alexandre Janniaux <ajanni at videolabs.io>
> 
> Generate a simple operations structure for filters generating their own filter
> callback via VIDEO_FILTER_WRAPPER().
> 
> The filter chain sets a mouse handler on video filters that don't have one,
> just as before, by using a local version of the ops structure of the filter and
> adding the local mouse callback.
> 
> Co-authored-by: Steve Lhomme <robux4 at ycbcr.xyz>
> ---
>  include/vlc_filter.h                          | 124 ++++++++++--------
>  modules/access/screen/screen.c                |   2 +-
>  modules/arm_neon/chroma_yuv.c                 |  44 +++----
>  modules/arm_neon/yuv_rgb.c                    |  10 +-
>  modules/audio_filter/audiobargraph_a.c        |   7 +-
>  modules/audio_filter/center.c                 |   7 +-
>  modules/audio_filter/channel_mixer/dolby.c    |   6 +-
>  .../audio_filter/channel_mixer/headphone.c    |   7 +-
>  modules/audio_filter/channel_mixer/mono.c     |   7 +-
>  modules/audio_filter/channel_mixer/remap.c    |   6 +-
>  modules/audio_filter/channel_mixer/simple.c   |   8 +-
>  .../channel_mixer/spatialaudio.cpp            |  18 ++-
>  modules/audio_filter/channel_mixer/trivial.c  |  24 +++-
>  modules/audio_filter/chorus_flanger.c         |  11 +-
>  modules/audio_filter/compressor.c             |   7 +-
>  modules/audio_filter/converter/format.c       |  14 +-
>  modules/audio_filter/converter/tospdif.c      |   8 +-
>  modules/audio_filter/equalizer.c              |   7 +-
>  modules/audio_filter/gain.c                   |   6 +-
>  modules/audio_filter/karaoke.c                |   7 +-
>  modules/audio_filter/libebur128.c             |   7 +-
>  modules/audio_filter/normvol.c                |   6 +-
>  modules/audio_filter/param_eq.c               |  20 +--
>  modules/audio_filter/resampler/bandlimited.c  |   6 +-
>  modules/audio_filter/resampler/soxr.c         |  10 +-
>  modules/audio_filter/resampler/speex.c        |   6 +-
>  modules/audio_filter/resampler/src.c          |   7 +-
>  modules/audio_filter/resampler/ugly.c         |   6 +-
>  modules/audio_filter/scaletempo.c             |  14 +-
>  .../audio_filter/spatializer/spatializer.cpp  |  12 +-
>  modules/audio_filter/stereo_widen.c           |   6 +-
>  modules/hw/d3d11/d3d11_deinterlace.c          |   7 +-
>  modules/hw/d3d11/d3d11_filters.c              |   6 +-
>  modules/hw/d3d11/d3d11_surface.c              |  20 +--
>  modules/hw/d3d9/d3d9_filters.c                |   6 +-
>  modules/hw/d3d9/dxa9.c                        |  16 ++-
>  modules/hw/d3d9/dxva2_deinterlace.c           |   7 +-
>  modules/hw/mmal/converter.c                   |   7 +-
>  modules/hw/mmal/deinterlace.c                 |  14 +-
>  modules/hw/nvdec/chroma.c                     |   6 +-
>  modules/hw/vaapi/chroma.c                     |  10 +-
>  modules/hw/vaapi/filters.c                    |  24 +++-
>  modules/hw/vdpau/adjust.c                     |   6 +-
>  modules/hw/vdpau/chroma.c                     |  23 +++-
>  modules/hw/vdpau/deinterlace.c                |   6 +-
>  modules/hw/vdpau/sharpen.c                    |   6 +-
>  modules/spu/audiobargraph_v.c                 |  12 +-
>  modules/spu/dynamicoverlay/dynamicoverlay.c   |   6 +-
>  modules/spu/logo.c                            |  18 ++-
>  modules/spu/marq.c                            |   6 +-
>  modules/spu/mosaic.c                          |   6 +-
>  modules/spu/rss.c                             |   6 +-
>  modules/spu/subsdelay.c                       |   6 +-
>  modules/text_renderer/freetype/freetype.c     |   7 +-
>  modules/text_renderer/nsspeechsynthesizer.m   |   6 +-
>  modules/text_renderer/sapi.cpp                |  10 +-
>  modules/text_renderer/svg.c                   |   6 +-
>  modules/text_renderer/tdummy.c                |   6 +-
>  modules/video_chroma/chain.c                  |  15 ++-
>  modules/video_chroma/cvpx.c                   |  28 +++-
>  modules/video_chroma/grey_yuv.c               |  29 ++--
>  modules/video_chroma/i420_nv12.c              |  12 +-
>  modules/video_chroma/i420_rgb.c               |  18 +--
>  modules/video_chroma/i420_yuy2.c              |  59 +++++----
>  modules/video_chroma/i422_i420.c              |   6 +-
>  modules/video_chroma/i422_yuy2.c              |  69 +++++-----
>  modules/video_chroma/omxdl.c                  |  52 ++++----
>  modules/video_chroma/rv32.c                   |   6 +-
>  modules/video_chroma/swscale.c                |   6 +-
>  modules/video_chroma/yuvp.c                   |   2 +-
>  modules/video_chroma/yuy2_i420.c              |   6 +-
>  modules/video_chroma/yuy2_i422.c              |   6 +-
>  modules/video_filter/adjust.c                 |  16 ++-
>  modules/video_filter/alphamask.c              |   7 +-
>  modules/video_filter/anaglyph.c               |   8 +-
>  modules/video_filter/antiflicker.c            |   7 +-
>  modules/video_filter/ball.c                   |   6 +-
>  modules/video_filter/blend.cpp                |  13 +-
>  modules/video_filter/blendbench.c             |  12 +-
>  modules/video_filter/bluescreen.c             |   7 +-
>  modules/video_filter/canvas.c                 |   7 +-
>  modules/video_filter/ci_filters.m             |  14 +-
>  modules/video_filter/colorthres.c             |  14 +-
>  modules/video_filter/croppadd.c               |   7 +-
>  .../video_filter/deinterlace/deinterlace.c    |  12 +-
>  modules/video_filter/edgedetection.c          |   7 +-
>  modules/video_filter/erase.c                  |   7 +-
>  modules/video_filter/extract.c                |   7 +-
>  modules/video_filter/fps.c                    |   7 +-
>  modules/video_filter/freeze.c                 |   9 +-
>  modules/video_filter/gaussianblur.c           |   7 +-
>  modules/video_filter/gradfun.c                |  10 +-
>  modules/video_filter/gradient.c               |   7 +-
>  modules/video_filter/grain.c                  |  10 +-
>  modules/video_filter/hqdn3d.c                 |   7 +-
>  modules/video_filter/invert.c                 |   6 +-
>  modules/video_filter/magnify.c                |   9 +-
>  modules/video_filter/mirror.c                 |   6 +-
>  modules/video_filter/motionblur.c             |   6 +-
>  modules/video_filter/motiondetect.c           |   6 +-
>  modules/video_filter/oldmovie.c               |   6 +-
>  modules/video_filter/opencv_example.cpp       |  12 +-
>  modules/video_filter/opencv_wrapper.c         |   9 +-
>  modules/video_filter/posterize.c              |   6 +-
>  modules/video_filter/postproc.c               |   6 +-
>  modules/video_filter/psychedelic.c            |   6 +-
>  modules/video_filter/puzzle.c                 |   8 +-
>  modules/video_filter/ripple.c                 |   7 +-
>  modules/video_filter/rotate.c                 |  14 +-
>  modules/video_filter/scale.c                  |   7 +-
>  modules/video_filter/scene.c                  |   8 +-
>  modules/video_filter/sepia.c                  |   6 +-
>  modules/video_filter/sharpen.c                |   6 +-
>  modules/video_filter/transform.c              |   8 +-
>  modules/video_filter/vhs.c                    |   6 +-
>  modules/video_filter/wave.c                   |   6 +-
>  modules/visualization/glspectrum.c            |   6 +-
>  modules/visualization/goom.c                  |   8 +-
>  modules/visualization/projectm.cpp            |  10 +-
>  modules/visualization/visual/visual.c         |   8 +-
>  modules/visualization/vsxu.cpp                |  10 +-
>  src/audio_output/filters.c                    |   6 +-
>  src/audio_output/meter.c                      |   8 +-
>  src/misc/filter.c                             |   2 +-
>  src/misc/filter_chain.c                       |  10 +-
>  src/misc/image.c                              |   6 +-
>  src/video_output/vout_subpictures.c           |   6 +-
>  127 files changed, 1007 insertions(+), 438 deletions(-)
> 
> diff --git a/include/vlc_filter.h b/include/vlc_filter.h
> index d9c274a645a..0ced8ea03d6 100644
> --- a/include/vlc_filter.h
> +++ b/include/vlc_filter.h
> @@ -79,68 +79,44 @@ typedef struct filter_owner_t
>  
>  struct vlc_mouse_t;
>  
> -/** Structure describing a filter
> - * @warning BIG FAT WARNING : the code relies on the first 4 members of
> - * filter_t and decoder_t to be the same, so if you have anything to add,
> - * do it at the end of the structure.
> - */
> -struct filter_t
> +struct vlc_filter_operations
>  {
> -    struct vlc_object_t obj;
> -
> -    /* Module properties */
> -    module_t *          p_module;
> -    void               *p_sys;
> -
> -    /* Input format */
> -    es_format_t         fmt_in;
> -    vlc_video_context   *vctx_in;  // video filter, set by owner
> -
> -    /* Output format of filter */
> -    es_format_t         fmt_out;
> -    vlc_video_context   *vctx_out; // video filter, handled by the filter
> -    bool                b_allow_fmt_out_change;
> -
> -    /* Name of the "video filter" shortcut that is requested, can be NULL */
> -    const char *        psz_name;
> -    /* Filter configuration */
> -    config_chain_t *    p_cfg;
> -
> +    /* Operation depending on the type of filter. */
>      union
>      {
>          /** Filter a picture (video filter) */
> -        picture_t * (*pf_video_filter)( filter_t *, picture_t * );
> +        picture_t * (*filter_video)(filter_t *, picture_t *);
>  
>          /** Filter an audio block (audio filter) */
> -        block_t * (*pf_audio_filter)( filter_t *, block_t * );
> +        block_t * (*filter_audio)(filter_t *, block_t *);
>  
>          /** Blend a subpicture onto a picture (blend) */
> -        void (*pf_video_blend)( filter_t *,  picture_t *, const picture_t *,
> -                                 int, int, int );
> +        void (*blend_video)(filter_t *,  picture_t *, const picture_t *,
> +                            int, int, int);
>  
>          /** Generate a subpicture (sub source) */
> -        subpicture_t *(*pf_sub_source)( filter_t *, vlc_tick_t );
> +        subpicture_t *(*source_sub)(filter_t *, vlc_tick_t);
>  
>          /** Filter a subpicture (sub filter) */
> -        subpicture_t *(*pf_sub_filter)( filter_t *, subpicture_t * );
> +        subpicture_t *(*filter_sub)(filter_t *, subpicture_t *);
>  
>          /** Render text (text render) */
> -        int (*pf_render)( filter_t *, subpicture_region_t *,
> -                          subpicture_region_t *, const vlc_fourcc_t * );
> +        int (*render)(filter_t *, subpicture_region_t *,
> +                      subpicture_region_t *, const vlc_fourcc_t *);
>      };
>  
>      union
>      {
>          /* TODO: video filter drain */
>          /** Drain (audio filter) */
> -        block_t *(*pf_audio_drain) ( filter_t * );
> +        block_t *(*drain_audio)(filter_t *);
>      };
>  
>      /** Flush
>       *
>       * Flush (i.e. discard) any internal buffer in a video or audio filter.
>       */
> -    void (*pf_flush)( filter_t * );
> +    void (*flush)(filter_t *);
>  
>      /** Change viewpoint
>       *
> @@ -148,20 +124,49 @@ struct filter_t
>       * used for Ambisonics rendering will change its output according to this
>       * viewpoint.
>       */
> -    void (*pf_change_viewpoint)( filter_t *, const vlc_viewpoint_t * );
> +    void (*change_viewpoint)(filter_t *, const vlc_viewpoint_t *);
>  
> -    union
> -    {
> -        /** Filter mouse state (video filter).
> -         *
> -         * If non-NULL, you must convert from output to input formats:
> -         * - If VLC_SUCCESS is returned, the mouse state is propagated.
> -         * - Otherwise, the mouse change is not propagated.
> -         * If NULL, the mouse state is considered unchanged and will be
> -         * propagated. */
> -        int (*pf_video_mouse)( filter_t *, struct vlc_mouse_t *,
> -                               const struct vlc_mouse_t *p_old);
> -    };
> +    /** Filter mouse state (video filter).
> +     *
> +     * If non-NULL, you must convert from output to input formats:
> +     * - If VLC_SUCCESS is returned, the mouse state is propagated.
> +     * - Otherwise, the mouse change is not propagated.
> +     * If NULL, the mouse state is considered unchanged and will be
> +     * propagated. */
> +    int (*video_mouse)(filter_t *, struct vlc_mouse_t *,
> +                       const struct vlc_mouse_t *p_old);
> +
> +};
> +
> +/** Structure describing a filter
> + * @warning BIG FAT WARNING : the code relies on the first 4 members of
> + * filter_t and decoder_t to be the same, so if you have anything to add,
> + * do it at the end of the structure.
> + */
> +struct filter_t
> +{
> +    struct vlc_object_t obj;
> +
> +    /* Module properties */
> +    module_t *          p_module;
> +    void               *p_sys;
> +
> +    /* Input format */
> +    es_format_t         fmt_in;
> +    vlc_video_context   *vctx_in;  // video filter, set by owner
> +
> +    /* Output format of filter */
> +    es_format_t         fmt_out;
> +    vlc_video_context   *vctx_out; // video filter, handled by the filter
> +    bool                b_allow_fmt_out_change;
> +
> +    /* Name of the "video filter" shortcut that is requested, can be NULL */
> +    const char *        psz_name;
> +    /* Filter configuration */
> +    config_chain_t *    p_cfg;
> +
> +    /* Implementation of filter API */
> +    const struct vlc_filter_operations *ops;
>  
>      /** Private structure for the owner of the filter */
>      filter_owner_t      owner;
> @@ -170,7 +175,7 @@ struct filter_t
>  /**
>   * This function will return a new picture usable by p_filter as an 
> output
>   * buffer. You have to release it using picture_Release or by returning
> - * it to the caller as a pf_video_filter return value.
> + * it to the caller as a ops->filter_video return value.
>   * Provided for convenience.
>   *
>   * \param p_filter filter_t object
> @@ -198,15 +203,15 @@ static inline picture_t *filter_NewPicture( 
> filter_t *p_filter )
>   */
>  static inline void filter_Flush( filter_t *p_filter )
>  {
> -    if( p_filter->pf_flush != NULL )
> -        p_filter->pf_flush( p_filter );
> +    if( p_filter->ops && p_filter->ops->flush != NULL )

filters->ops should be mandatory no ?

> +        p_filter->ops->flush( p_filter );
>  }
>  
>  static inline void filter_ChangeViewpoint( filter_t *p_filter,
>                                             const vlc_viewpoint_t *vp)
>  {
> -    if( p_filter->pf_change_viewpoint != NULL )
> -        p_filter->pf_change_viewpoint( p_filter, vp );
> +    if( p_filter->ops && p_filter->ops->change_viewpoint != NULL )
> +        p_filter->ops->change_viewpoint( p_filter, vp );
>  }
>  
>  static inline vlc_decoder_device * filter_HoldDecoderDevice( filter_t 
> *p_filter )
> @@ -239,8 +244,8 @@ static inline vlc_decoder_device * 
> filter_HoldDecoderDeviceType( filter_t *p_fil
>   */
>  static inline block_t *filter_DrainAudio( filter_t *p_filter )
>  {
> -    if( p_filter->pf_audio_drain )
> -        return p_filter->pf_audio_drain( p_filter );
> +    if( p_filter->ops && p_filter->ops->drain_audio )
> +        return p_filter->ops->drain_audio( p_filter );
>      else
>          return NULL;
>  }
> @@ -255,7 +260,7 @@ static inline void 
> filter_SendAudioLoudness(filter_t *filter,
>  /**
>   * This function will return a new subpicture usable by p_filter as an 
> output
>   * buffer. You have to release it using subpicture_Delete or by 
> returning it to
> - * the caller as a pf_sub_source return value.
> + * the caller as a ops->sub_source return value.
>   * Provided for convenience.
>   *
>   * \param p_filter filter_t object
> @@ -353,7 +358,10 @@ VLC_API void filter_DeleteBlend( vlc_blender_t * );
>          }                                                              
>  \
>          picture_Release( p_pic );                                      
>  \
>          return p_outpic;                                               
>  \
> -    }
> +    }                                                                  
>  \
> +    static const struct vlc_filter_operations name ## _ops = {         
>  \
> +        .filter_video = name ## _Filter,                               
>  \
> +    };
>  
>  /**
>   * Filter chain management API
> diff --git a/modules/access/screen/screen.c 
> b/modules/access/screen/screen.c
> index 0c472deaa8f..fda1417eb0c 100644
> --- a/modules/access/screen/screen.c
> +++ b/modules/access/screen/screen.c
> @@ -390,7 +390,7 @@ void RenderCursor( demux_t *p_demux, int i_x, int 
> i_y,
>      if( p_sys->p_blend )
>      {
>          p_sys->dst.p->p_pixels = p_dst;
> -        p_sys->p_blend->pf_video_blend( p_sys->p_blend,
> +        p_sys->p_blend->ops->blend_video( p_sys->p_blend,
>                                          &p_sys->dst,
>                                          p_sys->p_mouse,
>  #ifdef SCREEN_SUBSCREEN
> diff --git a/modules/arm_neon/chroma_yuv.c 
> b/modules/arm_neon/chroma_yuv.c
> index 53fe0f86f51..6715f228a97 100644
> --- a/modules/arm_neon/chroma_yuv.c
> +++ b/modules/arm_neon/chroma_yuv.c
> @@ -231,16 +231,16 @@ static int Open (vlc_object_t *obj)
>              switch (filter->fmt_out.video.i_chroma)
>              {
>                  case VLC_CODEC_YUYV:
> -                    filter->pf_video_filter = I420_YUYV_Filter;
> +                    filter->ops = &I420_YUYV_ops;
>                      break;
>                  case VLC_CODEC_UYVY:
> -                    filter->pf_video_filter = I420_UYVY_Filter;
> +                    filter->ops = &I420_UYVY_ops;
>                      break;
>                  case VLC_CODEC_YVYU:
> -                    filter->pf_video_filter = I420_YVYU_Filter;
> +                    filter->ops = &I420_YVYU_ops;
>                      break;
>                  case VLC_CODEC_VYUY:
> -                    filter->pf_video_filter = I420_VYUY_Filter;
> +                    filter->ops = &I420_VYUY_ops;
>                      break;
>                  default:
>                      return VLC_EGENERIC;
> @@ -251,16 +251,16 @@ static int Open (vlc_object_t *obj)
>              switch (filter->fmt_out.video.i_chroma)
>              {
>                  case VLC_CODEC_YUYV:
> -                    filter->pf_video_filter = I420_YVYU_Filter;
> +                    filter->ops = &I420_YVYU_ops;
>                      break;
>                  case VLC_CODEC_UYVY:
> -                    filter->pf_video_filter = I420_VYUY_Filter;
> +                    filter->ops = &I420_VYUY_ops;
>                      break;
>                  case VLC_CODEC_YVYU:
> -                    filter->pf_video_filter = I420_YUYV_Filter;
> +                    filter->ops = &I420_YUYV_ops;
>                      break;
>                  case VLC_CODEC_VYUY:
> -                    filter->pf_video_filter = I420_UYVY_Filter;
> +                    filter->ops = &I420_UYVY_ops;
>                      break;
>                  default:
>                      return VLC_EGENERIC;
> @@ -271,16 +271,16 @@ static int Open (vlc_object_t *obj)
>              switch (filter->fmt_out.video.i_chroma)
>              {
>                  case VLC_CODEC_YUYV:
> -                    filter->pf_video_filter = I422_YUYV_Filter;
> +                    filter->ops = &I422_YUYV_ops;
>                      break;
>                  case VLC_CODEC_UYVY:
> -                    filter->pf_video_filter = I422_UYVY_Filter;
> +                    filter->ops = &I422_UYVY_ops;
>                      break;
>                  case VLC_CODEC_YVYU:
> -                    filter->pf_video_filter = I422_YVYU_Filter;
> +                    filter->ops = &I422_YVYU_ops;
>                      break;
>                  case VLC_CODEC_VYUY:
> -                    filter->pf_video_filter = I422_VYUY_Filter;
> +                    filter->ops = &I422_VYUY_ops;
>                      break;
>                  default:
>                      return VLC_EGENERIC;
> @@ -292,10 +292,10 @@ static int Open (vlc_object_t *obj)
>              switch (filter->fmt_out.video.i_chroma)
>              {
>                  case VLC_CODEC_I420:
> -                    filter->pf_video_filter = 
> Semiplanar_Planar_420_Filter;
> +                    filter->ops = &Semiplanar_Planar_420_ops;
>                      break;
>                  case VLC_CODEC_YV12:
> -                    filter->pf_video_filter = 
> Semiplanar_Planar_420_Swap_Filter;
> +                    filter->ops = &Semiplanar_Planar_420_Swap_ops;
>                      break;
>                  default:
>                      return VLC_EGENERIC;
> @@ -306,10 +306,10 @@ static int Open (vlc_object_t *obj)
>              switch (filter->fmt_out.video.i_chroma)
>              {
>                  case VLC_CODEC_I420:
> -                    filter->pf_video_filter = 
> Semiplanar_Planar_420_Swap_Filter;
> +                    filter->ops = &Semiplanar_Planar_420_Swap_ops;
>                      break;
>                  case VLC_CODEC_YV12:
> -                    filter->pf_video_filter = 
> Semiplanar_Planar_420_Filter;
> +                    filter->ops = &Semiplanar_Planar_420_ops;
>                      break;
>                  default:
>                      return VLC_EGENERIC;
> @@ -320,7 +320,7 @@ static int Open (vlc_object_t *obj)
>              switch (filter->fmt_out.video.i_chroma)
>              {
>                  case VLC_CODEC_I422:
> -                    filter->pf_video_filter = 
> Semiplanar_Planar_422_Filter;
> +                    filter->ops = &Semiplanar_Planar_422_ops;
>                      break;
>                  default:
>                      return VLC_EGENERIC;
> @@ -331,7 +331,7 @@ static int Open (vlc_object_t *obj)
>              switch (filter->fmt_out.video.i_chroma)
>              {
>                  case VLC_CODEC_I444:
> -                    filter->pf_video_filter = 
> Semiplanar_Planar_444_Filter;
> +                    filter->ops = &Semiplanar_Planar_444_ops;
>                      break;
>                  default:
>                      return VLC_EGENERIC;
> @@ -343,7 +343,7 @@ static int Open (vlc_object_t *obj)
>              switch (filter->fmt_out.video.i_chroma)
>              {
>                  case VLC_CODEC_I422:
> -                    filter->pf_video_filter = YUYV_I422_Filter;
> +                    filter->ops = &YUYV_I422_ops;
>                      break;
>                  default:
>                      return VLC_EGENERIC;
> @@ -353,7 +353,7 @@ static int Open (vlc_object_t *obj)
>              switch (filter->fmt_out.video.i_chroma)
>              {
>                  case VLC_CODEC_I422:
> -                    filter->pf_video_filter = UYVY_I422_Filter;
> +                    filter->ops = &UYVY_I422_ops;
>                      break;
>                  default:
>                      return VLC_EGENERIC;
> @@ -363,7 +363,7 @@ static int Open (vlc_object_t *obj)
>              switch (filter->fmt_out.video.i_chroma)
>              {
>                  case VLC_CODEC_I422:
> -                    filter->pf_video_filter = YVYU_I422_Filter;
> +                    filter->ops = &YVYU_I422_ops;
>                      break;
>                  default:
>                      return VLC_EGENERIC;
> @@ -374,7 +374,7 @@ static int Open (vlc_object_t *obj)
>              switch (filter->fmt_out.video.i_chroma)
>              {
>                  case VLC_CODEC_I422:
> -                    filter->pf_video_filter = VYUY_I422_Filter;
> +                    filter->ops = &VYUY_I422_ops;
>                      break;
>                  default:
>                      return VLC_EGENERIC;
> diff --git a/modules/arm_neon/yuv_rgb.c b/modules/arm_neon/yuv_rgb.c
> index 84b5328d768..a654a72f50d 100644
> --- a/modules/arm_neon/yuv_rgb.c
> +++ b/modules/arm_neon/yuv_rgb.c
> @@ -150,7 +150,7 @@ static int Open (vlc_object_t *obj)
>              switch (filter->fmt_in.video.i_chroma)
>              {
>                  case VLC_CODEC_I420:
> -                    filter->pf_video_filter = I420_RV16_Filter;
> +                    filter->ops = &I420_RV16_ops;
>                      break;
>                  default:
>                      return VLC_EGENERIC;
> @@ -166,16 +166,16 @@ static int Open (vlc_object_t *obj)
>              switch (filter->fmt_in.video.i_chroma)
>              {
>                  case VLC_CODEC_I420:
> -                    filter->pf_video_filter = I420_RGBA_Filter;
> +                    filter->ops = &I420_RGBA_ops;
>                      break;
>                  case VLC_CODEC_YV12:
> -                    filter->pf_video_filter = YV12_RGBA_Filter;
> +                    filter->ops = &YV12_RGBA_ops;
>                      break;
>                  case VLC_CODEC_NV21:
> -                    filter->pf_video_filter = NV21_RGBA_Filter;
> +                    filter->ops = &NV21_RGBA_ops;
>                      break;
>                  case VLC_CODEC_NV12:
> -                    filter->pf_video_filter = NV12_RGBA_Filter;
> +                    filter->ops = &NV12_RGBA_ops;
>                      break;
>                  default:
>                      return VLC_EGENERIC;
> diff --git a/modules/audio_filter/audiobargraph_a.c 
> b/modules/audio_filter/audiobargraph_a.c
> index e744b52f243..90a212c2901 100644
> --- a/modules/audio_filter/audiobargraph_a.c
> +++ b/modules/audio_filter/audiobargraph_a.c
> @@ -137,7 +137,12 @@ static int Open( vlc_object_t *p_this )
>      p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
>      aout_FormatPrepare(&p_filter->fmt_in.audio);
>      p_filter->fmt_out.audio = p_filter->fmt_in.audio;
> -    p_filter->pf_audio_filter = DoWork;
> +
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_audio = DoWork,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      vlc_object_t *vlc = VLC_OBJECT(vlc_object_instance(p_filter));
>  
> diff --git a/modules/audio_filter/center.c b/modules/audio_filter/center.c
> index fe9301cfdbf..14a41e156c3 100644
> --- a/modules/audio_filter/center.c
> +++ b/modules/audio_filter/center.c
> @@ -75,7 +75,12 @@ static int Open (vlc_object_t *in)
>      filter->fmt_out.audio.i_rate = filter->fmt_in.audio.i_rate;
>      aout_FormatPrepare(&filter->fmt_in.audio);
>      aout_FormatPrepare(&filter->fmt_out.audio);
> -    filter->pf_audio_filter = Process;
> +
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_audio = Process,
> +    };
> +    filter->ops = &filter_ops;
>      return VLC_SUCCESS;
>  }
>  
> diff --git a/modules/audio_filter/channel_mixer/dolby.c 
> b/modules/audio_filter/channel_mixer/dolby.c
> index 8d5c63c2567..de05e3eb7ff 100644
> --- a/modules/audio_filter/channel_mixer/dolby.c
> +++ b/modules/audio_filter/channel_mixer/dolby.c
> @@ -138,7 +138,11 @@ static int Create( vlc_object_t *p_this )
>          ++i;
>      }
>  
> -    p_filter->pf_audio_filter = DoWork;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_audio = DoWork,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/audio_filter/channel_mixer/headphone.c 
> b/modules/audio_filter/channel_mixer/headphone.c
> index 0c91a158926..77aa32411ba 100644
> --- a/modules/audio_filter/channel_mixer/headphone.c
> +++ b/modules/audio_filter/channel_mixer/headphone.c
> @@ -478,7 +478,12 @@ static int OpenFilter( vlc_object_t *p_this )
>      {
>          p_filter->fmt_in.audio.i_physical_channels = AOUT_CHANS_5_0;
>      }
> -    p_filter->pf_audio_filter = Convert;
> +
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_audio = Convert,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      aout_FormatPrepare(&p_filter->fmt_in.audio);
>      aout_FormatPrepare(&p_filter->fmt_out.audio);
> diff --git a/modules/audio_filter/channel_mixer/mono.c 
> b/modules/audio_filter/channel_mixer/mono.c
> index 0c383e761ec..74f8ccb736f 100644
> --- a/modules/audio_filter/channel_mixer/mono.c
> +++ b/modules/audio_filter/channel_mixer/mono.c
> @@ -384,7 +384,12 @@ static int OpenFilter( vlc_object_t *p_this )
>          p_filter->fmt_out.audio.i_channels = 2;
>      }
>      p_filter->fmt_out.audio.i_rate = p_filter->fmt_in.audio.i_rate;
> -    p_filter->pf_audio_filter = Convert;
> +
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_audio = Convert,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      msg_Dbg( p_this, "%4.4s->%4.4s, channels %d->%d, bits per sample: 
> %i->%i",
>               (char *)&p_filter->fmt_in.i_codec,
> diff --git a/modules/audio_filter/channel_mixer/remap.c 
> b/modules/audio_filter/channel_mixer/remap.c
> index 8c49a5f1ae3..e6299f8cb8f 100644
> --- a/modules/audio_filter/channel_mixer/remap.c
> +++ b/modules/audio_filter/channel_mixer/remap.c
> @@ -366,7 +366,11 @@ static int OpenFilter( vlc_object_t *p_this )
>               aout_FormatPrintChannels( audio_in ),
>               aout_FormatPrintChannels( audio_out ) );
>  
> -    p_filter->pf_audio_filter = Remap;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_audio = Remap,
> +    };
> +    p_filter->ops = &filter_ops;
>      return VLC_SUCCESS;
>  }
>  
> diff --git a/modules/audio_filter/channel_mixer/simple.c 
> b/modules/audio_filter/channel_mixer/simple.c
> index 567269a6bff..06f17e3fbfc 100644
> --- a/modules/audio_filter/channel_mixer/simple.c
> +++ b/modules/audio_filter/channel_mixer/simple.c
> @@ -345,8 +345,11 @@ static int OpenFilter( vlc_object_t *p_this )
>      if( do_work == NULL )
>          return VLC_EGENERIC;
>  
> -    p_filter->pf_audio_filter = Filter;
> -    p_filter->p_sys = (void *)do_work;
> +    static const struct vlc_filter_operations filter_ops =
> +        { .filter_audio = Filter };
> +
> +    p_filter->ops = &filter_ops;
> +    p_filter->p_sys = do_work;
>      return VLC_SUCCESS;
>  }
>  
> @@ -392,4 +395,3 @@ static block_t *Filter( filter_t *p_filter, block_t 
> *p_block )
>  
>      return p_out;
>  }
> -
> diff --git a/modules/audio_filter/channel_mixer/spatialaudio.cpp 
> b/modules/audio_filter/channel_mixer/spatialaudio.cpp
> index acb9ed0b75b..803c03bbaa2 100644
> --- a/modules/audio_filter/channel_mixer/spatialaudio.cpp
> +++ b/modules/audio_filter/channel_mixer/spatialaudio.cpp
> @@ -318,6 +318,16 @@ static int allocateBuffers(filter_spatialaudio 
> *p_sys)
>      return VLC_SUCCESS;
>  }
>  
> +static const struct FilterOperationInitializer {
> +    struct vlc_filter_operations ops {};
> +    FilterOperationInitializer()
> +    {
> +        ops.filter_audio = Mix;
> +        ops.flush = Flush;
> +        ops.change_viewpoint = ChangeViewpoint;
> +    };
> +} filter_ops;
> +
>  static int OpenBinauralizer(vlc_object_t *p_this)
>  {
>      filter_t *p_filter = (filter_t *)p_this;
> @@ -391,9 +401,7 @@ static int OpenBinauralizer(vlc_object_t *p_this)
>      aout_FormatPrepare(outfmt);
>  
>      p_filter->p_sys = p_sys;
> -    p_filter->pf_audio_filter = Mix;
> -    p_filter->pf_flush = Flush;
> -    p_filter->pf_change_viewpoint = ChangeViewpoint;
> +    p_filter->ops = &filter_ops.ops;
>  
>      return VLC_SUCCESS;
>  }
> @@ -541,9 +549,7 @@ static int Open(vlc_object_t *p_this)
>      }
>  
>      p_filter->p_sys = p_sys;
> -    p_filter->pf_audio_filter = Mix;
> -    p_filter->pf_flush = Flush;
> -    p_filter->pf_change_viewpoint = ChangeViewpoint;
> +    p_filter->ops = &filter_ops.ops;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/audio_filter/channel_mixer/trivial.c 
> b/modules/audio_filter/channel_mixer/trivial.c
> index 6bb58e34da6..faa142e6488 100644
> --- a/modules/audio_filter/channel_mixer/trivial.c
> +++ b/modules/audio_filter/channel_mixer/trivial.c
> @@ -174,6 +174,18 @@ static int Create( vlc_object_t *p_this )
>      const audio_format_t *infmt = &p_filter->fmt_in.audio;
>      const audio_format_t *outfmt = &p_filter->fmt_out.audio;
>  
> +    static const struct vlc_filter_operations equal_filter_ops =
> +        { .filter_audio = Equals };
> +
> +    static const struct vlc_filter_operations extract_filter_ops =
> +        { .filter_audio = Extract };
> +
> +    static const struct vlc_filter_operations upmix_filter_ops =
> +        { .filter_audio = Upmix };
> +
> +    static const struct vlc_filter_operations downmix_filter_ops =
> +        { .filter_audio = Downmix };
> +
>      if( infmt->i_physical_channels == 0 )
>      {
>          assert( infmt->i_channels > 0 );
> @@ -181,7 +193,7 @@ static int Create( vlc_object_t *p_this )
>              return VLC_EGENERIC;
>          if( aout_FormatNbChannels( outfmt ) == infmt->i_channels )
>          {
> -            p_filter->pf_audio_filter = Equals;
> +            p_filter->ops = &equal_filter_ops;
>              return VLC_SUCCESS;
>          }
>          else
> @@ -189,7 +201,7 @@ static int Create( vlc_object_t *p_this )
>              if( infmt->i_channels > AOUT_CHAN_MAX )
>                  msg_Info(p_filter, "%d channels will be dropped.",
>                           infmt->i_channels - AOUT_CHAN_MAX);
> -            p_filter->pf_audio_filter = Extract;
> +            p_filter->ops = &extract_filter_ops;
>              return VLC_SUCCESS;
>          }
>      }
> @@ -211,7 +223,7 @@ static int Create( vlc_object_t *p_this )
>      if ( aout_FormatNbChannels( outfmt ) == 1
>        && aout_FormatNbChannels( infmt ) == 1 )
>      {
> -        p_filter->pf_audio_filter = Equals;
> +        p_filter->ops = &equal_filter_ops;
>          return VLC_SUCCESS;
>      }
>  
> @@ -293,7 +305,7 @@ static int Create( vlc_object_t *p_this )
>              }
>          if( b_equals )
>          {
> -            p_filter->pf_audio_filter = Equals;
> +            p_filter->ops = &equal_filter_ops;
>              return VLC_SUCCESS;
>          }
>      }
> @@ -305,9 +317,9 @@ static int Create( vlc_object_t *p_this )
>      memcpy( p_sys->channel_map, channel_map, sizeof(channel_map) );
>  
>      if( aout_FormatNbChannels( outfmt ) > aout_FormatNbChannels( infmt ) )
> -        p_filter->pf_audio_filter = Upmix;
> +        p_filter->ops = &upmix_filter_ops;
>      else
> -        p_filter->pf_audio_filter = Downmix;
> +        p_filter->ops = &downmix_filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/audio_filter/chorus_flanger.c 
> b/modules/audio_filter/chorus_flanger.c
> index 8643c502f8e..6732fd412c2 100644
> --- a/modules/audio_filter/chorus_flanger.c
> +++ b/modules/audio_filter/chorus_flanger.c
> @@ -196,7 +196,12 @@ static int Open( vlc_object_t *p_this )
>      p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
>      aout_FormatPrepare(&p_filter->fmt_in.audio);
>      p_filter->fmt_out.audio = p_filter->fmt_in.audio;
> -    p_filter->pf_audio_filter = DoWork;
> +
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_audio = DoWork,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> @@ -344,7 +349,7 @@ static int paramCallback( vlc_object_t *p_this, 
> char const *psz_var,
>          {
>              p_sys->f_delayTime = oldval.f_float;
>              p_sys->i_bufferLength = p_sys->i_channels * ( (int)
> -                            ( ( p_sys->f_delayTime + 
> p_sys->f_sweepDepth ) * 
> +                            ( ( p_sys->f_delayTime + 
> p_sys->f_sweepDepth ) *
>                                p_filter->fmt_in.audio.i_rate/1000 ) + 1 
> );
>          }
>      }
> @@ -357,7 +362,7 @@ static int paramCallback( vlc_object_t *p_this, 
> char const *psz_var,
>          {
>              p_sys->f_sweepDepth = oldval.f_float;
>              p_sys->i_bufferLength = p_sys->i_channels * ( (int)
> -                            ( ( p_sys->f_delayTime + 
> p_sys->f_sweepDepth ) * 
> +                            ( ( p_sys->f_delayTime + 
> p_sys->f_sweepDepth ) *
>                                p_filter->fmt_in.audio.i_rate/1000 ) + 1 
> );
>          }
>      }
> diff --git a/modules/audio_filter/compressor.c 
> b/modules/audio_filter/compressor.c
> index 5aab1ceaaca..5f25865d55d 100644
> --- a/modules/audio_filter/compressor.c
> +++ b/modules/audio_filter/compressor.c
> @@ -256,7 +256,12 @@ static int Open( vlc_object_t *p_this )
>      p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
>      aout_FormatPrepare(&p_filter->fmt_in.audio);
>      p_filter->fmt_out.audio = p_filter->fmt_in.audio;
> -    p_filter->pf_audio_filter = DoWork;
> +
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_audio = DoWork,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      /* At this stage, we are ready! */
>      msg_Dbg( p_filter, "compressor successfully initialized" );
> diff --git a/modules/audio_filter/converter/format.c 
> b/modules/audio_filter/converter/format.c
> index b7801998d98..e9098f24fe0 100644
> --- a/modules/audio_filter/converter/format.c
> +++ b/modules/audio_filter/converter/format.c
> @@ -71,10 +71,20 @@ static int Open(vlc_object_t *object)
>      if (src->i_codec == dst->i_codec)
>          return VLC_EGENERIC;
>  
> -    filter->pf_audio_filter = FindConversion(src->i_codec, dst->i_codec);
> -    if (filter->pf_audio_filter == NULL)
> +    cvt_t conv_func = FindConversion(src->i_codec, dst->i_codec);
> +    if (conv_func == NULL)
>          return VLC_EGENERIC;
>  
> +    struct vlc_filter_operations *filter_ops = vlc_obj_malloc(object, 
> sizeof *filter_ops);
> +    if (filter_ops == NULL)
> +        return VLC_ENOMEM;
> +
> +    (*filter_ops) = (const struct vlc_filter_operations) {
> +        .filter_audio = conv_func,
> +    };
> +
> +    filter->ops = filter_ops;
> +
>      msg_Dbg(filter, "%4.4s->%4.4s, bits per sample: %i->%i",
>              (char *)&src->i_codec, (char *)&dst->i_codec,
>              src->audio.i_bitspersample, dst->audio.i_bitspersample);
> diff --git a/modules/audio_filter/converter/tospdif.c 
> b/modules/audio_filter/converter/tospdif.c
> index c9d7e08adcf..e513e7aa288 100644
> --- a/modules/audio_filter/converter/tospdif.c
> +++ b/modules/audio_filter/converter/tospdif.c
> @@ -616,8 +616,12 @@ static int Open( vlc_object_t *p_this )
>      if( unlikely( p_sys == NULL ) )
>          return VLC_ENOMEM;
>  
> -    p_filter->pf_audio_filter = DoWork;
> -    p_filter->pf_flush = Flush;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_audio = DoWork,
> +        .flush = Flush,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/audio_filter/equalizer.c b/modules/audio_filter/equalizer.c
> index 92ccaf85b2d..b99beb7ccd3 100644
> --- a/modules/audio_filter/equalizer.c
> +++ b/modules/audio_filter/equalizer.c
> @@ -164,7 +164,11 @@ static int Open( vlc_object_t *p_this )
>      p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
>      aout_FormatPrepare(&p_filter->fmt_in.audio);
>      p_filter->fmt_out.audio = p_filter->fmt_in.audio;
> -    p_filter->pf_audio_filter = DoWork;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_audio = DoWork,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> @@ -571,4 +575,3 @@ static int TwoPassCallback( vlc_object_t *p_this, 
> char const *psz_cmd,
>      vlc_mutex_unlock( &p_sys->lock );
>      return VLC_SUCCESS;
>  }
> -
> diff --git a/modules/audio_filter/gain.c b/modules/audio_filter/gain.c
> index 655d9443049..049677396f0 100644
> --- a/modules/audio_filter/gain.c
> +++ b/modules/audio_filter/gain.c
> @@ -98,7 +98,11 @@ static int Open( vlc_object_t *p_this )
>      msg_Dbg( p_filter, "gain multiplier sets to %.2fx", p_sys->f_gain 
> );
>  
>      p_filter->fmt_out.audio = p_filter->fmt_in.audio;
> -    p_filter->pf_audio_filter = Process;
> +
> +    static const struct vlc_filter_operations filter_ops =
> +        { .filter_audio = Process };
> +    p_filter->ops = &filter_ops;
> +
>      return VLC_SUCCESS;
>  }
>  
> diff --git a/modules/audio_filter/karaoke.c b/modules/audio_filter/karaoke.c
> index 62967e5cec0..2ec3d6e37c2 100644
> --- a/modules/audio_filter/karaoke.c
> +++ b/modules/audio_filter/karaoke.c
> @@ -54,7 +54,12 @@ static int Open (vlc_object_t *obj)
>      filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
>      aout_FormatPrepare(&filter->fmt_in.audio);
>      filter->fmt_out.audio = filter->fmt_in.audio;
> -    filter->pf_audio_filter = Process;
> +
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_audio = Process,
> +    };
> +    filter->ops = &filter_ops;
>      return VLC_SUCCESS;
>  }
>  
> diff --git a/modules/audio_filter/libebur128.c 
> b/modules/audio_filter/libebur128.c
> index 2a1623741f0..368016eb7b6 100644
> --- a/modules/audio_filter/libebur128.c
> +++ b/modules/audio_filter/libebur128.c
> @@ -230,6 +230,10 @@ Flush(filter_t *filter)
>      }
>  }
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .filter_audio = Process, .flush = Flush,
> +};
> +
>  static int Open(vlc_object_t *this)
>  {
>      filter_t *filter = (filter_t *) this;
> @@ -279,8 +283,7 @@ static int Open(vlc_object_t *this)
>  
>      filter->p_sys = sys;
>      filter->fmt_out.audio = filter->fmt_in.audio;
> -    filter->pf_audio_filter = Process;
> -    filter->pf_flush = Flush;
> +    filter->ops = &filter_ops;
>      return VLC_SUCCESS;
>  }
>  
> diff --git a/modules/audio_filter/normvol.c b/modules/audio_filter/normvol.c
> index a9aa2b9cfb3..0fc01194d11 100644
> --- a/modules/audio_filter/normvol.c
> +++ b/modules/audio_filter/normvol.c
> @@ -119,7 +119,11 @@ static int Open( vlc_object_t *p_this )
>      p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
>      aout_FormatPrepare(&p_filter->fmt_in.audio);
>      p_filter->fmt_out.audio = p_filter->fmt_in.audio;
> -    p_filter->pf_audio_filter = DoWork;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_audio = DoWork,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/audio_filter/param_eq.c b/modules/audio_filter/param_eq.c
> index f498ffe08f0..57efac83d56 100644
> --- a/modules/audio_filter/param_eq.c
> +++ b/modules/audio_filter/param_eq.c
> @@ -114,17 +114,22 @@ static int Open( vlc_object_t *p_this )
>  
>      p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
>      p_filter->fmt_out.audio = p_filter->fmt_in.audio;
> -    p_filter->pf_audio_filter = DoWork;
> +
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_audio = DoWork,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      p_sys->f_lowf = var_InheritFloat( p_this, "param-eq-lowf");
>      p_sys->f_lowgain = var_InheritFloat( p_this, "param-eq-lowgain");
>      p_sys->f_highf = var_InheritFloat( p_this, "param-eq-highf");
>      p_sys->f_highgain = var_InheritFloat( p_this, "param-eq-highgain");
> - 
> +
>      p_sys->f_f1 = var_InheritFloat( p_this, "param-eq-f1");
>      p_sys->f_Q1 = var_InheritFloat( p_this, "param-eq-q1");
>      p_sys->f_gain1 = var_InheritFloat( p_this, "param-eq-gain1");
> - 
> +
>      p_sys->f_f2 = var_InheritFloat( p_this, "param-eq-f2");
>      p_sys->f_Q2 = var_InheritFloat( p_this, "param-eq-q2");
>      p_sys->f_gain2 = var_InheritFloat( p_this, "param-eq-gain2");
> @@ -132,7 +137,7 @@ static int Open( vlc_object_t *p_this )
>      p_sys->f_f3 = var_InheritFloat( p_this, "param-eq-f3");
>      p_sys->f_Q3 = var_InheritFloat( p_this, "param-eq-q3");
>      p_sys->f_gain3 = var_InheritFloat( p_this, "param-eq-gain3");
> - 
> +
>  
>      i_samplerate = p_filter->fmt_in.audio.i_rate;
>      CalcPeakEQCoeffs(p_sys->f_f1, p_sys->f_Q1, p_sys->f_gain1,
> @@ -200,18 +205,18 @@ static void CalcPeakEQCoeffs( float f0, float Q, 
> float gainDB, float Fs,
>      if (f0 > Fs/2*0.95f) f0 = Fs/2*0.95f;
>      if (gainDB < -40) gainDB = -40;
>      if (gainDB > 40) gainDB = 40;
> - 
> +
>      A = powf(10, gainDB/40);
>      w0 = 2*((float)M_PI)*f0/Fs;
>      alpha = sinf(w0)/(2*Q);
> - 
> +
>      b0 = 1 + alpha*A;
>      b1 = -2*cosf(w0);
>      b2 = 1 - alpha*A;
>      a0 = 1 + alpha/A;
>      a1 = -2*cosf(w0);
>      a2 = 1 - alpha/A;
> - 
> +
>      // Store values to coeffs and normalize by 1/a0
>      coeffs[0] = b0/a0;
>      coeffs[1] = b1/a0;
> @@ -320,4 +325,3 @@ void ProcessEQ( const float *src, float *dest, 
> float *state,
>          }
>      }
>  }
> -
> diff --git a/modules/audio_filter/resampler/bandlimited.c 
> b/modules/audio_filter/resampler/bandlimited.c
> index 2d544315af3..1256f71afd6 100644
> --- a/modules/audio_filter/resampler/bandlimited.c
> +++ b/modules/audio_filter/resampler/bandlimited.c
> @@ -279,6 +279,10 @@ static block_t *Resample( filter_t * p_filter, 
> block_t * p_in_buf )
>      return p_out_buf;
>  }
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .filter_audio = Resample,
> +};
> +
>  /*****************************************************************************
>   * OpenFilter:
>   *****************************************************************************/
> @@ -306,7 +310,7 @@ static int OpenFilter( vlc_object_t *p_this )
>  
>      p_sys->i_old_wing = 0;
>      p_sys->b_first = true;
> -    p_filter->pf_audio_filter = Resample;
> +    p_filter->ops = &filter_ops;
>  
>      msg_Dbg( p_this, "%4.4s/%iKHz/%i->%4.4s/%iKHz/%i",
>               (char *)&p_filter->fmt_in.i_codec,
> diff --git a/modules/audio_filter/resampler/soxr.c 
> b/modules/audio_filter/resampler/soxr.c
> index 1490d30c63e..b073e3e3a34 100644
> --- a/modules/audio_filter/resampler/soxr.c
> +++ b/modules/audio_filter/resampler/soxr.c
> @@ -188,10 +188,14 @@ Open( vlc_object_t *p_obj, bool b_change_ratio )
>               (const char *)&p_filter->fmt_out.audio.i_format,
>               p_filter->fmt_out.audio.i_rate );
>  
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_audio = Resample,
> +        .drain_audio = Drain,
> +        .flush = Flush,
> +    };
> +    p_filter->ops = &filter_ops;
>      p_filter->p_sys = p_sys;
> -    p_filter->pf_audio_filter = Resample;
> -    p_filter->pf_flush = Flush;
> -    p_filter->pf_audio_drain = Drain;
>      return VLC_SUCCESS;
>  }
>  
> diff --git a/modules/audio_filter/resampler/speex.c 
> b/modules/audio_filter/resampler/speex.c
> index eeefc4f4c43..6c339489c2a 100644
> --- a/modules/audio_filter/resampler/speex.c
> +++ b/modules/audio_filter/resampler/speex.c
> @@ -92,8 +92,12 @@ static int OpenResampler (vlc_object_t *obj)
>          return VLC_ENOMEM;
>      }
>  
> +    static const struct vlc_filter_operations filter_ops =
> +        { .filter_audio = Resample };
> +
>      filter->p_sys = st;
> -    filter->pf_audio_filter = Resample;
> +    filter->ops = &filter_ops;
> +
>      return VLC_SUCCESS;
>  }
>  
> diff --git a/modules/audio_filter/resampler/src.c 
> b/modules/audio_filter/resampler/src.c
> index 1254314b137..5e069e76bc6 100644
> --- a/modules/audio_filter/resampler/src.c
> +++ b/modules/audio_filter/resampler/src.c
> @@ -103,8 +103,13 @@ static int OpenResampler (vlc_object_t *obj)
>          return VLC_EGENERIC;
>      }
>  
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_audio = Resample,
> +    };
> +    filter->ops = &filter_ops;
>      filter->p_sys = s;
> -    filter->pf_audio_filter = Resample;
> +
>      return VLC_SUCCESS;
>  }
>  
> diff --git a/modules/audio_filter/resampler/ugly.c 
> b/modules/audio_filter/resampler/ugly.c
> index 63bba803c77..da13a598dd0 100644
> --- a/modules/audio_filter/resampler/ugly.c
> +++ b/modules/audio_filter/resampler/ugly.c
> @@ -77,7 +77,11 @@ static int CreateResampler( vlc_object_t *p_this )
>       || !AOUT_FMT_LINEAR( &p_filter->fmt_in.audio ) )
>          return VLC_EGENERIC;
>  
> -    p_filter->pf_audio_filter = DoWork;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_audio = DoWork,
> +    };
> +    p_filter->ops = &filter_ops;
>      return VLC_SUCCESS;
>  }
>  
> diff --git a/modules/audio_filter/scaletempo.c 
> b/modules/audio_filter/scaletempo.c
> index c67e01a50fc..18f8a30d106 100644
> --- a/modules/audio_filter/scaletempo.c
> +++ b/modules/audio_filter/scaletempo.c
> @@ -444,7 +444,11 @@ static int Open( vlc_object_t *p_this )
>      p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
>      aout_FormatPrepare(&p_filter->fmt_in.audio);
>      p_filter->fmt_out.audio = p_filter->fmt_in.audio;
> -    p_filter->pf_audio_filter = DoWork;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_audio = DoWork,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> @@ -510,7 +514,11 @@ static int OpenPitch( vlc_object_t *p_this )
>      if( !p_sys->resampler )
>          return VLC_EGENERIC;
>  
> -    p_filter->pf_audio_filter = DoPitchWork;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_audio = DoPitchWork,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> @@ -609,7 +617,7 @@ static block_t *DoPitchWork( filter_t * p_filter, 
> block_t * p_in_buf )
>      p_filter->fmt_in.audio.i_rate = rate_shift;
>  
>      /* Change rate, thus changing pitch */
> -    p_in_buf = p->resampler->pf_audio_filter( p->resampler, p_in_buf );
> +    p_in_buf = p->resampler->ops->filter_audio( p->resampler, p_in_buf );
>  
>      /* Change tempo while preserving shifted pitch */
>      return DoWork( p_filter, p_in_buf );
> diff --git a/modules/audio_filter/spatializer/spatializer.cpp 
> b/modules/audio_filter/spatializer/spatializer.cpp
> index 4f9c19b1330..a1f78d30af9 100644
> --- a/modules/audio_filter/spatializer/spatializer.cpp
> +++ b/modules/audio_filter/spatializer/spatializer.cpp
> @@ -171,7 +171,16 @@ static int Open( vlc_object_t *p_this )
>      p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
>      aout_FormatPrepare(&p_filter->fmt_in.audio);
>      p_filter->fmt_out.audio = p_filter->fmt_in.audio;
> -    p_filter->pf_audio_filter = DoWork;
> +
> +    static const struct FilterOperationInitializer {
> +        struct vlc_filter_operations ops {};
> +        FilterOperationInitializer()
> +        {
> +            ops.filter_audio = DoWork;
> +        };
> +    } filter_ops;
> +
> +    p_filter->ops = &filter_ops.ops;
>      return VLC_SUCCESS;
>  }
>  
> @@ -286,4 +295,3 @@ static int DampCallback( vlc_object_t *p_this, char 
> const *,
>      msg_Dbg( p_this, "'damp' value is now %3.1f", newval.f_float );
>      return VLC_SUCCESS;
>  }
> -
> diff --git a/modules/audio_filter/stereo_widen.c 
> b/modules/audio_filter/stereo_widen.c
> index 1ec29bfffd0..ba19de6d464 100644
> --- a/modules/audio_filter/stereo_widen.c
> +++ b/modules/audio_filter/stereo_widen.c
> @@ -153,7 +153,11 @@ static int Open( vlc_object_t *obj )
>          return VLC_ENOMEM;
>      }
>  
> -    p_filter->pf_audio_filter = Filter;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_audio = Filter,
> +    };
> +    p_filter->ops = &filter_ops;
>      return VLC_SUCCESS;
>  }
>  
> diff --git a/modules/hw/d3d11/d3d11_deinterlace.c 
> b/modules/hw/d3d11/d3d11_deinterlace.c
> index b2c8d99447a..3eb8e9bf081 100644
> --- a/modules/hw/d3d11/d3d11_deinterlace.c
> +++ b/modules/hw/d3d11/d3d11_deinterlace.c
> @@ -232,6 +232,10 @@ picture_t *AllocPicture( filter_t *p_filter )
>      return pic;
>  }
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .filter_video = Deinterlace, .flush = Flush,
> +};
> +
>  int D3D11OpenDeinterlace(vlc_object_t *obj)
>  {
>      filter_t *filter = (filter_t *)obj;
> @@ -350,8 +354,7 @@ int D3D11OpenDeinterlace(vlc_object_t *obj)
>  
>      filter->fmt_out.video   = out_fmt;
>      filter->vctx_out        = vlc_video_context_Hold(filter->vctx_in);
> -    filter->pf_video_filter = Deinterlace;
> -    filter->pf_flush        = Flush;
> +    filter->ops             = &filter_ops;
>      filter->p_sys = sys;
>  
>      return VLC_SUCCESS;
> diff --git a/modules/hw/d3d11/d3d11_filters.c 
> b/modules/hw/d3d11/d3d11_filters.c
> index 868ce7ac31f..36e3351543e 100644
> --- a/modules/hw/d3d11/d3d11_filters.c
> +++ b/modules/hw/d3d11/d3d11_filters.c
> @@ -335,6 +335,10 @@ static int AdjustCallback( vlc_object_t *p_this, 
> char const *psz_var,
>      return VLC_SUCCESS;
>  }
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .filter_video = Filter,
> +};
> +
>  static int D3D11OpenAdjust(vlc_object_t *obj)
>  {
>      filter_t *filter = (filter_t *)obj;
> @@ -507,7 +511,7 @@ static int D3D11OpenAdjust(vlc_object_t *obj)
>          }
>      }
>  
> -    filter->pf_video_filter = Filter;
> +    filter->ops = &filter_ops;
>      filter->p_sys = sys;
>      filter->vctx_out = vlc_video_context_Hold(filter->vctx_in);
>      d3d11_device_unlock(sys->d3d_dev);
> diff --git a/modules/hw/d3d11/d3d11_surface.c 
> b/modules/hw/d3d11/d3d11_surface.c
> index d6bc9ba0267..f3fe4ddbd44 100644
> --- a/modules/hw/d3d11/d3d11_surface.c
> +++ b/modules/hw/d3d11/d3d11_surface.c
> @@ -566,7 +566,7 @@ static void NV12_D3D11(filter_t *p_filter, 
> picture_t *src, picture_t *dst)
>          sys->staging_pic->context = NULL; // some CPU filters won't 
> like the mix of CPU/GPU
>  
>          picture_Hold( src );
> -        sys->filter->pf_video_filter(sys->filter, src);
> +        sys->filter->ops->filter_video(sys->filter, src);
>  
>          sys->staging_pic->context = staging_pic_ctx;
>          ID3D11DeviceContext_Unmap(sys->d3d_dev->d3dcontext, 
> p_staging_sys->resource[KNOWN_DXGI_INDEX], 0);
> @@ -660,6 +660,10 @@ static picture_t *NV12_D3D11_Filter( filter_t 
> *p_filter, picture_t *p_pic )
>      return p_outpic;
>  }
>  
> +static const struct vlc_filter_operations NV12_D3D11_ops = {
> +    .filter_video = NV12_D3D11_Filter,
> +};
> +
>  int D3D11OpenConverter( vlc_object_t *obj )
>  {
>      filter_t *p_filter = (filter_t *)obj;
> @@ -679,34 +683,34 @@ int D3D11OpenConverter( vlc_object_t *obj )
>      case VLC_CODEC_YV12:
>          if( p_filter->fmt_in.video.i_chroma != VLC_CODEC_D3D11_OPAQUE )
>              return VLC_EGENERIC;
> -        p_filter->pf_video_filter = D3D11_YUY2_Filter;
> +        p_filter->ops = &D3D11_YUY2_ops;
>          break;
>      case VLC_CODEC_I420_10L:
>          if( p_filter->fmt_in.video.i_chroma != 
> VLC_CODEC_D3D11_OPAQUE_10B )
>              return VLC_EGENERIC;
> -        p_filter->pf_video_filter = D3D11_YUY2_Filter;
> +        p_filter->ops = &D3D11_YUY2_ops;
>          pixel_bytes = 2;
>          break;
>      case VLC_CODEC_NV12:
>          if( p_filter->fmt_in.video.i_chroma != VLC_CODEC_D3D11_OPAQUE )
>              return VLC_EGENERIC;
> -        p_filter->pf_video_filter = D3D11_NV12_Filter;
> +        p_filter->ops = &D3D11_NV12_ops;
>          break;
>      case VLC_CODEC_P010:
>          if( p_filter->fmt_in.video.i_chroma != 
> VLC_CODEC_D3D11_OPAQUE_10B )
>              return VLC_EGENERIC;
> -        p_filter->pf_video_filter = D3D11_NV12_Filter;
> +        p_filter->ops = &D3D11_NV12_ops;
>          pixel_bytes = 2;
>          break;
>      case VLC_CODEC_RGBA:
>          if( p_filter->fmt_in.video.i_chroma != 
> VLC_CODEC_D3D11_OPAQUE_RGBA )
>              return VLC_EGENERIC;
> -        p_filter->pf_video_filter = D3D11_RGBA_Filter;
> +        p_filter->ops = &D3D11_RGBA_ops;
>          break;
>      case VLC_CODEC_BGRA:
>          if( p_filter->fmt_in.video.i_chroma != 
> VLC_CODEC_D3D11_OPAQUE_BGRA )
>              return VLC_EGENERIC;
> -        p_filter->pf_video_filter = D3D11_RGBA_Filter;
> +        p_filter->ops = &D3D11_RGBA_ops;
>          break;
>      default:
>          return VLC_EGENERIC;
> @@ -753,7 +757,7 @@ int D3D11OpenCPUConverter( vlc_object_t *obj )
>      case VLC_CODEC_YV12:
>      case VLC_CODEC_NV12:
>      case VLC_CODEC_P010:
> -        p_filter->pf_video_filter = NV12_D3D11_Filter;
> +        p_filter->ops = &NV12_D3D11_ops;
>          break;
>      default:
>          return VLC_EGENERIC;
> diff --git a/modules/hw/d3d9/d3d9_filters.c 
> b/modules/hw/d3d9/d3d9_filters.c
> index 80106bce5b2..07eaa133a99 100644
> --- a/modules/hw/d3d9/d3d9_filters.c
> +++ b/modules/hw/d3d9/d3d9_filters.c
> @@ -259,6 +259,10 @@ static int AdjustCallback( vlc_object_t *p_this, 
> char const *psz_var,
>      return VLC_SUCCESS;
>  }
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .filter_video = Filter,
> +};
> +
>  static int D3D9OpenAdjust(vlc_object_t *obj)
>  {
>      filter_t *filter = (filter_t *)obj;
> @@ -450,7 +454,7 @@ static int D3D9OpenAdjust(vlc_object_t *obj)
>  
>      sys->hdecoder_dll = hdecoder_dll;
>  
> -    filter->pf_video_filter = Filter;
> +    filter->ops = &filter_ops;
>      filter->p_sys = sys;
>      filter->vctx_out = vlc_video_context_Hold(filter->vctx_in);
>  
> diff --git a/modules/hw/d3d9/dxa9.c b/modules/hw/d3d9/dxa9.c
> index 563818dc708..afb484895d1 100644
> --- a/modules/hw/d3d9/dxa9.c
> +++ b/modules/hw/d3d9/dxa9.c
> @@ -258,7 +258,7 @@ static void YV12_D3D9(filter_t *p_filter, picture_t 
> *src, picture_t *dst)
>  
>          picture_Hold( src );
>  
> -        sys->filter->pf_video_filter(sys->filter, src);
> +        sys->filter->ops->filter_video(sys->filter, src);
>  
>          sys->staging->context = staging_pic_ctx;
>  
> @@ -396,23 +396,23 @@ int D3D9OpenConverter( vlc_object_t *obj )
>      case VLC_CODEC_YV12:
>          if( p_filter->fmt_in.video.i_chroma != VLC_CODEC_D3D9_OPAQUE )
>              return VLC_EGENERIC;
> -        p_filter->pf_video_filter = DXA9_YV12_Filter;
> +        p_filter->ops = &DXA9_YV12_ops;
>          break;
>      case VLC_CODEC_I420_10L:
>          if( p_filter->fmt_in.video.i_chroma != VLC_CODEC_D3D9_OPAQUE_10B )
>              return VLC_EGENERIC;
> -        p_filter->pf_video_filter = DXA9_YV12_Filter;
> +        p_filter->ops = &DXA9_YV12_ops;
>          pixel_bytes = 2;
>          break;
>      case VLC_CODEC_NV12:
>          if( p_filter->fmt_in.video.i_chroma != VLC_CODEC_D3D9_OPAQUE )
>              return VLC_EGENERIC;
> -        p_filter->pf_video_filter = DXA9_NV12_Filter;
> +        p_filter->ops = &DXA9_NV12_ops;
>          break;
>      case VLC_CODEC_P010:
>          if( p_filter->fmt_in.video.i_chroma != VLC_CODEC_D3D9_OPAQUE_10B )
>              return VLC_EGENERIC;
> -        p_filter->pf_video_filter = DXA9_NV12_Filter;
> +        p_filter->ops = &DXA9_NV12_ops;
>          pixel_bytes = 2;
>          break;
>      default:
> @@ -433,6 +433,10 @@ int D3D9OpenConverter( vlc_object_t *obj )
>      return VLC_SUCCESS;
>  }
>  
> +static const struct vlc_filter_operations YV12_D3D9_ops = {
> +    .filter_video = YV12_D3D9_Filter,
> +};
> +
>  int D3D9OpenCPUConverter( vlc_object_t *obj )
>  {
>      filter_t *p_filter = (filter_t *)obj;
> @@ -452,7 +456,7 @@ int D3D9OpenCPUConverter( vlc_object_t *obj )
>      case VLC_CODEC_YV12:
>      case VLC_CODEC_I420_10L:
>      case VLC_CODEC_P010:
> -        p_filter->pf_video_filter = YV12_D3D9_Filter;
> +        p_filter->ops = &YV12_D3D9_ops;
>          break;
>      default:
>          return VLC_EGENERIC;
> diff --git a/modules/hw/d3d9/dxva2_deinterlace.c 
> b/modules/hw/d3d9/dxva2_deinterlace.c
> index 7c0c4ec6920..0d5f5825500 100644
> --- a/modules/hw/d3d9/dxva2_deinterlace.c
> +++ b/modules/hw/d3d9/dxva2_deinterlace.c
> @@ -321,6 +321,10 @@ picture_t *AllocPicture( filter_t *p_filter )
>      return pic;
>  }
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .filter_video = Deinterlace, .flush = Flush,
> +};
> +
>  int D3D9OpenDeinterlace(vlc_object_t *obj)
>  {
>      filter_t *filter = (filter_t *)obj;
> @@ -501,8 +505,7 @@ int D3D9OpenDeinterlace(vlc_object_t *obj)
>  
>      filter->fmt_out.video   = out_fmt;
>      filter->vctx_out        = vlc_video_context_Hold(filter->vctx_in);
> -    filter->pf_video_filter = Deinterlace;
> -    filter->pf_flush        = Flush;
> +    filter->ops             = &filter_ops;
>      filter->p_sys = sys;
>  
>      return VLC_SUCCESS;
> diff --git a/modules/hw/mmal/converter.c b/modules/hw/mmal/converter.c
> index f62f21e0876..eecf36eac00 100644
> --- a/modules/hw/mmal/converter.c
> +++ b/modules/hw/mmal/converter.c
> @@ -787,6 +787,10 @@ static MMAL_FOURCC_T filter_enc_in(const 
> video_format_t * const fmt)
>  }
>  
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .filter_video = conv_filter, .flush = conv_flush,
> +};
> +
>  int OpenConverter(vlc_object_t * obj)
>  {
>      filter_t * const p_filter = (filter_t *)obj;
> @@ -975,8 +979,7 @@ retry:
>          }
>      }
>  
> -    p_filter->pf_video_filter = conv_filter;
> -    p_filter->pf_flush = conv_flush;
> +    p_filter->ops = &filter_ops;
>      // video_drain NIF in filter structure
>  
>      return VLC_SUCCESS;
> diff --git a/modules/hw/mmal/deinterlace.c b/modules/hw/mmal/deinterlace.c
> index 2709a1f1730..97c11a20c81 100644
> --- a/modules/hw/mmal/deinterlace.c
> +++ b/modules/hw/mmal/deinterlace.c
> @@ -417,6 +417,14 @@ static bool is_fmt_valid_in(const vlc_fourcc_t fmt)
>      return fmt == VLC_CODEC_MMAL_OPAQUE;
>  }
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .filter_video = deinterlace, .flush = di_flush,
> +};
> +
> +static const struct vlc_filter_operations filter_pass_ops = {
> +    .filter_video = pass_deinterlace, .flush = pass_flush,
> +};
> +
>  static int OpenMmalDeinterlace(vlc_object_t *p_this)
>  {
>      filter_t *filter = (filter_t*)p_this;
> @@ -499,8 +507,7 @@ static int OpenMmalDeinterlace(vlc_object_t *p_this)
>  
>      if (sys->use_passthrough)
>      {
> -        filter->pf_video_filter = pass_deinterlace;
> -        filter->pf_flush = pass_flush;
> +        filter->ops = &filter_pass_ops;
>          return VLC_SUCCESS;
>      }
>  
> @@ -600,8 +607,7 @@ static int OpenMmalDeinterlace(vlc_object_t *p_this)
>          goto fail;
>      }
>  
> -    filter->pf_video_filter = deinterlace;
> -    filter->pf_flush = di_flush;
> +    filter->ops = &filter_ops;
>      return 0;
>  
>  fail:
> diff --git a/modules/hw/nvdec/chroma.c b/modules/hw/nvdec/chroma.c
> index 88353f5cbed..2e09fb18cef 100644
> --- a/modules/hw/nvdec/chroma.c
> +++ b/modules/hw/nvdec/chroma.c
> @@ -117,6 +117,10 @@ done:
>      return dst;
>  }
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .filter_video = FilterCUDAToCPU,
> +};
> +
>  static int OpenCUDAToCPU( vlc_object_t *p_this )
>  {
>      filter_t *p_filter = (filter_t *)p_this;
> @@ -137,7 +141,7 @@ static int OpenCUDAToCPU( vlc_object_t *p_this )
>             ) )
>          return VLC_EGENERIC;
>  
> -    p_filter->pf_video_filter = FilterCUDAToCPU;
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/hw/vaapi/chroma.c b/modules/hw/vaapi/chroma.c
> index 38b904ed203..7ea95254d40 100644
> --- a/modules/hw/vaapi/chroma.c
> +++ b/modules/hw/vaapi/chroma.c
> @@ -316,6 +316,14 @@ static int CheckFmt(const video_format_t *in, 
> const video_format_t *out,
>      return VLC_EGENERIC;
>  }
>  
> +static const struct vlc_filter_operations filter_upload_ops = {
> +    .filter_video = UploadSurface,
> +};
> +
> +static const struct vlc_filter_operations filter_download_ops = {
> +    .filter_video = DownloadSurface,
> +};
> +
>  int
>  vlc_vaapi_OpenChroma(vlc_object_t *obj)
>  {
> @@ -333,7 +341,7 @@ vlc_vaapi_OpenChroma(vlc_object_t *obj)
>                   &pixel_bytes))
>          return VLC_EGENERIC;
>  
> -    filter->pf_video_filter = is_upload ? UploadSurface : DownloadSurface;
> +    filter->ops = is_upload ? &filter_upload_ops : &filter_download_ops;
>  
>      if (!(filter_sys = calloc(1, sizeof(filter_sys_t))))
>      {
> diff --git a/modules/hw/vaapi/filters.c b/modules/hw/vaapi/filters.c
> index debe2b507b1..ac96b16c81c 100644
> --- a/modules/hw/vaapi/filters.c
> +++ b/modules/hw/vaapi/filters.c
> @@ -567,6 +567,10 @@ OpenAdjust_InitFilterParams(filter_t * filter, 
> void * p_data,
>      return VLC_SUCCESS;
>  }
>  
> +static const struct vlc_filter_operations Adjust_ops = {
> +    .filter_video = Adjust,
> +};
> +
>  static int
>  OpenAdjust(vlc_object_t * obj)
>  {
> @@ -587,7 +591,7 @@ OpenAdjust(vlc_object_t * obj)
>      for (unsigned int i = 0; i < NUM_ADJUST_MODES; ++i)
>          var_AddCallback(obj, adjust_params_names[i], FilterCallback, p_data);
>  
> -    filter->pf_video_filter = Adjust;
> +    filter->ops = &Adjust_ops;
>  
>      return VLC_SUCCESS;
>  
> @@ -684,6 +688,10 @@ OpenBasicFilter_InitFilterParams(filter_t * 
> filter, void * p_data,
>      return VLC_SUCCESS;
>  }
>  
> +static const struct vlc_filter_operations BasicFilter_ops = {
> +    .filter_video = BasicFilter,
> +};
> +
>  static int
>  OpenBasicFilter(vlc_object_t * obj, VAProcFilterType filter_type,
>                  const char *psz_sigma_name, struct range const 
> *p_vlc_range)
> @@ -708,7 +716,7 @@ OpenBasicFilter(vlc_object_t * obj, 
> VAProcFilterType filter_type,
>  
>      var_AddCallback(obj, p_data->sigma.psz_name, FilterCallback, p_data);
>  
> -    filter->pf_video_filter = BasicFilter;
> +    filter->ops = &BasicFilter_ops;
>  
>      return VLC_SUCCESS;
>  
> @@ -1065,6 +1073,13 @@ OpenDeinterlace_InitHistory(void * p_data, 
> VAProcPipelineCaps const * pipeline_c
>      return VLC_SUCCESS;
>  }
>  
> +static const struct vlc_filter_operations DeinterlaceX2_ops = {
> +    .filter_video = DeinterlaceX2, .flush = Deinterlace_Flush,
> +};
> +static const struct vlc_filter_operations Deinterlace_ops = {
> +    .filter_video = Deinterlace,   .flush = Deinterlace_Flush,
> +};
> +
>  static int
>  OpenDeinterlace(vlc_object_t * obj)
>  {
> @@ -1079,10 +1094,9 @@ OpenDeinterlace(vlc_object_t * obj)
>          goto error;
>  
>      if (p_data->b_double_rate)
> -        filter->pf_video_filter = DeinterlaceX2;
> +        filter->ops = &DeinterlaceX2_ops;
>      else
> -        filter->pf_video_filter = Deinterlace;
> -    filter->pf_flush = Deinterlace_Flush;
> +        filter->ops = &Deinterlace_ops;
>  
>      for (unsigned int i = 0; i < METADATA_SIZE; ++i)
>      {
> diff --git a/modules/hw/vdpau/adjust.c b/modules/hw/vdpau/adjust.c
> index 27c8b4bfa7d..3a4d5a5387a 100644
> --- a/modules/hw/vdpau/adjust.c
> +++ b/modules/hw/vdpau/adjust.c
> @@ -139,6 +139,10 @@ static const char *const options[] = {
>      "brightness", "contrast", "saturation", "hue", NULL
>  };
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .filter_video = Adjust,
> +};
> +
>  static int Open(vlc_object_t *obj)
>  {
>      filter_t *filter = (filter_t *)obj;
> @@ -157,7 +161,7 @@ static int Open(vlc_object_t *obj)
>      if (unlikely(sys == NULL))
>          return VLC_ENOMEM;
>  
> -    filter->pf_video_filter = Adjust;
> +    filter->ops = &filter_ops;
>      filter->p_sys = sys;
>  
>      config_ChainParse(filter, "", options, filter->p_cfg);
> diff --git a/modules/hw/vdpau/chroma.c b/modules/hw/vdpau/chroma.c
> index fa4b1e8b62c..06c1a633d89 100644
> --- a/modules/hw/vdpau/chroma.c
> +++ b/modules/hw/vdpau/chroma.c
> @@ -743,6 +743,14 @@ const struct vlc_video_context_operations 
> vdpau_vctx_ops = {
>      NULL,
>  };
>  
> +static const struct vlc_filter_operations filter_output_opaque_ops = {
> +    .filter_video = VideoRender, .flush = Flush,
> +};
> +
> +static const struct vlc_filter_operations filter_output_ycbcr_ops = {
> +    .filter_video = YCbCrRender, .flush = Flush,
> +};
> +
>  static int OutputOpen(vlc_object_t *obj)
>  {
>      filter_t *filter = (filter_t *)obj;
> @@ -766,7 +774,7 @@ static int OutputOpen(vlc_object_t *obj)
>  
>      filter->p_sys = sys;
>  
> -    picture_t *(*video_filter)(filter_t *, picture_t *) = VideoRender;
> +    const struct vlc_filter_operations *ops = &filter_output_opaque_ops;
>  
>      if (filter->fmt_in.video.i_chroma == VLC_CODEC_VDPAU_VIDEO_444)
>      {
> @@ -789,7 +797,7 @@ static int OutputOpen(vlc_object_t *obj)
>      else
>      if (vlc_fourcc_to_vdp_ycc(filter->fmt_in.video.i_chroma,
>                                &sys->chroma, &sys->format))
> -        video_filter = YCbCrRender;
> +        ops = &filter_output_ycbcr_ops;
>      else
>      {
>          vlc_decoder_device_Release(dec_device);
> @@ -817,7 +825,7 @@ static int OutputOpen(vlc_object_t *obj)
>      }
>  
>      /* Create the video-to-output mixer */
> -    sys->mixer = MixerCreate(filter, video_filter == YCbCrRender);
> +    sys->mixer = MixerCreate(filter, ops == &filter_output_ycbcr_ops);
>      if (sys->mixer == VDP_INVALID_HANDLE)
>      {
>          picture_pool_Release(sys->pool);
> @@ -834,8 +842,7 @@ static int OutputOpen(vlc_object_t *obj)
>      sys->procamp.saturation = 1.f;
>      sys->procamp.hue = 0.f;
>  
> -    filter->pf_video_filter = video_filter;
> -    filter->pf_flush = Flush;
> +    filter->ops = ops;
>      return VLC_SUCCESS;
>  }
>  
> @@ -888,6 +895,10 @@ static bool ChromaMatches(VdpChromaType vdp_type, 
> vlc_fourcc_t vlc_chroma)
>      }
>  }
>  
> +static const struct vlc_filter_operations filter_ycbcr_ops = {
> +    .filter_video = VideoExport_Filter,
> +};
> +
>  static int YCbCrOpen(vlc_object_t *obj)
>  {
>      filter_t *filter = (filter_t *)obj;
> @@ -913,7 +924,7 @@ static int YCbCrOpen(vlc_object_t *obj)
>          return VLC_ENOMEM;
>      sys->format = format;
>  
> -    filter->pf_video_filter = VideoExport_Filter;
> +    filter->ops = &filter_ycbcr_ops;
>      filter->p_sys = sys;
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/hw/vdpau/deinterlace.c 
> b/modules/hw/vdpau/deinterlace.c
> index bd66a61d1b0..2a5a996f384 100644
> --- a/modules/hw/vdpau/deinterlace.c
> +++ b/modules/hw/vdpau/deinterlace.c
> @@ -96,6 +96,10 @@ static picture_t *Deinterlace(filter_t *filter, 
> picture_t *src)
>      return src;
>  }
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .filter_video = Deinterlace,
> +};
> +
>  static int Open(vlc_object_t *obj)
>  {
>      filter_t *filter = (filter_t *)obj;
> @@ -119,7 +123,7 @@ static int Open(vlc_object_t *obj)
>  
>      sys->last_pts = VLC_TICK_INVALID;
>  
> -    filter->pf_video_filter = Deinterlace;
> +    filter->ops = &filter_ops;
>      filter->p_sys = sys;
>      filter->fmt_out.video.i_frame_rate *= 2;
>      filter->vctx_out = vlc_video_context_Hold(filter->vctx_in);
> diff --git a/modules/hw/vdpau/sharpen.c b/modules/hw/vdpau/sharpen.c
> index 4aee7e3c406..718eedddb1f 100644
> --- a/modules/hw/vdpau/sharpen.c
> +++ b/modules/hw/vdpau/sharpen.c
> @@ -79,6 +79,10 @@ static picture_t *Sharpen(filter_t *filter, picture_t *pic)
>  
>  static const char *const options[] = { "sigma", NULL };
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .filter_video = Sharpen,
> +};
> +
>  static int Open(vlc_object_t *obj)
>  {
>      filter_t *filter = (filter_t *)obj;
> @@ -125,7 +129,7 @@ static int Open(vlc_object_t *obj)
>      if (unlikely(sys == NULL))
>          return VLC_ENOMEM;
>  
> -    filter->pf_video_filter = Sharpen;
> +    filter->ops = &filter_ops;
>      filter->p_sys = sys;
>  
>      config_ChainParse(filter, "sharpen-", options, filter->p_cfg);
> diff --git a/modules/spu/audiobargraph_v.c b/modules/spu/audiobargraph_v.c
> index de3f9627a6f..9fdc78c7e4d 100644
> --- a/modules/spu/audiobargraph_v.c
> +++ b/modules/spu/audiobargraph_v.c
> @@ -487,6 +487,14 @@ out:
>      return p_dst;
>  }
>  
> +static const struct vlc_filter_operations filter_sub_ops = {
> +    .source_sub = FilterSub,
> +};
> +
> +static const struct vlc_filter_operations filter_video_ops = {
> +    .filter_video = FilterVideo,
> +};
> +
>  /**
>   * Common open function
>   */
> @@ -558,9 +566,9 @@ static int OpenCommon(vlc_object_t *p_this, bool b_sub)
>                           BarGraphCallback, p_sys);
>  
>      if (b_sub)
> -        p_filter->pf_sub_source = FilterSub;
> +        p_filter->ops = &filter_sub_ops;
>      else
> -        p_filter->pf_video_filter = FilterVideo;
> +        p_filter->ops = &filter_video_ops;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/spu/dynamicoverlay/dynamicoverlay.c 
> b/modules/spu/dynamicoverlay/dynamicoverlay.c
> index b038a2d66ac..37cdf7936da 100644
> --- a/modules/spu/dynamicoverlay/dynamicoverlay.c
> +++ b/modules/spu/dynamicoverlay/dynamicoverlay.c
> @@ -81,6 +81,10 @@ static const char *const ppsz_filter_options[] = {
>      "input", "output", NULL
>  };
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .source_sub = Filter,
> +};
> +
>  /*****************************************************************************
>   * Create: allocates adjust video thread output method
>   *****************************************************************************
> @@ -110,7 +114,7 @@ static int Create( vlc_object_t *p_this )
>      p_sys->b_atomic = false;
>      vlc_mutex_init( &p_sys->lock );
>  
> -    p_filter->pf_sub_source = Filter;
> +    p_filter->ops = &filter_ops;
>  
>      config_ChainParse( p_filter, "overlay-", ppsz_filter_options,
>                         p_filter->p_cfg );
> diff --git a/modules/spu/logo.c b/modules/spu/logo.c
> index c8d1b02445b..9a713cb2407 100644
> --- a/modules/spu/logo.c
> +++ b/modules/spu/logo.c
> @@ -215,6 +215,15 @@ static int OpenVideo( vlc_object_t *p_this )
>      return OpenCommon( p_this, false );
>  }
>  
> +static const struct vlc_filter_operations filter_sub_ops = {
> +    .source_sub = FilterSub,
> +};
> +
> +static const struct vlc_filter_operations filter_video_ops = {
> +    .filter_video = FilterVideo,
> +    .video_mouse = Mouse,
> +};
> +
>  /**
>   * Common open function
>   */
> @@ -293,14 +302,9 @@ static int OpenCommon( vlc_object_t *p_this, bool b_sub )
>  
>      /* Misc init */
>      if( b_sub )
> -    {
> -        p_filter->pf_sub_source = FilterSub;
> -    }
> +        p_filter->ops = &filter_sub_ops;
>      else
> -    {
> -        p_filter->pf_video_filter = FilterVideo;
> -        p_filter->pf_video_mouse = Mouse;
> -    }
> +        p_filter->ops = &filter_video_ops;
>  
>      free( psz_filename );
>      return VLC_SUCCESS;
> diff --git a/modules/spu/marq.c b/modules/spu/marq.c
> index c370c5322dc..31657aa0478 100644
> --- a/modules/spu/marq.c
> +++ b/modules/spu/marq.c
> @@ -178,6 +178,10 @@ static const char *const ppsz_filter_options[] = {
>      NULL
>  };
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .source_sub = Filter,
> +};
> +
>  /*****************************************************************************
>   * CreateFilter: allocates marquee video filter
>   *****************************************************************************/
> @@ -228,7 +232,7 @@ static int CreateFilter( vlc_object_t *p_this )
>      CREATE_VAR( p_style->i_font_size, Integer, "marq-size" );
>  
>      /* Misc init */
> -    p_filter->pf_sub_source = Filter;
> +    p_filter->ops = &filter_ops;
>      p_sys->last_time = 0;
>  
>      return VLC_SUCCESS;
> diff --git a/modules/spu/mosaic.c b/modules/spu/mosaic.c
> index deecc74e73c..c8544950014 100644
> --- a/modules/spu/mosaic.c
> +++ b/modules/spu/mosaic.c
> @@ -271,6 +271,10 @@ static void mosaic_ParseSetOffsets( vlc_object_t *p_this,
>  #define mosaic_ParseSetOffsets( a, b, c ) \
>              mosaic_ParseSetOffsets( VLC_OBJECT( a ), b, c )
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .source_sub = Filter,
> +};
> +
>  /*****************************************************************************
>   * CreateFiler: allocate mosaic video filter
>   *****************************************************************************/
> @@ -288,7 +292,7 @@ static int CreateFilter( vlc_object_t *p_this )
>      if( p_sys == NULL )
>          return VLC_ENOMEM;
>  
> -    p_filter->pf_sub_source = Filter;
> +    p_filter->ops = &filter_ops;
>  
>      vlc_mutex_init( &p_sys->lock );
>      vlc_mutex_lock( &p_sys->lock );
> diff --git a/modules/spu/rss.c b/modules/spu/rss.c
> index 95f55e34a4b..08c5ea59577 100644
> --- a/modules/spu/rss.c
> +++ b/modules/spu/rss.c
> @@ -240,6 +240,10 @@ static void InitCurrentContext(filter_sys_t *p_sys)
>      p_sys->i_cur_char = -1;
>  }
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .source_sub = Filter,
> +};
> +
>  /*****************************************************************************
>   * CreateFilter: allocates RSS video filter
>   *****************************************************************************/
> @@ -311,7 +315,7 @@ static int CreateFilter( vlc_object_t *p_this )
>  
>      /* Misc init */
>      vlc_mutex_init( &p_sys->lock );
> -    p_filter->pf_sub_source = Filter;
> +    p_filter->ops = &filter_ops;
>      p_sys->last_date = (vlc_tick_t)0;
>      p_sys->b_fetched = false;
>  
> diff --git a/modules/spu/subsdelay.c b/modules/spu/subsdelay.c
> index 70b2e9a332f..b17a4325655 100644
> --- a/modules/spu/subsdelay.c
> +++ b/modules/spu/subsdelay.c
> @@ -293,6 +293,10 @@ vlc_module_begin()
>  
>  static const char * const ppsz_filter_options[] = { "mode", "factor", 
> "overlap", NULL };
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .filter_sub = SubsdelayFilter,
> +};
> +
>  
> /*****************************************************************************
>   * SubsdelayCreate: Create subsdelay filter
>   
> *****************************************************************************/
> @@ -336,7 +340,7 @@ static int SubsdelayCreate( vlc_object_t *p_this )
>      var_AddCallback( p_filter, CFG_MIN_START_STOP_INTERVAL, 
> SubsdelayCallback, p_sys );
>  
>      p_filter->p_sys = p_sys;
> -    p_filter->pf_sub_filter = SubsdelayFilter;
> +    p_filter->ops = &filter_ops;
>  
>      config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options, 
> p_filter->p_cfg );
>  
> diff --git a/modules/text_renderer/freetype/freetype.c 
> b/modules/text_renderer/freetype/freetype.c
> index 5b5fc2d08f2..fe78056212d 100644
> --- a/modules/text_renderer/freetype/freetype.c
> +++ b/modules/text_renderer/freetype/freetype.c
> @@ -1159,6 +1159,11 @@ static int Render( filter_t *p_filter, 
> subpicture_region_t *p_region_out,
>      return rv;
>  }
>  
> +static const struct vlc_filter_operations filter_ops =
> +{
> +    .render = Render,
> +};
> +
>  /*****************************************************************************
>   * Create: allocates osd-text video thread output method
>   *****************************************************************************
> @@ -1236,7 +1241,7 @@ static int Create( vlc_object_t *p_this )
>      if( LoadFontsFromAttachments( p_filter ) == VLC_ENOMEM )
>          goto error;
>  
> -    p_filter->pf_render = Render;
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  
> diff --git a/modules/text_renderer/nsspeechsynthesizer.m 
> b/modules/text_renderer/nsspeechsynthesizer.m
> index 1b71c779ec6..5e6774f09af 100644
> --- a/modules/text_renderer/nsspeechsynthesizer.m
> +++ b/modules/text_renderer/nsspeechsynthesizer.m
> @@ -58,6 +58,10 @@ typedef struct filter_sys_t
>      NSString *lastString;
>  } filter_sys_t;
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .render = RenderText,
> +};
> +
>  static int  Create (vlc_object_t *p_this)
>  {
>      filter_t *p_filter = (filter_t *)p_this;
> @@ -70,7 +74,7 @@ static int  Create (vlc_object_t *p_this)
>      p_sys->currentLocale = p_sys->lastString = @"";
>      p_sys->speechSynthesizer = [[NSSpeechSynthesizer alloc] init];
>  
> -    p_filter->pf_render = RenderText;
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/text_renderer/sapi.cpp b/modules/text_renderer/sapi.cpp
> index 874f5c83143..ad5109ef05c 100644
> --- a/modules/text_renderer/sapi.cpp
> +++ b/modules/text_renderer/sapi.cpp
> @@ -93,6 +93,14 @@ static void LeaveMTA(void)
>      CoUninitialize();
>  }
>  
> +static const struct FilterOperationInitializer {
> +    struct vlc_filter_operations ops {};
> +    FilterOperationInitializer()
> +    {
> +        ops.render = RenderText;
> +    };
> +} filter_ops;
> +
>  static int Create (vlc_object_t *p_this)
>  {
>      filter_t *p_filter = (filter_t *)p_this;
> @@ -157,7 +165,7 @@ static int Create (vlc_object_t *p_this)
>  
>      LeaveMTA();
>  
> -    p_filter->pf_render = RenderText;
> +    p_filter->ops = &filter_ops.ops;
>  
>      return VLC_SUCCESS;
>  
> diff --git a/modules/text_renderer/svg.c b/modules/text_renderer/svg.c
> index f97c5fa3bf6..5e710d15eec 100644
> --- a/modules/text_renderer/svg.c
> +++ b/modules/text_renderer/svg.c
> @@ -169,6 +169,10 @@ static char *svg_GetDocument( filter_t *p_filter, 
> int i_width, int i_height, con
>      return psz_result;
>  }
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .render = RenderText,
> +};
> +
>  /*****************************************************************************
>   * Create: allocates svg video thread output method
>   *****************************************************************************
> @@ -184,7 +188,7 @@ static int Create( vlc_object_t *p_this )
>          return VLC_ENOMEM;
>      p_filter->p_sys = p_sys;
>  
> -    p_filter->pf_render = RenderText;
> +    p_filter->ops = &filter_ops;
>      svg_LoadTemplate( p_filter );
>  
>  #if (GLIB_MAJOR_VERSION < 2 || GLIB_MINOR_VERSION < 36)
> diff --git a/modules/text_renderer/tdummy.c 
> b/modules/text_renderer/tdummy.c
> index 3451c1068c9..4fd996674c1 100644
> --- a/modules/text_renderer/tdummy.c
> +++ b/modules/text_renderer/tdummy.c
> @@ -47,9 +47,13 @@ static int RenderText( filter_t *p_filter, 
> subpicture_region_t *p_region_out,
>      return VLC_EGENERIC;
>  }
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .render = RenderText,
> +};
> +
>  static int OpenRenderer( vlc_object_t *p_this )
>  {
>      filter_t *p_filter = (filter_t *)p_this;
> -    p_filter->pf_render = RenderText;
> +    p_filter->ops = &filter_ops;
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/video_chroma/chain.c b/modules/video_chroma/chain.c
> index 7ec2798cc3c..c25711c7d32 100644
> --- a/modules/video_chroma/chain.c
> +++ b/modules/video_chroma/chain.c
> @@ -114,6 +114,7 @@ typedef struct
>  {
>      filter_chain_t *p_chain;
>      filter_t *p_video_filter;
> +    struct vlc_filter_operations custom_ops;
>  } filter_sys_t;
>  
>  /* Restart filter callback */
> @@ -152,6 +153,10 @@ static const struct filter_video_callbacks 
> filter_video_chain_cbs =
>      BufferChainNew, HoldChainDecoderDevice,
>  };
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .filter_video = Chain,
> +};
> +
>  
> /*****************************************************************************
>   * Activate: allocate a chroma function
>   
> *****************************************************************************
> @@ -213,7 +218,7 @@ static int Activate( filter_t *p_filter, int 
> (*pf_build)(filter_t *) )
>      }
>      assert(p_filter->vctx_out == filter_chain_GetVideoCtxOut( 
> p_sys->p_chain ));
>      /* */
> -    p_filter->pf_video_filter = Chain;
> +    p_filter->ops = &filter_ops;
>      return VLC_SUCCESS;
>  }
>  
> @@ -409,8 +414,12 @@ static int BuildFilterChain( filter_t *p_filter )
>                  filter_AddProxyCallbacks( p_filter,
>                                            p_sys->p_video_filter,
>                                            RestartFilterCallback );
> -                if (p_sys->p_video_filter->pf_video_mouse != NULL)
> -                    p_filter->pf_video_mouse = ChainMouse;
> +                if (p_sys->p_video_filter->ops->video_mouse != NULL)
> +                {
> +                    p_sys->custom_ops = *p_sys->p_video_filter->ops;
> +                    p_sys->p_video_filter->ops = &p_sys->custom_ops;
> +                    p_sys->custom_ops.video_mouse = ChainMouse;
> +                }
>                  es_format_Clean( &fmt_mid );
>                  i_ret = VLC_SUCCESS;
>                  p_filter->vctx_out = filter_chain_GetVideoCtxOut( 
> p_sys->p_chain );
> diff --git a/modules/video_chroma/cvpx.c b/modules/video_chroma/cvpx.c
> index f3ece816328..062b1ef0f9a 100644
> --- a/modules/video_chroma/cvpx.c
> +++ b/modules/video_chroma/cvpx.c
> @@ -248,6 +248,13 @@ static void Close(vlc_object_t *obj)
>      free(p_sys);
>  }
>  
> +static const struct vlc_filter_operations CVPX_TO_SW_ops = {
> +    .filter_video = CVPX_TO_SW_Filter,
> +};
> +static const struct vlc_filter_operations SW_TO_CVPX_ops = {
> +    .filter_video = SW_TO_CVPX_Filter,
> +};
> +
>  static int Open(vlc_object_t *obj)
>  {
>      filter_t *p_filter = (filter_t *)obj;
> @@ -261,9 +268,9 @@ static int Open(vlc_object_t *obj)
>      case VLC_CODEC_CVPX_##x: \
>          sw_fmt = p_filter->fmt_out.video; \
>          if (p_filter->fmt_out.video.i_chroma == VLC_CODEC_##x) \
> -            p_filter->pf_video_filter = CVPX_TO_SW_Filter; \
> +            p_filter->ops = &CVPX_TO_SW_ops; \
>          else if (i420_fcc != 0 && p_filter->fmt_out.video.i_chroma == 
> i420_fcc) { \
> -            p_filter->pf_video_filter = CVPX_TO_SW_Filter; \
> +            p_filter->ops = &CVPX_TO_SW_ops; \
>              sw_fmt.i_chroma = VLC_CODEC_##x; \
>          } else return VLC_EGENERIC; \
>  
> @@ -271,10 +278,10 @@ static int Open(vlc_object_t *obj)
>      case VLC_CODEC_CVPX_##x: \
>          sw_fmt = p_filter->fmt_in.video; \
>          if (p_filter->fmt_in.video.i_chroma == VLC_CODEC_##x) { \
> -            p_filter->pf_video_filter = SW_TO_CVPX_Filter; \
> +            p_filter->ops = &SW_TO_CVPX_ops; \
>          } \
>          else if (i420_fcc != 0 && p_filter->fmt_in.video.i_chroma == 
> i420_fcc) { \
> -            p_filter->pf_video_filter = SW_TO_CVPX_Filter; \
> +            p_filter->ops = &SW_TO_CVPX_ops; \
>              sw_fmt.i_chroma = VLC_CODEC_##x; \
>          } else return VLC_EGENERIC; \
>          b_need_pool = true;
> @@ -432,6 +439,10 @@ static vlc_fourcc_t const supported_chromas[] = { 
> VLC_CODEC_CVPX_BGRA,
>                                                    VLC_CODEC_CVPX_P010,
>                                                    VLC_CODEC_CVPX_UYVY 
> };
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .filter_video = Filter,
> +};
> +
>  static int
>  Open_CVPX_to_CVPX(vlc_object_t *obj)
>  {
> @@ -480,7 +491,7 @@ Open_CVPX_to_CVPX(vlc_object_t *obj)
>      }
>  
>      filter->p_sys = p_sys;
> -    filter->pf_video_filter = Filter;
> +    filter->ops = &filter_ops;
>      filter->vctx_out = vlc_video_context_Hold(filter->vctx_in);
>      filter->fmt_out.i_codec = filter->fmt_out.video.i_chroma;
>      return VLC_SUCCESS;
> @@ -553,6 +564,10 @@ static const vlc_fourcc_t supported_sw_chromas[] = {
>      VLC_CODEC_UYVY, VLC_CODEC_P010,
>  };
>  
> +static const struct vlc_filter_operations chain_CVPX_ops = {
> +    .filter_video = chain_CVPX_Filter, .flush = chain_CVPX_Flush,
> +};
> +
>  static int
>  Open_chain_CVPX(vlc_object_t *obj)
>  {
> @@ -656,8 +671,7 @@ Open_chain_CVPX(vlc_object_t *obj)
>  
>      filter->vctx_out = vctx_out;
>      filter->p_sys = chain;
> -    filter->pf_flush = chain_CVPX_Flush;
> -    filter->pf_video_filter = chain_CVPX_Filter;
> +    filter->ops = &chain_CVPX_ops;
>  
>      /* Display the current conversion chain in the logs. */
>      msg_Dbg(filter, "CVPX conversion chain:");
> diff --git a/modules/video_chroma/grey_yuv.c b/modules/video_chroma/grey_yuv.c
> index ea09775c9cb..308bd5c24b0 100644
> --- a/modules/video_chroma/grey_yuv.c
> +++ b/modules/video_chroma/grey_yuv.c
> @@ -56,6 +56,20 @@ vlc_module_end ()
>  VIDEO_FILTER_WRAPPER( GREY_I420 )
>  VIDEO_FILTER_WRAPPER( GREY_YUY2 )
>  
> +static const struct vlc_filter_operations *
> +GetFilterOperations( filter_t *filter )
> +{
> +    switch( filter->fmt_out.video.i_chroma )
> +    {
> +        case VLC_CODEC_I420:
> +            return &GREY_I420_ops;
> +        case VLC_CODEC_YUYV:
> +            return &GREY_YUY2_ops;
> +        default:
> +            return NULL;
> +    }
> +}
> +
>  /*****************************************************************************
>   * Activate: allocate a chroma function
>   *****************************************************************************
> @@ -78,18 +92,9 @@ static int Activate( vlc_object_t *p_this )
>  
>      if ( p_filter->fmt_in.video.i_chroma != VLC_CODEC_GREY )
>          return VLC_EGENERIC;
> -
> -    switch( p_filter->fmt_out.video.i_chroma )
> -    {
> -        case VLC_CODEC_I420:
> -            p_filter->pf_video_filter = GREY_I420_Filter;
> -            break;
> -        case VLC_CODEC_YUYV:
> -            p_filter->pf_video_filter = GREY_YUY2_Filter;
> -            break;
> -        default:
> -            return VLC_EGENERIC;
> -    }
> +    p_filter->ops = GetFilterOperations(p_filter);
> +    if ( p_filter->ops == NULL )
> +        return VLC_EGENERIC;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/video_chroma/i420_nv12.c b/modules/video_chroma/i420_nv12.c
> index 53e1364adfc..fe27022517d 100644
> --- a/modules/video_chroma/i420_nv12.c
> +++ b/modules/video_chroma/i420_nv12.c
> @@ -168,23 +168,23 @@ static int Create( vlc_object_t *p_this )
>          case VLC_CODEC_J420:
>              if( outfcc != VLC_CODEC_NV12 )
>                  return -1;
> -            p_filter->pf_video_filter = I420_NV12_Filter;
> +            p_filter->ops = &I420_NV12_ops;
>              break;
>  
>          case VLC_CODEC_YV12:
>              if( outfcc != VLC_CODEC_NV12 )
>                  return -1;
> -            p_filter->pf_video_filter = YV12_NV12_Filter;
> +            p_filter->ops = &YV12_NV12_ops;
>              break;
>          case VLC_CODEC_NV12:
>              switch( outfcc )
>              {
>                  case VLC_CODEC_I420:
>                  case VLC_CODEC_J420:
> -                    p_filter->pf_video_filter = NV12_I420_Filter;
> +                    p_filter->ops = &NV12_I420_ops;
>                      break;
>                  case VLC_CODEC_YV12:
> -                    p_filter->pf_video_filter = NV12_YV12_Filter;
> +                    p_filter->ops = &NV12_YV12_ops;
>                      break;
>                  default:
>                      return -1;
> @@ -195,14 +195,14 @@ static int Create( vlc_object_t *p_this )
>              if( outfcc != VLC_CODEC_P010 )
>                  return -1;
>              pixel_bytes = 2;
> -            p_filter->pf_video_filter = I42010B_P010_Filter;
> +            p_filter->ops = &I42010B_P010_ops;
>              break;
>  
>          case VLC_CODEC_P010:
>              if( outfcc != VLC_CODEC_I420_10L )
>                  return -1;
>              pixel_bytes = 2;
> -            p_filter->pf_video_filter = P010_I42010B_Filter;
> +            p_filter->ops = &P010_I42010B_ops;
>              break;
>  
>          default:
> diff --git a/modules/video_chroma/i420_rgb.c b/modules/video_chroma/i420_rgb.c
> index fbb8f90e42b..20bb2eadbe9 100644
> --- a/modules/video_chroma/i420_rgb.c
> +++ b/modules/video_chroma/i420_rgb.c
> @@ -131,7 +131,7 @@ static int Activate( vlc_object_t *p_this )
>                      {
>                          /* R5G5B6 pixel format */
>                          msg_Dbg(p_this, "RGB pixel format is R5G5B5");
> -                        p_filter->pf_video_filter = I420_R5G5B5_Filter;
> +                        p_filter->ops = &I420_R5G5B5_ops;
>                      }
>                      else if( ( p_filter->fmt_out.video.i_rmask == 0xf800
>                              && p_filter->fmt_out.video.i_gmask == 0x07e0
> @@ -139,7 +139,7 @@ static int Activate( vlc_object_t *p_this )
>                      {
>                          /* R5G6B5 pixel format */
>                          msg_Dbg(p_this, "RGB pixel format is R5G6B5");
> -                        p_filter->pf_video_filter = I420_R5G6B5_Filter;
> +                        p_filter->ops = &I420_R5G6B5_ops;
>                      }
>                      else
>                          return VLC_EGENERIC;
> @@ -152,7 +152,7 @@ static int Activate( vlc_object_t *p_this )
>                      {
>                          /* A8R8G8B8 pixel format */
>                          msg_Dbg(p_this, "RGB pixel format is A8R8G8B8");
> -                        p_filter->pf_video_filter = I420_A8R8G8B8_Filter;
> +                        p_filter->ops = &I420_A8R8G8B8_ops;
>                      }
>                      else if( p_filter->fmt_out.video.i_rmask == 0xff000000
>                            && p_filter->fmt_out.video.i_gmask == 0x00ff0000
> @@ -160,7 +160,7 @@ static int Activate( vlc_object_t *p_this )
>                      {
>                          /* R8G8B8A8 pixel format */
>                          msg_Dbg(p_this, "RGB pixel format is R8G8B8A8");
> -                        p_filter->pf_video_filter = I420_R8G8B8A8_Filter;
> +                        p_filter->ops = &I420_R8G8B8A8_ops;
>                      }
>                      else if( p_filter->fmt_out.video.i_rmask == 0x0000ff00
>                            && p_filter->fmt_out.video.i_gmask == 0x00ff0000
> @@ -168,7 +168,7 @@ static int Activate( vlc_object_t *p_this )
>                      {
>                          /* B8G8R8A8 pixel format */
>                          msg_Dbg(p_this, "RGB pixel format is B8G8R8A8");
> -                        p_filter->pf_video_filter = I420_B8G8R8A8_Filter;
> +                        p_filter->ops = &I420_B8G8R8A8_ops;
>                      }
>                      else if( p_filter->fmt_out.video.i_rmask == 0x000000ff
>                            && p_filter->fmt_out.video.i_gmask == 0x0000ff00
> @@ -176,21 +176,21 @@ static int Activate( vlc_object_t *p_this )
>                      {
>                          /* A8B8G8R8 pixel format */
>                          msg_Dbg(p_this, "RGB pixel format is A8B8G8R8");
> -                        p_filter->pf_video_filter = I420_A8B8G8R8_Filter;
> +                        p_filter->ops = &I420_A8B8G8R8_ops;
>                      }
>                      else
>                          return VLC_EGENERIC;
>                      break;
>  #else
>                  case VLC_CODEC_RGB8:
> -                    p_filter->pf_video_filter = I420_RGB8_Filter;
> +                    p_filter->ops = &I420_RGB8_ops;
>                      break;
>                  case VLC_CODEC_RGB15:
>                  case VLC_CODEC_RGB16:
> -                    p_filter->pf_video_filter = I420_RGB16_Filter;
> +                    p_filter->ops = &I420_RGB16_ops;
>                      break;
>                  case VLC_CODEC_RGB32:
> -                    p_filter->pf_video_filter = I420_RGB32_Filter;
> +                    p_filter->ops = &I420_RGB32_ops;
>                      break;
>  #endif
>                  default:
> diff --git a/modules/video_chroma/i420_yuy2.c b/modules/video_chroma/i420_yuy2.c
> index b18fea3c31f..fb7a787f957 100644
> --- a/modules/video_chroma/i420_yuy2.c
> +++ b/modules/video_chroma/i420_yuy2.c
> @@ -107,6 +107,34 @@ VIDEO_FILTER_WRAPPER( I420_IUYV )
>  VIDEO_FILTER_WRAPPER( I420_Y211 )
>  #endif
>  
> +static const struct vlc_filter_operations *
> +GetFilterOperations( filter_t *p_filter )
> +{
> +    switch( p_filter->fmt_out.video.i_chroma )
> +    {
> +        case VLC_CODEC_YUYV:
> +            return &I420_YUY2_ops;
> +
> +        case VLC_CODEC_YVYU:
> +            return &I420_YVYU_ops;
> +
> +        case VLC_CODEC_UYVY:
> +            return &I420_UYVY_ops;
> +
> +#if !defined (MODULE_NAME_IS_i420_yuy2_altivec)
> +        case VLC_FOURCC('I','U','Y','V'):
> +            return &I420_IUYV_ops;
> +#endif
> +
> +#if defined (MODULE_NAME_IS_i420_yuy2)
> +        case VLC_CODEC_Y211:
> +            return &I420_Y211_ops;
> +#endif
> +        default:
> +            return NULL;
> +    }
> +}
> +
>  /*****************************************************************************
>   * Activate: allocate a chroma function
>   *****************************************************************************
> @@ -134,33 +162,10 @@ static int Activate( vlc_object_t *p_this )
>      if( p_filter->fmt_in.video.i_chroma != VLC_CODEC_I420)
>          return VLC_EGENERIC;
>  
> -    switch( p_filter->fmt_out.video.i_chroma )
> -    {
> -        case VLC_CODEC_YUYV:
> -            p_filter->pf_video_filter = I420_YUY2_Filter;
> -            break;
> -
> -        case VLC_CODEC_YVYU:
> -            p_filter->pf_video_filter = I420_YVYU_Filter;
> -            break;
> -
> -        case VLC_CODEC_UYVY:
> -            p_filter->pf_video_filter = I420_UYVY_Filter;
> -            break;
> -#if !defined (MODULE_NAME_IS_i420_yuy2_altivec)
> -        case VLC_FOURCC('I','U','Y','V'):
> -            p_filter->pf_video_filter = I420_IUYV_Filter;
> -            break;
> -#endif
> -
> -#if defined (MODULE_NAME_IS_i420_yuy2)
> -        case VLC_CODEC_Y211:
> -            p_filter->pf_video_filter = I420_Y211_Filter;
> -            break;
> -#endif
> -        default:
> -            return VLC_EGENERIC;
> -    }
> +    /* Find the adequate filter function depending on the output format. */
> +    p_filter->ops = GetFilterOperations( p_filter );
> +    if( p_filter->ops == NULL )
> +        return VLC_EGENERIC;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/video_chroma/i422_i420.c b/modules/video_chroma/i422_i420.c
> index 5ac6918f853..72315a8d943 100644
> --- a/modules/video_chroma/i422_i420.c
> +++ b/modules/video_chroma/i422_i420.c
> @@ -87,15 +87,15 @@ static int Activate( vlc_object_t *p_this )
>              {
>                  case VLC_CODEC_I420:
>                  case VLC_CODEC_J420:
> -                    p_filter->pf_video_filter = I422_I420_Filter;
> +                    p_filter->ops = &I422_I420_ops;
>                      break;
>  
>                  case VLC_CODEC_YV12:
> -                    p_filter->pf_video_filter = I422_YV12_Filter;
> +                    p_filter->ops = &I422_YV12_ops;
>                      break;
>  
>                  case VLC_CODEC_YUV420A:
> -                    p_filter->pf_video_filter = I422_YUVA_Filter;
> +                    p_filter->ops = &I422_YUVA_ops;
>                      break;
>  
>                  default:
> diff --git a/modules/video_chroma/i422_yuy2.c b/modules/video_chroma/i422_yuy2.c
> index 416dbfff02c..eb29dc965da 100644
> --- a/modules/video_chroma/i422_yuy2.c
> +++ b/modules/video_chroma/i422_yuy2.c
> @@ -89,6 +89,35 @@ VIDEO_FILTER_WRAPPER( I422_IUYV )
>  VIDEO_FILTER_WRAPPER( I422_Y211 )
>  #endif
>  
> +
> +static const struct vlc_filter_operations*
> +GetFilterOperations(filter_t *filter)
> +{
> +    switch( filter->fmt_out.video.i_chroma )
> +    {
> +        case VLC_CODEC_YUYV:
> +            return &I422_YUY2_ops;
> +
> +        case VLC_CODEC_YVYU:
> +            return &I422_YVYU_ops;
> +
> +        case VLC_CODEC_UYVY:
> +            return &I422_UYVY_ops;
> +
> +        case VLC_FOURCC('I','U','Y','V'):
> +            return &I422_IUYV_ops;
> +
> +#if defined (MODULE_NAME_IS_i422_yuy2)
> +        case VLC_CODEC_Y211:
> +            return &I422_Y211_ops;
> +#endif
> +
> +        default:
> +            return NULL;
> +    }
> +
> +}
> +
>  /*****************************************************************************
>   * Activate: allocate a chroma function
>   *****************************************************************************
> @@ -111,42 +140,16 @@ static int Activate( vlc_object_t *p_this )
>          return VLC_EGENERIC;
>      }
>  
> -    switch( p_filter->fmt_in.video.i_chroma )
> -    {
> -        case VLC_CODEC_I422:
> -            switch( p_filter->fmt_out.video.i_chroma )
> -            {
> -                case VLC_CODEC_YUYV:
> -                    p_filter->pf_video_filter = I422_YUY2_Filter;
> -                    break;
> -
> -                case VLC_CODEC_YVYU:
> -                    p_filter->pf_video_filter = I422_YVYU_Filter;
> -                    break;
> -
> -                case VLC_CODEC_UYVY:
> -                    p_filter->pf_video_filter = I422_UYVY_Filter;
> -                    break;
> +    /* This is a i422 -> * converter. */
> +    if( p_filter->fmt_in.video.i_chroma != VLC_CODEC_I422 )
> +        return VLC_EGENERIC;
>  
> -                case VLC_FOURCC('I','U','Y','V'):
> -                    p_filter->pf_video_filter = I422_IUYV_Filter;
> -                    break;
>  
> -#if defined (MODULE_NAME_IS_i422_yuy2)
> -                case VLC_CODEC_Y211:
> -                    p_filter->pf_video_filter = I422_Y211_Filter;
> -                    break;
> -#endif
> -
> -                default:
> -                    return -1;
> -            }
> -            break;
> +    p_filter->ops = GetFilterOperations( p_filter );
> +    if( p_filter->ops == NULL)
> +        return VLC_EGENERIC;
>  
> -        default:
> -            return -1;
> -    }
> -    return 0;
> +    return VLC_SUCCESS;
>  }
>  
>  /* Following functions are local */
> diff --git a/modules/video_chroma/omxdl.c b/modules/video_chroma/omxdl.c
> index 292fe950eab..e643f0735d5 100644
> --- a/modules/video_chroma/omxdl.c
> +++ b/modules/video_chroma/omxdl.c
> @@ -265,7 +265,7 @@ static int Open (vlc_object_t *obj)
>                  case VLC_CODEC_RGB16:
>                      if (FixRV16 (&filter->fmt_out.video))
>                          return VLC_EGENERIC;
> -                    filter->pf_video_filter = I420_RV16_Filter;
> +                    filter->ops = &I420_RV16_ops;
>                      return VLC_SUCCESS;
>              }
>              break;
> @@ -276,7 +276,7 @@ static int Open (vlc_object_t *obj)
>                  case VLC_CODEC_RGB16:
>                      if (FixRV16 (&filter->fmt_out.video))
>                          return VLC_EGENERIC;
> -                    filter->pf_video_filter = YV12_RV16_Filter;
> +                    filter->ops = &YV12_RV16_ops;
>                      return VLC_SUCCESS;
>              }
>              break;
> @@ -285,10 +285,10 @@ static int Open (vlc_object_t *obj)
>              switch (filter->fmt_out.video.i_chroma)
>              {
>                  case VLC_CODEC_I420:
> -                    filter->pf_video_filter = I422_I420_Filter;
> +                    filter->ops = &I422_I420_ops;
>                      return VLC_SUCCESS;
>                  case VLC_CODEC_YV12:
> -                    filter->pf_video_filter = I422_YV12_Filter;
> +                    filter->ops = &I422_YV12_ops;
>                      return VLC_SUCCESS;
>              }
>              break;
> @@ -299,7 +299,7 @@ static int Open (vlc_object_t *obj)
>                  case VLC_CODEC_RGB16:
>                      if (FixRV16 (&filter->fmt_out.video))
>                          return VLC_EGENERIC;
> -                    filter->pf_video_filter = I444_RV16_Filter;
> +                    filter->ops = &I444_RV16_ops;
>                      return VLC_SUCCESS;
>              }
>              return VLC_EGENERIC;
> @@ -310,12 +310,12 @@ static int Open (vlc_object_t *obj)
>                  case VLC_CODEC_RGB24:
>                      if (FixRV24 (&filter->fmt_out.video))
>                          return VLC_EGENERIC;
> -                    filter->pf_video_filter = YUYV_RV24_Filter;
> +                    filter->ops = &YUYV_RV24_ops;
>                      return VLC_SUCCESS;
>                  case VLC_CODEC_RGB16:
>                      if (FixRV16 (&filter->fmt_out.video))
>                          return VLC_EGENERIC;
> -                    filter->pf_video_filter = YUYV_RV16_Filter;
> +                    filter->ops = &YUYV_RV16_ops;
>                      return VLC_SUCCESS;
>              }
>              return VLC_EGENERIC;
> @@ -324,10 +324,10 @@ static int Open (vlc_object_t *obj)
>              switch (filter->fmt_out.video.i_chroma)
>              {
>                  case VLC_CODEC_I420:
> -                    filter->pf_video_filter = UYVY_I420_Filter;
> +                    filter->ops = &UYVY_I420_ops;
>                      return VLC_SUCCESS;
>                  case VLC_CODEC_YV12:
> -                    filter->pf_video_filter = UYVY_YV12_Filter;
> +                    filter->ops = &UYVY_YV12_ops;
>                      return VLC_SUCCESS;
>              }
>              return VLC_EGENERIC;
> @@ -484,33 +484,33 @@ static int OpenScaler (vlc_object_t *obj)
>              switch (filter->fmt_out.video.i_chroma)
>              {
>                  case VLC_CODEC_I420:
> -                    filter->pf_video_filter = I420_I420_Scale_Filter;
> +                    filter->ops = &I420_I420_Scale_ops;
>                      return VLC_SUCCESS;
>                  case VLC_CODEC_YV12:
> -                    filter->pf_video_filter = I420_YV12_Scale_Filter;
> +                    filter->ops = &I420_YV12_Scale_ops;
>                      return VLC_SUCCESS;
>                  case VLC_CODEC_RGB16:
>                      if (FixRV16 (&filter->fmt_out.video))
>                          return VLC_EGENERIC;
> -                    filter->pf_video_filter = I420_RGB_Scale_Filter;
> +                    filter->ops = &I420_RGB_Scale_ops;
>                      *conv = OMX_IP_BGR565;
>                      return VLC_SUCCESS;
>                  case VLC_CODEC_RGB15:
>                      if (FixRV15 (&filter->fmt_out.video))
>                          break;
> -                    filter->pf_video_filter = I420_RGB_Scale_Filter;
> +                    filter->ops = &I420_RGB_Scale_ops;
>                      *conv = OMX_IP_BGR555;
>                      return VLC_SUCCESS;
>                  case VLC_CODEC_RGB12:
>                      if (FixRV12 (&filter->fmt_out.video))
>                          break;
> -                    filter->pf_video_filter = I420_RGB_Scale_Filter;
> +                    filter->ops = &I420_RGB_Scale_ops;
>                      *conv = OMX_IP_BGR444;
>                      return VLC_SUCCESS;
>                  case VLC_CODEC_RGB24:
>                      if (FixRV24 (&filter->fmt_out.video))
>                          break;
> -                    filter->pf_video_filter = I420_RGB_Scale_Filter;
> +                    filter->ops = &I420_RGB_Scale_ops;
>                      *conv = OMX_IP_BGR888;
>                      return VLC_SUCCESS;
>              }
> @@ -520,33 +520,33 @@ static int OpenScaler (vlc_object_t *obj)
>              switch (filter->fmt_out.video.i_chroma)
>              {
>                  case VLC_CODEC_I420:
> -                    filter->pf_video_filter = YV12_I420_Scale_Filter;
> +                    filter->ops = &YV12_I420_Scale_ops;
>                      return VLC_SUCCESS;
>                  case VLC_CODEC_YV12:
> -                    filter->pf_video_filter = YV12_YV12_Scale_Filter;
> +                    filter->ops = &YV12_YV12_Scale_ops;
>                      return VLC_SUCCESS;
>                  case VLC_CODEC_RGB16:
>                      if (FixRV16 (&filter->fmt_out.video))
>                          break;
> -                    filter->pf_video_filter = YV12_RGB_Scale_Filter;
> +                    filter->ops = &YV12_RGB_Scale_ops;
>                      *conv = OMX_IP_BGR565;
>                      return VLC_SUCCESS;
>                  case VLC_CODEC_RGB15:
>                      if (FixRV15 (&filter->fmt_out.video))
>                          break;
> -                    filter->pf_video_filter = YV12_RGB_Scale_Filter;
> +                    filter->ops = &YV12_RGB_Scale_ops;
>                      *conv = OMX_IP_BGR555;
>                      return VLC_SUCCESS;
>                  case VLC_CODEC_RGB12:
>                      if (FixRV12 (&filter->fmt_out.video))
>                          break;
> -                    filter->pf_video_filter = YV12_RGB_Scale_Filter;
> +                    filter->ops = &YV12_RGB_Scale_ops;
>                      *conv = OMX_IP_BGR444;
>                      return VLC_SUCCESS;
>                  case VLC_CODEC_RGB24:
>                      if (FixRV24 (&filter->fmt_out.video))
>                          break;
> -                    filter->pf_video_filter = YV12_RGB_Scale_Filter;
> +                    filter->ops = &YV12_RGB_Scale_ops;
>                      *conv = OMX_IP_BGR888;
>                      return VLC_SUCCESS;
>              }
> @@ -556,30 +556,30 @@ static int OpenScaler (vlc_object_t *obj)
>              switch (filter->fmt_out.video.i_chroma)
>              {
>                  case VLC_CODEC_I422:
> -                    filter->pf_video_filter = I422_I422_Scale_Filter;
> +                    filter->ops = &I422_I422_Scale_ops;
>                      return VLC_SUCCESS;
>                  case VLC_CODEC_RGB16:
>                      if (FixRV16 (&filter->fmt_out.video))
>                          break;
> -                    filter->pf_video_filter = I422_RGB_Scale_Filter;
> +                    filter->ops = &I422_RGB_Scale_ops;
>                      *conv = OMX_IP_BGR565;
>                      return VLC_SUCCESS;
>                  case VLC_CODEC_RGB15:
>                      if (FixRV15 (&filter->fmt_out.video))
>                          break;
> -                    filter->pf_video_filter = I422_RGB_Scale_Filter;
> +                    filter->ops = &I422_RGB_Scale_ops;
>                      *conv = OMX_IP_BGR555;
>                      return VLC_SUCCESS;
>                  case VLC_CODEC_RGB12:
>                      if (FixRV12 (&filter->fmt_out.video))
>                          break;
> -                    filter->pf_video_filter = I422_RGB_Scale_Filter;
> +                    filter->ops = &I422_RGB_Scale_ops;
>                      *conv = OMX_IP_BGR444;
>                      return VLC_SUCCESS;
>                  case VLC_CODEC_RGB24:
>                      if (FixRV24 (&filter->fmt_out.video))
>                          break;
> -                    filter->pf_video_filter = I422_RGB_Scale_Filter;
> +                    filter->ops = &I422_RGB_Scale_ops;
>                      *conv = OMX_IP_BGR888;
>                      return VLC_SUCCESS;
>              }
> diff --git a/modules/video_chroma/rv32.c b/modules/video_chroma/rv32.c
> index 9546872722b..835b658531d 100644
> --- a/modules/video_chroma/rv32.c
> +++ b/modules/video_chroma/rv32.c
> @@ -47,6 +47,10 @@ vlc_module_begin ()
>      set_callback( OpenFilter )
>  vlc_module_end ()
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .filter_video = Filter,
> +};
> +
>  
> /*****************************************************************************
>   * OpenFilter: probe the filter and return score
>   
> *****************************************************************************/
> @@ -67,7 +71,7 @@ static int OpenFilter( vlc_object_t *p_this )
>       || p_filter->fmt_in.video.orientation != 
> p_filter->fmt_out.video.orientation)
>          return -1;
>  
> -    p_filter->pf_video_filter = Filter;
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/video_chroma/swscale.c b/modules/video_chroma/swscale.c
> index 3c7bdf78ea3..8d5106ffd61 100644
> --- a/modules/video_chroma/swscale.c
> +++ b/modules/video_chroma/swscale.c
> @@ -139,6 +139,10 @@ static int GetSwsCpuMask(void);
>  /* XXX is it always 3 even for BIG_ENDIAN (blend.c seems to think so) ? */
>  #define OFFSET_A (3)
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .filter_video = Filter,
> +};
> +
>  /*****************************************************************************
>   * OpenScaler: probe the filter and return score
>   *****************************************************************************/
> @@ -192,7 +196,7 @@ static int OpenScaler( vlc_object_t *p_this )
>      }
>  
>      /* */
> -    p_filter->pf_video_filter = Filter;
> +    p_filter->ops = &filter_ops;
>  
>      msg_Dbg( p_filter, "%ix%i (%ix%i) chroma: %4.4s -> %ix%i (%ix%i) 
> chroma: %4.4s with scaling using %s",
>               p_filter->fmt_in.video.i_visible_width, 
> p_filter->fmt_in.video.i_visible_height,
> diff --git a/modules/video_chroma/yuvp.c b/modules/video_chroma/yuvp.c
> index b6149ce1654..8fe086637bd 100644
> --- a/modules/video_chroma/yuvp.c
> +++ b/modules/video_chroma/yuvp.c
> @@ -78,7 +78,7 @@ static int Open( vlc_object_t *p_this )
>          return VLC_EGENERIC;
>      }
>  
> -    p_filter->pf_video_filter = Convert_Filter;
> +    p_filter->ops = &Convert_ops;
>  
>      msg_Dbg( p_filter, "YUVP to %4.4s converter",
>               (const char*)&p_filter->fmt_out.video.i_chroma );
> diff --git a/modules/video_chroma/yuy2_i420.c b/modules/video_chroma/yuy2_i420.c
> index cfcb2c3717b..fa0c3cfd570 100644
> --- a/modules/video_chroma/yuy2_i420.c
> +++ b/modules/video_chroma/yuy2_i420.c
> @@ -84,15 +84,15 @@ static int Activate( vlc_object_t *p_this )
>              switch( p_filter->fmt_in.video.i_chroma )
>              {
>                  case VLC_CODEC_YUYV:
> -                    p_filter->pf_video_filter = YUY2_I420_Filter;
> +                    p_filter->ops = &YUY2_I420_ops;
>                      break;
>  
>                  case VLC_CODEC_YVYU:
> -                    p_filter->pf_video_filter = YVYU_I420_Filter;
> +                    p_filter->ops = &YVYU_I420_ops;
>                      break;
>  
>                  case VLC_CODEC_UYVY:
> -                    p_filter->pf_video_filter = UYVY_I420_Filter;
> +                    p_filter->ops = &UYVY_I420_ops;
>                      break;
>  
>                  default:
> diff --git a/modules/video_chroma/yuy2_i422.c b/modules/video_chroma/yuy2_i422.c
> index f8aa0b9b040..b35c62f84dc 100644
> --- a/modules/video_chroma/yuy2_i422.c
> +++ b/modules/video_chroma/yuy2_i422.c
> @@ -84,15 +84,15 @@ static int Activate( vlc_object_t *p_this )
>              switch( p_filter->fmt_in.video.i_chroma )
>              {
>                  case VLC_CODEC_YUYV:
> -                    p_filter->pf_video_filter = YUY2_I422_Filter;
> +                    p_filter->ops = &YUY2_I422_ops;
>                      break;
>  
>                  case VLC_CODEC_YVYU:
> -                    p_filter->pf_video_filter = YVYU_I422_Filter;
> +                    p_filter->ops = &YVYU_I422_ops;
>                      break;
>  
>                  case VLC_CODEC_UYVY:
> -                    p_filter->pf_video_filter = UYVY_I422_Filter;
> +                    p_filter->ops = &UYVY_I422_ops;
>                      break;
>  
>                  default:
> diff --git a/modules/video_filter/adjust.c 
> b/modules/video_filter/adjust.c
> index 063af77f22a..85cf2bbb165 100644
> --- a/modules/video_filter/adjust.c
> +++ b/modules/video_filter/adjust.c
> @@ -141,6 +141,16 @@ static int BoolCallback( vlc_object_t *obj, char 
> const *varname,
>      return VLC_SUCCESS;
>  }
>  
> +static const struct vlc_filter_operations planar_filter_ops =
> +{
> +    .filter_video = FilterPlanar,
> +};
> +
> +static const struct vlc_filter_operations packed_filter_ops =
> +{
> +    .filter_video = FilterPacked,
> +};
> +
>  /*****************************************************************************
>   * Create: allocates adjust video filter
>   *****************************************************************************/
> @@ -166,7 +176,7 @@ static int Create( vlc_object_t *p_this )
>      {
>          CASE_PLANAR_YUV
>              /* Planar YUV */
> -            p_filter->pf_video_filter = FilterPlanar;
> +            p_filter->ops = &planar_filter_ops;
>              p_sys->pf_process_sat_hue_clip = planar_sat_hue_clip_C;
>              p_sys->pf_process_sat_hue = planar_sat_hue_C;
>              break;
> @@ -174,14 +184,14 @@ static int Create( vlc_object_t *p_this )
>          CASE_PLANAR_YUV10
>          CASE_PLANAR_YUV9
>              /* Planar YUV 9-bit or 10-bit */
> -            p_filter->pf_video_filter = FilterPlanar;
> +            p_filter->ops = &planar_filter_ops;
>              p_sys->pf_process_sat_hue_clip = planar_sat_hue_clip_C_16;
>              p_sys->pf_process_sat_hue = planar_sat_hue_C_16;
>              break;
>  
>          CASE_PACKED_YUV_422
>              /* Packed YUV 4:2:2 */
> -            p_filter->pf_video_filter = FilterPacked;
> +            p_filter->ops = &packed_filter_ops;
>              p_sys->pf_process_sat_hue_clip = packed_sat_hue_clip_C;
>              p_sys->pf_process_sat_hue = packed_sat_hue_C;
>              break;
> diff --git a/modules/video_filter/alphamask.c b/modules/video_filter/alphamask.c
> index 001f37185c7..9b894f9f760 100644
> --- a/modules/video_filter/alphamask.c
> +++ b/modules/video_filter/alphamask.c
> @@ -82,6 +82,11 @@ typedef struct
>      vlc_mutex_t mask_lock;
>  } filter_sys_t;
>  
> +static const struct vlc_filter_operations filter_ops =
> +{
> +    .filter_video = Filter,
> +};
> +
>  static int Create( vlc_object_t *p_this )
>  {
>      filter_t *p_filter = (filter_t *)p_this;
> @@ -122,7 +127,7 @@ static int Create( vlc_object_t *p_this )
>      vlc_mutex_init( &p_sys->mask_lock );
>      var_AddCallback( p_filter, CFG_PREFIX "mask", MaskCallback,
>                       p_filter );
> -    p_filter->pf_video_filter = Filter;
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/video_filter/anaglyph.c b/modules/video_filter/anaglyph.c
> index b17bf7a5256..0f1b16689f4 100644
> --- a/modules/video_filter/anaglyph.c
> +++ b/modules/video_filter/anaglyph.c
> @@ -86,6 +86,11 @@ typedef struct
>      int left, right;
>  } filter_sys_t;
>  
> +static const struct vlc_filter_operations filter_ops =
> +{
> +    .filter_video = &Filter,
> +};
> +
>  
>  static int Create(vlc_object_t *p_this)
>  {
> @@ -156,7 +161,7 @@ static int Create(vlc_object_t *p_this)
>              break;
>      }
>  
> -    p_filter->pf_video_filter = Filter;
> +    p_filter->ops = &filter_ops;
>      return VLC_SUCCESS;
>  }
>  
> @@ -301,4 +306,3 @@ static void combine_side_by_side_yuv420(picture_t 
> *p_inpic, picture_t *p_outpic,
>          vout += p_outpic->p[V_PLANE].i_pitch - uv_visible_pitch;
>      }
>  }
> -
> diff --git a/modules/video_filter/antiflicker.c 
> b/modules/video_filter/antiflicker.c
> index 568a84ae615..4f360ee0027 100644
> --- a/modules/video_filter/antiflicker.c
> +++ b/modules/video_filter/antiflicker.c
> @@ -94,6 +94,11 @@ typedef struct
>      uint8_t *p_old_data;
>  } filter_sys_t;
>  
> +static const struct vlc_filter_operations filter_ops =
> +{
> +    .filter_video = Filter,
> +};
> +
>  /*****************************************************************************
>   * Create: allocates Distort video thread output method
>   *****************************************************************************
> @@ -121,7 +126,7 @@ static int Create( vlc_object_t *p_this )
>          return VLC_ENOMEM;
>      p_filter->p_sys = p_sys;
>  
> -    p_filter->pf_video_filter = Filter;
> +    p_filter->ops = &filter_ops;
>  
>      /* Initialize the arguments */
>      atomic_init( &p_sys->i_window_size,
> diff --git a/modules/video_filter/ball.c b/modules/video_filter/ball.c
> index 563ff8bedc0..15086833117 100644
> --- a/modules/video_filter/ball.c
> +++ b/modules/video_filter/ball.c
> @@ -215,6 +215,10 @@ struct filter_sys_t
>      } colorList[4];
>  };
>  
> +static const struct vlc_filter_operations filter_ops =
> +{
> +    .filter_video = Filter,
> +};
>  
>  /*****************************************************************************
>  * Create: allocates Distort video thread output method
> @@ -261,7 +265,7 @@ static int Create( vlc_object_t *p_this )
>      if( p_sys->p_image == NULL )
>          return VLC_EGENERIC;
>  
> -    p_filter->pf_video_filter = Filter;
> +    p_filter->ops = &filter_ops;
>  
>      config_ChainParse( p_filter, FILTER_PREFIX, ppsz_filter_options,
>                         p_filter->p_cfg );
> diff --git a/modules/video_filter/blend.cpp b/modules/video_filter/blend.cpp
> index b33aa084f38..faf49cf482f 100644
> --- a/modules/video_filter/blend.cpp
> +++ b/modules/video_filter/blend.cpp
> @@ -649,7 +649,7 @@ struct filter_sys_t {
>  /**
>   * It blends 2 picture together.
>   */
> -static void Blend(filter_t *filter,
> +static void DoBlend(filter_t *filter,
>                    picture_t *dst, const picture_t *src,
>                    int x_offset, int y_offset, int alpha)
>  {
> @@ -680,6 +680,14 @@ static void Blend(filter_t *filter,
>                 width, height, alpha);
>  }
>  
> +static const struct FilterOperationInitializer {
> +    struct vlc_filter_operations ops {};
> +    FilterOperationInitializer()
> +    {
> +        ops.blend_video = DoBlend;
> +    };
> +} filter_ops;
> +
>  static int Open(vlc_object_t *object)
>  {
>      filter_t *filter = (filter_t *)object;
> @@ -699,7 +707,7 @@ static int Open(vlc_object_t *object)
>          return VLC_EGENERIC;
>      }
>  
> -    filter->pf_video_blend = Blend;
> +    filter->ops = &filter_ops.ops;
>      filter->p_sys          = sys;
>      return VLC_SUCCESS;
>  }
> @@ -710,4 +718,3 @@ static void Close(vlc_object_t *object)
>      filter_sys_t *p_sys = reinterpret_cast<filter_sys_t *>( 
> filter->p_sys );
>      delete p_sys;
>  }
> -
> diff --git a/modules/video_filter/blendbench.c 
> b/modules/video_filter/blendbench.c
> index f2886385311..0a6c17ad22b 100644
> --- a/modules/video_filter/blendbench.c
> +++ b/modules/video_filter/blendbench.c
> @@ -144,6 +144,11 @@ static int blendbench_LoadImage( vlc_object_t 
> *p_this, picture_t **pp_pic,
>      return VLC_SUCCESS;
>  }
>  
> +static const struct vlc_filter_operations filter_ops =
> +{
> +    .filter_video = Filter,
> +};
> +
>  /*****************************************************************************
>   * Create: allocates video thread output method
>   *****************************************************************************/
> @@ -162,7 +167,7 @@ static int Create( vlc_object_t *p_this )
>      p_sys = p_filter->p_sys;
>      p_sys->b_done = false;
>  
> -    p_filter->pf_video_filter = Filter;
> +    p_filter->ops = &filter_ops;
>  
>      /* needed to get options passed in transcode using the
>       * adjust{name=value} syntax */
> @@ -252,9 +257,8 @@ static picture_t *Filter( filter_t *p_filter, 
> picture_t *p_pic )
>      vlc_tick_t time = vlc_tick_now();
>      for( int i_iter = 0; i_iter < p_sys->i_loops; ++i_iter )
>      {
> -        p_blend->pf_video_blend( p_blend,
> -                                 p_sys->p_base_image, 
> p_sys->p_blend_image,
> -                                 0, 0, p_sys->i_alpha );
> +        filter_Blend( p_blend, p_sys->p_base_image, 
> p_sys->p_blend_image,
> +                      0, 0, p_sys->i_alpha );
>      }
>      time = vlc_tick_now() - time;
>  
> diff --git a/modules/video_filter/bluescreen.c 
> b/modules/video_filter/bluescreen.c
> index 3fe83a1a5df..d624ddf2df9 100644
> --- a/modules/video_filter/bluescreen.c
> +++ b/modules/video_filter/bluescreen.c
> @@ -106,6 +106,11 @@ typedef struct
>      uint8_t *p_at;
>  } filter_sys_t;
>  
> +static const struct vlc_filter_operations filter_ops =
> +{
> +    .filter_video = Filter,
> +};
> +
>  static int Create( vlc_object_t *p_this )
>  {
>      filter_t *p_filter = (filter_t *)p_this;
> @@ -143,7 +148,7 @@ static int Create( vlc_object_t *p_this )
>      p_sys->p_at = NULL;
>  #undef GET_VAR
>  
> -    p_filter->pf_video_filter = Filter;
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/video_filter/canvas.c b/modules/video_filter/canvas.c
> index 80010eecc17..580948f719c 100644
> --- a/modules/video_filter/canvas.c
> +++ b/modules/video_filter/canvas.c
> @@ -144,6 +144,11 @@ static const struct filter_video_callbacks canvas_cbs =
>      video_chain_new, NULL,
>  };
>  
> +static const struct vlc_filter_operations filter_ops =
> +{
> +    .filter_video = Filter,
> +};
> +
>  /*****************************************************************************
>   *
>   *****************************************************************************/
> @@ -373,7 +378,7 @@ static int Activate( vlc_object_t *p_this )
>                    i_canvas_width, i_canvas_height );
>      }
>  
> -    p_filter->pf_video_filter = Filter;
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/video_filter/ci_filters.m 
> b/modules/video_filter/ci_filters.m
> index 154fc44012c..4bc46be3460 100644
> --- a/modules/video_filter/ci_filters.m
> +++ b/modules/video_filter/ci_filters.m
> @@ -376,7 +376,8 @@ Filter(filter_t *filter, picture_t *src)
>  
>      if (ctx->src_converter)
>      {
> -        src = ctx->dst_converter->pf_video_filter(ctx->src_converter, src);
> +        // TODO
> +        src = ctx->dst_converter->ops->filter_video(ctx->src_converter, src);
>          if (!src)
>              return NULL;
>      }
> @@ -416,7 +417,7 @@ Filter(filter_t *filter, picture_t *src)
>  
>      if (ctx->dst_converter)
>      {
> -        dst = ctx->dst_converter->pf_video_filter(ctx->dst_converter, 
> dst);
> +        dst = 
> ctx->dst_converter->ops->filter_video(ctx->dst_converter, dst);
>          if (!dst)
>              return NULL;
>      }
> @@ -568,6 +569,12 @@ CVPX_to_CVPX_converter_Create(filter_t *filter, 
> bool to_rgba)
>      return converter;
>  }
>  
> +static const struct vlc_filter_operations filter_ops =
> +{
> +    .filter_video = Filter,
> +    .video_mouse = Mouse,
> +};
> +
>  static int
>  Open(vlc_object_t *obj, char const *psz_filter)
>  {
> @@ -677,8 +684,7 @@ Open(vlc_object_t *obj, char const *psz_filter)
>      p_sys->psz_filter = psz_filter;
>      p_sys->ctx = ctx;
>  
> -    filter->pf_video_filter = Filter;
> -    filter->pf_video_mouse = Mouse;
> +    filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  
> diff --git a/modules/video_filter/colorthres.c 
> b/modules/video_filter/colorthres.c
> index 3011b94f99d..5f7e483b117 100644
> --- a/modules/video_filter/colorthres.c
> +++ b/modules/video_filter/colorthres.c
> @@ -102,6 +102,16 @@ typedef struct
>      atomic_int i_color;
>  } filter_sys_t;
>  
> +static const struct vlc_filter_operations planar_filter_ops =
> +{
> +    .filter_video = Filter,
> +};
> +
> +static const struct vlc_filter_operations packed_filter_ops =
> +{
> +    .filter_video = FilterPacked,
> +};
> +
>  /*****************************************************************************
>   * Create: allocates adjust video thread output method
>   *****************************************************************************
> @@ -115,11 +125,11 @@ static int Create( vlc_object_t *p_this )
>      switch( p_filter->fmt_in.video.i_chroma )
>      {
>          CASE_PLANAR_YUV
> -            p_filter->pf_video_filter = Filter;
> +            p_filter->ops = &planar_filter_ops;
>              break;
>  
>          CASE_PACKED_YUV_422
> -            p_filter->pf_video_filter = FilterPacked;
> +            p_filter->ops = &packed_filter_ops;
>              break;
>  
>          default:
> diff --git a/modules/video_filter/croppadd.c b/modules/video_filter/croppadd.c
> index 1d2f27fbe45..4471e859a7b 100644
> --- a/modules/video_filter/croppadd.c
> +++ b/modules/video_filter/croppadd.c
> @@ -122,6 +122,11 @@ typedef struct
>      int i_paddright;
>  } filter_sys_t;
>  
> +static const struct vlc_filter_operations filter_ops =
> +{
> +    .filter_video = Filter,
> +};
> +
>  /*****************************************************************************
>   * OpenFilter: probe the filter and return score
>   *****************************************************************************/
> @@ -185,7 +190,7 @@ static int OpenFilter( vlc_object_t *p_this )
>          - p_sys->i_cropleft - p_sys->i_cropright
>          + p_sys->i_paddleft + p_sys->i_paddright;
>  
> -    p_filter->pf_video_filter = Filter;
> +    p_filter->ops = &filter_ops;
>  
>      msg_Dbg( p_filter, "Crop: Top: %d, Bottom: %d, Left: %d, Right: 
> %d",
>               p_sys->i_croptop, p_sys->i_cropbottom, p_sys->i_cropleft,
> diff --git a/modules/video_filter/deinterlace/deinterlace.c 
> b/modules/video_filter/deinterlace/deinterlace.c
> index 1676a07f8ca..714066c21c3 100644
> --- a/modules/video_filter/deinterlace/deinterlace.c
> +++ b/modules/video_filter/deinterlace/deinterlace.c
> @@ -53,7 +53,7 @@
>  /**
>   * Top-level filtering method.
>   *
> - * Open() sets this up as the processing method (pf_video_filter)
> + * Open() sets this up as the processing method (filter_video)
>   * in the filter structure.
>   *
>   * Note that there is no guarantee that the returned picture directly
> @@ -480,6 +480,12 @@ int Mouse( filter_t *p_filter,
>  }
>  
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .filter_video = Deinterlace,
> +    .flush = Flush,
> +    .video_mouse = Mouse,
> +};
> +
>  /*****************************************************************************
>   * Open
>   *****************************************************************************/
> @@ -643,9 +649,7 @@ notsupp:
>      }
>      p_filter->fmt_out.video = fmt;
>      p_filter->fmt_out.i_codec = fmt.i_chroma;
> -    p_filter->pf_video_filter = Deinterlace;
> -    p_filter->pf_flush = Flush;
> -    p_filter->pf_video_mouse  = Mouse;
> +    p_filter->ops = &filter_ops;
>  
>      msg_Dbg( p_filter, "deinterlacing" );
>  
> diff --git a/modules/video_filter/edgedetection.c 
> b/modules/video_filter/edgedetection.c
> index 935189122f1..d425473704f 100644
> --- a/modules/video_filter/edgedetection.c
> +++ b/modules/video_filter/edgedetection.c
> @@ -82,6 +82,11 @@ static const struct filter_video_callbacks 
> filter_video_edge_cbs =
>      new_frame, NULL,
>  };
>  
> +static const struct vlc_filter_operations filter_ops =
> +{
> +    .filter_video = Filter,
> +};
> +
>  
> /*****************************************************************************
>   * Opens the filter.
>   * Allocates and initializes data needed by the filter. The image 
> needs to
> @@ -123,7 +128,7 @@ static int Open( vlc_object_t *p_this )
>          return VLC_EGENERIC;
>      }
>      /* Set callback function */
> -    p_filter->pf_video_filter = Filter;
> +    p_filter->ops = &filter_ops;
>      p_filter->p_sys = sys;
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/video_filter/erase.c b/modules/video_filter/erase.c
> index 7e6683b14e4..0ec8df77d50 100644
> --- a/modules/video_filter/erase.c
> +++ b/modules/video_filter/erase.c
> @@ -122,6 +122,11 @@ static void LoadMask( filter_t *p_filter, const 
> char *psz_filename )
>      image_HandlerDelete( p_image );
>  }
>  
> +static const struct vlc_filter_operations filter_ops =
> +{
> +    .filter_video = Filter,
> +};
> +
>  /*****************************************************************************
>   * Create
>   *****************************************************************************/
> @@ -153,7 +158,7 @@ static int Create( vlc_object_t *p_this )
>          return VLC_ENOMEM;
>      p_sys = p_filter->p_sys;
>  
> -    p_filter->pf_video_filter = Filter;
> +    p_filter->ops = &filter_ops;
>  
>      config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
>                         p_filter->p_cfg );
> diff --git a/modules/video_filter/extract.c b/modules/video_filter/extract.c
> index 0e849642a95..ca5708792c3 100644
> --- a/modules/video_filter/extract.c
> +++ b/modules/video_filter/extract.c
> @@ -89,6 +89,11 @@ typedef struct
>      uint32_t i_color;
>  } filter_sys_t;
>  
> +static const struct vlc_filter_operations filter_ops =
> +{
> +    .filter_video = Filter,
> +};
> +
>  /*****************************************************************************
>   * Create
>   *****************************************************************************/
> @@ -142,7 +147,7 @@ static int Create( vlc_object_t *p_this )
>      var_AddCallback( p_filter, FILTER_PREFIX "component",
>                       ExtractCallback, p_sys );
>  
> -    p_filter->pf_video_filter = Filter;
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/video_filter/fps.c b/modules/video_filter/fps.c
> index 4e6a59dfd66..a4ea6fd671a 100644
> --- a/modules/video_filter/fps.c
> +++ b/modules/video_filter/fps.c
> @@ -131,6 +131,11 @@ static picture_t *Filter( filter_t *p_filter, 
> picture_t *p_picture)
>      return last_pic;
>  }
>  
> +static const struct vlc_filter_operations filter_ops =
> +{
> +    .filter_video = Filter,
> +};
> +
>  static int Open( vlc_object_t *p_this)
>  {
>      filter_t *p_filter = (filter_t*)p_this;
> @@ -180,7 +185,7 @@ static int Open( vlc_object_t *p_this)
>  
>      p_sys->p_previous_pic = NULL;
>  
> -    p_filter->pf_video_filter = Filter;
> +    p_filter->ops = &filter_ops;
>  
>      /* We don't change neither the format nor the picture */
>      if ( p_filter->vctx_in )
> diff --git a/modules/video_filter/freeze.c b/modules/video_filter/freeze.c
> index be5fde8e9d9..719a762619f 100644
> --- a/modules/video_filter/freeze.c
> +++ b/modules/video_filter/freeze.c
> @@ -88,6 +88,12 @@ vlc_module_end()
>   * Local prototypes
>   *****************************************************************************/
>  
> +static const struct vlc_filter_operations filter_ops =
> +{
> +    .filter_video = Filter,
> +    .video_mouse = freeze_mouse,
> +};
> +
>  /**
>   * Open the filter
>   */
> @@ -121,8 +127,7 @@ static int Open( vlc_object_t *p_this )
>  
>      /* init data */
>  
> -    p_filter->pf_video_filter = Filter;
> -    p_filter->pf_video_mouse  = freeze_mouse;
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/video_filter/gaussianblur.c 
> b/modules/video_filter/gaussianblur.c
> index 3a97dbcd34e..ea0aa41167e 100644
> --- a/modules/video_filter/gaussianblur.c
> +++ b/modules/video_filter/gaussianblur.c
> @@ -123,6 +123,11 @@ static void gaussianblur_InitDistribution( 
> filter_sys_t *p_sys )
>      p_sys->pt_distribution = pt_distribution;
>  }
>  
> +static const struct vlc_filter_operations filter_ops =
> +{
> +    .filter_video = Filter,
> +};
> +
>  static int Create( vlc_object_t *p_this )
>  {
>      filter_t *p_filter = (filter_t *)p_this;
> @@ -155,7 +160,7 @@ static int Create( vlc_object_t *p_this )
>      config_ChainParse( p_filter, FILTER_PREFIX, ppsz_filter_options,
>                         p_filter->p_cfg );
>  
> -    p_filter->pf_video_filter = Filter;
> +    p_filter->ops = &filter_ops;
>  
>      p_sys->f_sigma =
>          var_CreateGetFloat( p_filter, FILTER_PREFIX "sigma" );
> diff --git a/modules/video_filter/gradfun.c b/modules/video_filter/gradfun.c
> index 093d207523e..01f0042486a 100644
> --- a/modules/video_filter/gradfun.c
> +++ b/modules/video_filter/gradfun.c
> @@ -110,6 +110,11 @@ typedef struct
>      struct vf_priv_s cfg;
>  } filter_sys_t;
>  
> +static const struct vlc_filter_operations filter_ops =
> +{
> +    .filter_video = Filter,
> +};
> +
>  static int Open(vlc_object_t *object)
>  {
>      filter_t *filter = (filter_t *)object;
> @@ -156,8 +161,8 @@ static int Open(vlc_object_t *object)
>  #endif
>          cfg->filter_line = filter_line_c;
>  
> -    filter->p_sys           = sys;
> -    filter->pf_video_filter = Filter;
> +    filter->p_sys = sys;
> +    filter->ops   = &filter_ops;
>      return VLC_SUCCESS;
>  }
>  
> @@ -236,4 +241,3 @@ static int Callback(vlc_object_t *object, char const *cmd,
>  
>      return VLC_SUCCESS;
>  }
> -
> diff --git a/modules/video_filter/gradient.c b/modules/video_filter/gradient.c
> index 22675305f5e..b7c599d2266 100644
> --- a/modules/video_filter/gradient.c
> +++ b/modules/video_filter/gradient.c
> @@ -124,6 +124,11 @@ typedef struct
>      int *p_pre_hough;
>  } filter_sys_t;
>  
> +static const struct vlc_filter_operations filter_ops =
> +{
> +    .filter_video = Filter,
> +};
> +
>  /*****************************************************************************
>   * Create: allocates Distort video thread output method
>   *****************************************************************************
> @@ -151,7 +156,7 @@ static int Create( vlc_object_t *p_this )
>          return VLC_ENOMEM;
>      p_filter->p_sys = p_sys;
>  
> -    p_filter->pf_video_filter = Filter;
> +    p_filter->ops = &filter_ops;
>  
>      p_sys->p_pre_hough = NULL;
>  
> diff --git a/modules/video_filter/grain.c b/modules/video_filter/grain.c
> index 174de0a822a..b63f836c6eb 100644
> --- a/modules/video_filter/grain.c
> +++ b/modules/video_filter/grain.c
> @@ -378,6 +378,11 @@ static int Callback(vlc_object_t *object, char const *cmd,
>      return VLC_SUCCESS;
>  }
>  
> +static const struct vlc_filter_operations filter_ops =
> +{
> +    .filter_video = Filter,
> +};
> +
>  static int Open(vlc_object_t *object)
>  {
>      filter_t *filter = (filter_t *)object;
> @@ -419,8 +424,8 @@ static int Open(vlc_object_t *object)
>      sys->cfg.variance = var_CreateGetFloatCommand(filter, CFG_PREFIX 
> "variance");
>      var_AddCallback(filter, CFG_PREFIX "variance", Callback, NULL);
>  
> -    filter->p_sys           = sys;
> -    filter->pf_video_filter = Filter;
> +    filter->p_sys = sys;
> +    filter->ops   = &filter_ops;
>      return VLC_SUCCESS;
>  }
>  
> @@ -432,4 +437,3 @@ static void Close(vlc_object_t *object)
>      var_DelCallback(filter, CFG_PREFIX "variance", Callback, NULL);
>      free(sys);
>  }
> -
> diff --git a/modules/video_filter/hqdn3d.c 
> b/modules/video_filter/hqdn3d.c
> index beb57974d8e..0dcbca718e9 100644
> --- a/modules/video_filter/hqdn3d.c
> +++ b/modules/video_filter/hqdn3d.c
> @@ -154,8 +154,13 @@ static int Open(vlc_object_t *this)
>      sys->luma_temp = var_CreateGetFloatCommand(filter, FILTER_PREFIX 
> "luma-temp");
>      sys->chroma_temp = var_CreateGetFloatCommand(filter, FILTER_PREFIX 
> "chroma-temp");
>  
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_video = Filter,
> +    };
> +
>      filter->p_sys = sys;
> -    filter->pf_video_filter = Filter;
> +    filter->ops = &filter_ops;
>  
>      var_AddCallback( filter, FILTER_PREFIX "luma-spat", 
> DenoiseCallback, sys );
>      var_AddCallback( filter, FILTER_PREFIX "chroma-spat", 
> DenoiseCallback, sys );
> diff --git a/modules/video_filter/invert.c 
> b/modules/video_filter/invert.c
> index 67f96977c78..632635ce588 100644
> --- a/modules/video_filter/invert.c
> +++ b/modules/video_filter/invert.c
> @@ -76,7 +76,11 @@ static int Create( vlc_object_t *p_this )
>       || p_chroma->pixel_size * 8 != p_chroma->pixel_bits )
>          return VLC_EGENERIC;
>  
> -    p_filter->pf_video_filter = Filter;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_video = Filter,
> +    };
> +    p_filter->ops = &filter_ops;
>      return VLC_SUCCESS;
>  }
>  
> diff --git a/modules/video_filter/magnify.c 
> b/modules/video_filter/magnify.c
> index e5582bbc0ff..b5d2fe042c0 100644
> --- a/modules/video_filter/magnify.c
> +++ b/modules/video_filter/magnify.c
> @@ -129,8 +129,12 @@ static int Create( vlc_object_t *p_this )
>      p_sys->i_hide_timeout = VLC_TICK_FROM_MS( var_InheritInteger( 
> p_filter, "mouse-hide-timeout" ) );
>  
>      /* */
> -    p_filter->pf_video_filter = Filter;
> -    p_filter->pf_video_mouse = Mouse;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_video = Filter,
> +        .video_mouse = Mouse,
> +    };
> +    p_filter->ops = &filter_ops;
>      return VLC_SUCCESS;
>  }
>  
> @@ -419,4 +423,3 @@ static int Mouse( filter_t *p_filter, vlc_mouse_t 
> *p_new, const vlc_mouse_t *p_o
>      p_new->i_y = p_sys->i_y + p_new->i_y * ZOOM_FACTOR / p_sys->i_zoom;
>      return VLC_SUCCESS;
>  }
> -
> diff --git a/modules/video_filter/mirror.c 
> b/modules/video_filter/mirror.c
> index ab8f9dcdd04..57a1dea39c4 100644
> --- a/modules/video_filter/mirror.c
> +++ b/modules/video_filter/mirror.c
> @@ -155,7 +155,11 @@ static int Create( vlc_object_t *p_this )
>      var_AddCallback( p_filter, CFG_PREFIX "split", FilterCallback, 
> p_sys );
>      var_AddCallback( p_filter, CFG_PREFIX "direction", FilterCallback, 
> p_sys );
>  
> -    p_filter->pf_video_filter = Filter;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_video = Filter,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/video_filter/motionblur.c 
> b/modules/video_filter/motionblur.c
> index 4bd504598c8..b536c0a4520 100644
> --- a/modules/video_filter/motionblur.c
> +++ b/modules/video_filter/motionblur.c
> @@ -113,7 +113,11 @@ static int Create( vlc_object_t *p_this )
>      }
>      p_sys->b_first = true;
>  
> -    p_filter->pf_video_filter = Filter;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_video = Filter,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      config_ChainParse( p_filter, FILTER_PREFIX, ppsz_filter_options,
>                         p_filter->p_cfg );
> diff --git a/modules/video_filter/motiondetect.c 
> b/modules/video_filter/motiondetect.c
> index 9906132a616..4787f5580ca 100644
> --- a/modules/video_filter/motiondetect.c
> +++ b/modules/video_filter/motiondetect.c
> @@ -106,7 +106,11 @@ static int Create( vlc_object_t *p_this )
>                       (char*)&(p_fmt->i_chroma) );
>              return VLC_EGENERIC;
>      }
> -    p_filter->pf_video_filter = Filter;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_video = Filter,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      /* Allocate structure */
>      p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
> diff --git a/modules/video_filter/oldmovie.c b/modules/video_filter/oldmovie.c
> index f7c6a710fc0..c2d4fc7cb54 100644
> --- a/modules/video_filter/oldmovie.c
> +++ b/modules/video_filter/oldmovie.c
> @@ -216,7 +216,11 @@ static int Open( vlc_object_t *p_this ) {
>          return VLC_ENOMEM;
>  
>      /* init data */
> -    p_filter->pf_video_filter = Filter;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_video = Filter,
> +    };
> +    p_filter->ops = &filter_ops;
>      p_sys->i_start_time = p_sys->i_cur_time = p_sys->i_last_time = 
> vlc_tick_now();
>  
>      return VLC_SUCCESS;
> diff --git a/modules/video_filter/opencv_example.cpp 
> b/modules/video_filter/opencv_example.cpp
> index 1334cd4c366..ed4e8cba266 100644
> --- a/modules/video_filter/opencv_example.cpp
> +++ b/modules/video_filter/opencv_example.cpp
> @@ -108,7 +108,14 @@ static int OpenFilter( vlc_object_t *p_this )
>      p_sys->event_info.p_region = NULL;
>      p_sys->i_id = 0;
>  
> -    p_filter->pf_video_filter = Filter;
> +    static const struct FilterOperationInitializer {
> +        struct vlc_filter_operations ops {};
> +        FilterOperationInitializer()
> +        {
> +            ops.filter_video = Filter;
> +        };
> +    } filter_ops;
> +    p_filter->ops = &filter_ops.ops;
>  
>      //create the VIDEO_FILTER_EVENT_VARIABLE
>      vlc_value_t val;
> @@ -157,7 +164,7 @@ static picture_t *Filter( filter_t *p_filter, 
> picture_t *p_pic )
>      CvPoint pt1, pt2;
>      int scale = 1;
>      filter_sys_t *p_sys = static_cast<filter_sys_t *>(p_filter->p_sys);
> - 
> +
>      if ((!p_pic) )
>      {
>          msg_Err( p_filter, "no image array" );
> @@ -217,4 +224,3 @@ static picture_t *Filter( filter_t *p_filter, 
> picture_t *p_pic )
>  
>      return p_pic;
>  }
> -
> diff --git a/modules/video_filter/opencv_wrapper.c 
> b/modules/video_filter/opencv_wrapper.c
> index 2ce695913f9..d103cc7edc8 100644
> --- a/modules/video_filter/opencv_wrapper.c
> +++ b/modules/video_filter/opencv_wrapper.c
> @@ -249,8 +249,12 @@ static int Create( vlc_object_t *p_this )
>      msg_Dbg( p_filter, "opencv_wrapper successfully started" );
>  #endif
>  
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_video = Filter,
> +    };
> +    p_filter->ops = &filter_ops;
>      p_filter->p_sys = p_sys;
> -    p_filter->pf_video_filter = Filter;
>  
>      return VLC_SUCCESS;
>  }
> @@ -414,7 +418,7 @@ static picture_t* Filter( filter_t* p_filter, 
> picture_t* p_pic )
>      VlcPictureToIplImage( p_filter, p_pic );
>      // Pass the image (as a pointer to the first IplImage*) to the
>      // internal OpenCV filter for processing.
> -    p_sys->p_opencv->pf_video_filter( p_sys->p_opencv, 
> (picture_t*)&(p_sys->p_cv_image[0]) );
> +    p_sys->p_opencv->ops->filter_video( p_sys->p_opencv, 
> (picture_t*)&(p_sys->p_cv_image[0]) );
>  
>      if(p_sys->i_wrapper_output == PROCESSED) {
>          // Processed video
> @@ -459,4 +463,3 @@ static picture_t* Filter( filter_t* p_filter, 
> picture_t* p_pic )
>          return NULL;
>      }
>  }
> -
> diff --git a/modules/video_filter/posterize.c 
> b/modules/video_filter/posterize.c
> index 91762a8591c..99d69cac17f 100644
> --- a/modules/video_filter/posterize.c
> +++ b/modules/video_filter/posterize.c
> @@ -134,7 +134,11 @@ static int Create( vlc_object_t *p_this )
>  
>      var_AddCallback( p_filter, CFG_PREFIX "level", FilterCallback, p_sys );
>  
> -    p_filter->pf_video_filter = Filter;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_video = Filter,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/video_filter/postproc.c b/modules/video_filter/postproc.c
> index bf0192de2b0..24fa4bc9ba7 100644
> --- a/modules/video_filter/postproc.c
> +++ b/modules/video_filter/postproc.c
> @@ -252,7 +252,11 @@ static int OpenPostproc( vlc_object_t *p_this )
>      var_AddCallback( p_filter, FILTER_PREFIX "q", PPQCallback, NULL );
>      var_AddCallback( p_filter, FILTER_PREFIX "name", PPNameCallback, NULL );
>  
> -    p_filter->pf_video_filter = PostprocPict;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_video = PostprocPict,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      msg_Warn( p_filter, "Quantification table was not set by video 
> decoder. "
>                          "Postprocessing won't look good." );
> diff --git a/modules/video_filter/psychedelic.c 
> b/modules/video_filter/psychedelic.c
> index 94c57641227..5295ffce441 100644
> --- a/modules/video_filter/psychedelic.c
> +++ b/modules/video_filter/psychedelic.c
> @@ -96,7 +96,11 @@ static int Create( vlc_object_t *p_this )
>          return VLC_ENOMEM;
>      p_filter->p_sys = p_sys;
>  
> -    p_filter->pf_video_filter = Filter;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_video = Filter,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      p_sys->x = 10;
>      p_sys->y = 10;
> diff --git a/modules/video_filter/puzzle.c 
> b/modules/video_filter/puzzle.c
> index bef39368bc4..80b5775c742 100644
> --- a/modules/video_filter/puzzle.c
> +++ b/modules/video_filter/puzzle.c
> @@ -202,8 +202,12 @@ static int Open( vlc_object_t *p_this )
>      var_AddCallback( p_filter, CFG_PREFIX "rotation",     
> puzzle_Callback, p_sys );
>      var_AddCallback( p_filter, CFG_PREFIX "mode",     puzzle_Callback, 
> p_sys );
>  
> -    p_filter->pf_video_filter = Filter;
> -    p_filter->pf_video_mouse = puzzle_mouse;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_video = Filter,
> +        .video_mouse = puzzle_mouse,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/video_filter/ripple.c b/modules/video_filter/ripple.c
> index a190c350039..c6748995bbd 100644
> --- a/modules/video_filter/ripple.c
> +++ b/modules/video_filter/ripple.c
> @@ -90,7 +90,12 @@ static int Create( vlc_object_t *p_this )
>      if( p_sys == NULL )
>          return VLC_ENOMEM;
>      p_filter->p_sys = p_sys;
> -    p_filter->pf_video_filter = Filter;
> +
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_video = Filter,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      p_sys->f_angle = 0.0;
>      p_sys->last_date = 0;
> diff --git a/modules/video_filter/rotate.c 
> b/modules/video_filter/rotate.c
> index 005ba96ec9b..48b9d92cb3a 100644
> --- a/modules/video_filter/rotate.c
> +++ b/modules/video_filter/rotate.c
> @@ -118,6 +118,16 @@ static void fetch_trigo( filter_sys_t *sys, int 
> *i_sin, int *i_cos )
>      *i_cos = sincos.cos;
>  }
>  
> +static const struct vlc_filter_operations planar_filter_ops =
> +{
> +    .filter_video = Filter,
> +};
> +
> +static const struct vlc_filter_operations packed_filter_ops =
> +{
> +    .filter_video = FilterPacked,
> +};
> +
>  /*****************************************************************************
>   * Create: allocates Distort video filter
>   *****************************************************************************/
> @@ -135,11 +145,11 @@ static int Create( vlc_object_t *p_this )
>      switch( p_filter->fmt_in.video.i_chroma )
>      {
>          CASE_PLANAR_YUV
> -            p_filter->pf_video_filter = Filter;
> +            p_filter->ops = &planar_filter_ops;
>              break;
>  
>          CASE_PACKED_YUV_422
> -            p_filter->pf_video_filter = FilterPacked;
> +            p_filter->ops = &packed_filter_ops;
>              break;
>  
>          default:
> diff --git a/modules/video_filter/scale.c b/modules/video_filter/scale.c
> index ef5189e2e13..648be69e4ab 100644
> --- a/modules/video_filter/scale.c
> +++ b/modules/video_filter/scale.c
> @@ -74,7 +74,12 @@ static int OpenFilter( vlc_object_t *p_this )
>  
>  #warning Converter cannot (really) change output format.
>      video_format_ScaleCropAr( &p_filter->fmt_out.video, 
> &p_filter->fmt_in.video );
> -    p_filter->pf_video_filter = Filter;
> +
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_video = Filter,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      msg_Dbg( p_filter, "%ix%i -> %ix%i", p_filter->fmt_in.video.i_width,
>               p_filter->fmt_in.video.i_height, p_filter->fmt_out.video.i_width,
> diff --git a/modules/video_filter/scene.c b/modules/video_filter/scene.c
> index 48ca076dfd8..f655cb4e7a6 100644
> --- a/modules/video_filter/scene.c
> +++ b/modules/video_filter/scene.c
> @@ -148,7 +148,7 @@ typedef struct
>  } filter_sys_t;
>  
>  /*****************************************************************************
> - * Create: initialize and set pf_video_filter()
> + * Create: initialize and set ops
>   *****************************************************************************/
>  static int Create( vlc_object_t *p_this )
>  {
> @@ -197,7 +197,11 @@ static int Create( vlc_object_t *p_this )
>      if( p_sys->psz_path == NULL )
>          p_sys->psz_path = config_GetUserDir( VLC_PICTURES_DIR );
>  
> -    p_filter->pf_video_filter = Filter;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_video = Filter,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/video_filter/sepia.c b/modules/video_filter/sepia.c
> index 9ca567c614e..aa653ffdc83 100644
> --- a/modules/video_filter/sepia.c
> +++ b/modules/video_filter/sepia.c
> @@ -143,7 +143,11 @@ static int Create( vlc_object_t *p_this )
>               var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "intensity" ) );
>      var_AddCallback( p_filter, CFG_PREFIX "intensity", FilterCallback, NULL );
>  
> -    p_filter->pf_video_filter = Filter;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_video = Filter,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/video_filter/sharpen.c b/modules/video_filter/sharpen.c
> index 76972de44af..a53ca5c5001 100644
> --- a/modules/video_filter/sharpen.c
> +++ b/modules/video_filter/sharpen.c
> @@ -118,7 +118,11 @@ static int Create( vlc_object_t *p_this )
>          return VLC_ENOMEM;
>      p_filter->p_sys = p_sys;
>  
> -    p_filter->pf_video_filter = Filter;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_video = Filter,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      config_ChainParse( p_filter, FILTER_PREFIX, ppsz_filter_options,
>                     p_filter->p_cfg );
> diff --git a/modules/video_filter/transform.c b/modules/video_filter/transform.c
> index a9e8dcb1479..ab48f748b41 100644
> --- a/modules/video_filter/transform.c
> +++ b/modules/video_filter/transform.c
> @@ -432,9 +432,13 @@ static int Open(vlc_object_t *object)
>              goto error;
>      }
>  
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_video = Filter,
> +        .video_mouse = Mouse,
> +    };
> +    filter->ops = &filter_ops;
>      filter->p_sys           = sys;
> -    filter->pf_video_filter = Filter;
> -    filter->pf_video_mouse  = Mouse;
>      return VLC_SUCCESS;
>  error:
>      free(sys);
> diff --git a/modules/video_filter/vhs.c b/modules/video_filter/vhs.c
> index 6d5c13db8e8..e27c9b376aa 100644
> --- a/modules/video_filter/vhs.c
> +++ b/modules/video_filter/vhs.c
> @@ -143,7 +143,11 @@ static int Open( vlc_object_t *p_this )
>          return VLC_ENOMEM;
>  
>      /* init data */
> -    p_filter->pf_video_filter = Filter;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_video = Filter,
> +    };
> +    p_filter->ops = &filter_ops;
>      p_sys->i_start_time = p_sys->i_cur_time = p_sys->i_last_time = 
> vlc_tick_now();
>  
>      return VLC_SUCCESS;
> diff --git a/modules/video_filter/wave.c b/modules/video_filter/wave.c
> index eae6c961b74..e732251cacc 100644
> --- a/modules/video_filter/wave.c
> +++ b/modules/video_filter/wave.c
> @@ -90,7 +90,11 @@ static int Create( vlc_object_t *p_this )
>      if( !p_sys )
>          return VLC_ENOMEM;
>  
> -    p_filter->pf_video_filter = Filter;
> +    static const struct vlc_filter_operations filter_ops =
> +    {
> +        .filter_video = Filter,
> +    };
> +    p_filter->ops = &filter_ops;
>  
>      p_sys->f_angle = 0.0;
>      p_sys->last_date = 0;
> diff --git a/modules/visualization/glspectrum.c 
> b/modules/visualization/glspectrum.c
> index 5ca0e75dedb..3e8b878197b 100644
> --- a/modules/visualization/glspectrum.c
> +++ b/modules/visualization/glspectrum.c
> @@ -111,6 +111,10 @@ static void *Thread(void *);
>  const GLfloat lightZeroColor[] = {1.0f, 1.0f, 1.0f, 1.0f};
>  const GLfloat lightZeroPosition[] = {0.0f, 3.0f, 10.0f, 0.0f};
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .filter_audio = DoWork,
> +};
> +
>  /**
>   * Open the module.
>   * @param p_this: the filter object
> @@ -160,7 +164,7 @@ static int Open(vlc_object_t * p_this)
>  
>      p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
>      p_filter->fmt_out.audio = p_filter->fmt_in.audio;
> -    p_filter->pf_audio_filter = DoWork;
> +    p_filter->ops = &filter_ops;
>  
>      return VLC_SUCCESS;
>  }
> diff --git a/modules/visualization/goom.c b/modules/visualization/goom.c
> index fa8f2cc197a..eeeeae90cf7 100644
> --- a/modules/visualization/goom.c
> +++ b/modules/visualization/goom.c
> @@ -104,6 +104,11 @@ static void Flush( filter_t * );
>  
>  static void *Thread( void * );
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .filter_audio = DoWork,
> +    .flush = Flush,
> +};
> +
>  /*****************************************************************************
>   * Open: open a scope effect plugin
>   *****************************************************************************/
> @@ -154,8 +159,7 @@ static int Open( vlc_object_t *p_this )
>  
>      p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
>      p_filter->fmt_out.audio = p_filter->fmt_in.audio;
> -    p_filter->pf_audio_filter = DoWork;
> -    p_filter->pf_flush = Flush;
> +    p_filter->ops = &filter_ops;
>      return VLC_SUCCESS;
>  }
>  
> diff --git a/modules/visualization/projectm.cpp 
> b/modules/visualization/projectm.cpp
> index f4b636fb232..b0e2d0edbb0 100644
> --- a/modules/visualization/projectm.cpp
> +++ b/modules/visualization/projectm.cpp
> @@ -157,6 +157,14 @@ struct filter_sys_t
>  static block_t *DoWork( filter_t *, block_t * );
>  static void *Thread( void * );
>  
> +static const struct FilterOperationInitializer {
> +    struct vlc_filter_operations ops {};
> +    FilterOperationInitializer()
> +    {
> +        ops.filter_audio = DoWork;
> +    };
> +} filter_ops;
> +
>  /**
>   * Open the module
>   * @param p_this: the filter object
> @@ -200,7 +208,7 @@ static int Open( vlc_object_t * p_this )
>  
>      p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
>      p_filter->fmt_out.audio = p_filter->fmt_in.audio;
> -    p_filter->pf_audio_filter = DoWork;
> +    p_filter->ops = &filter_ops.ops;
>      return VLC_SUCCESS;
>  
>  error:
> diff --git a/modules/visualization/visual/visual.c 
> b/modules/visualization/visual/visual.c
> index db693dfbcd9..21c34ad9452 100644
> --- a/modules/visualization/visual/visual.c
> +++ b/modules/visualization/visual/visual.c
> @@ -188,6 +188,11 @@ typedef struct
>      vlc_thread_t    thread;
>  } filter_sys_t;
>  
> +static const struct vlc_filter_operations filter_ops = {
> +    .filter_audio = DoWork,
> +    .flush = Flush,
> +};
> +
>  /*****************************************************************************
>   * Open: open the visualizer
>   *****************************************************************************/
> @@ -323,8 +328,7 @@ static int Open( vlc_object_t *p_this )
>  
>      p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
>      p_filter->fmt_out.audio = p_filter->fmt_in.audio;
> -    p_filter->pf_audio_filter = DoWork;
> -    p_filter->pf_flush = Flush;
> +    p_filter->ops = &filter_ops;
>      return VLC_SUCCESS;
>  
>  error:
> diff --git a/modules/visualization/vsxu.cpp b/modules/visualization/vsxu.cpp
> index dbbb061ecf2..9abea745da8 100644
> --- a/modules/visualization/vsxu.cpp
> +++ b/modules/visualization/vsxu.cpp
> @@ -97,6 +97,14 @@ struct filter_sys_t
>  static block_t *DoWork( filter_t *, block_t * );
>  static void *Thread( void * );
>  
> +static const struct FilterOperationInitializer {
> +    struct vlc_filter_operations ops {};
> +    FilterOperationInitializer()
> +    {
> +        ops.filter_audio = DoWork;
> +    };
> +} filter_ops;
> +
>  /**
>   * Open the module
>   * @param p_this: the filter object
> @@ -141,7 +149,7 @@ static int Open( vlc_object_t * p_this )
>  
>      p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
>      p_filter->fmt_out.audio = p_filter->fmt_in.audio;
> -    p_filter->pf_audio_filter = DoWork;
> +    p_filter->ops = &filter_ops.ops;
>  
>      return VLC_SUCCESS;
>  
> diff --git a/src/audio_output/filters.c b/src/audio_output/filters.c
> index 11a77e95b1f..d2d2b3225a6 100644
> --- a/src/audio_output/filters.c
> +++ b/src/audio_output/filters.c
> @@ -84,7 +84,9 @@ filter_t *aout_filter_Create(vlc_object_t *obj, const 
> filter_owner_t *restrict o
>          filter = NULL;
>      }
>      else
> -        assert (filter->pf_audio_filter != NULL);
> +    {
> +        assert (filter->ops != NULL && filter->ops->filter_audio != 
> NULL);
> +    }
>      return filter;
>  }
>  
> @@ -286,7 +288,7 @@ static block_t *aout_FiltersPipelinePlay(filter_t 
> *const *filters,
>  
>          /* Please note that p_block->i_nb_samples & i_buffer
>           * shall be set by the filter plug-in. */
> -        block = filter->pf_audio_filter (filter, block);
> +        block = filter->ops->filter_audio (filter, block);
>      }
>      return block;
>  }
> diff --git a/src/audio_output/meter.c b/src/audio_output/meter.c
> index 3cfc1158abe..8fd570d0b10 100644
> --- a/src/audio_output/meter.c
> +++ b/src/audio_output/meter.c
> @@ -109,7 +109,7 @@ vlc_audio_meter_AddPlugin(struct vlc_audio_meter 
> *meter, const char *chain,
>          if (plugin->filter == NULL)
>              goto error;
>  
> -        assert(plugin->filter->pf_audio_drain == NULL); /* Not supported */
> +        assert(plugin->filter->ops->drain_audio == NULL); /* Not supported */
>      }
>  
>      vlc_mutex_lock(&meter->lock);
> @@ -198,7 +198,7 @@ vlc_audio_meter_Process(struct vlc_audio_meter 
> *meter, block_t *block, vlc_tick_
>          {
>              plugin->last_date = date + block->i_length;
>  
> -            block_t *same_block = filter->pf_audio_filter(filter, block);
> +            block_t *same_block = filter->ops->filter_audio(filter, block);
>              assert(same_block == block); (void) same_block;
>          }
>      }
> @@ -215,8 +215,8 @@ vlc_audio_meter_Flush(struct vlc_audio_meter *meter)
>      vlc_list_foreach(plugin, &meter->plugins, node)
>      {
>          filter_t *filter = plugin->filter;
> -        if (filter != NULL && filter->pf_flush != NULL)
> -            filter->pf_flush(filter);
> +        if (filter != NULL && filter->ops->flush != NULL)
> +            filter->ops->flush(filter);
>      }
>  
>      vlc_mutex_unlock(&meter->lock);
> diff --git a/src/misc/filter.c b/src/misc/filter.c
> index e8c1cae82eb..ed23e1d7fe6 100644
> --- a/src/misc/filter.c
> +++ b/src/misc/filter.c
> @@ -164,7 +164,7 @@ int filter_Blend( vlc_blender_t *p_blend,
>      if( !p_blend->p_module )
>          return VLC_EGENERIC;
>  
> -    p_blend->pf_video_blend( p_blend, p_dst, p_src, i_dst_x, i_dst_y, 
> i_alpha );
> +    p_blend->ops->blend_video( p_blend, p_dst, p_src, i_dst_x, 
> i_dst_y, i_alpha );
>      return VLC_SUCCESS;
>  }
>  
> diff --git a/src/misc/filter_chain.c b/src/misc/filter_chain.c
> index 645a27507fd..bce6980d9bb 100644
> --- a/src/misc/filter_chain.c
> +++ b/src/misc/filter_chain.c
> @@ -429,7 +429,7 @@ static picture_t *FilterChainVideoFilter( 
> chained_filter_t *f, picture_t *p_pic
>      for( ; f != NULL; f = f->next )
>      {
>          filter_t *p_filter = &f->filter;
> -        p_pic = p_filter->pf_video_filter( p_filter, p_pic );
> +        p_pic = p_filter->ops->filter_video( p_filter, p_pic );
>          if( !p_pic )
>              break;
>          if( f->pending )
> @@ -483,7 +483,7 @@ void filter_chain_SubSource( filter_chain_t 
> *p_chain, spu_t *spu,
>      for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
>      {
>          filter_t *p_filter = &f->filter;
> -        subpicture_t *p_subpic = p_filter->pf_sub_source( p_filter, 
> display_date );
> +        subpicture_t *p_subpic = p_filter->ops->source_sub( p_filter, 
> display_date );
>          if( p_subpic )
>              spu_PutSubpicture( spu, p_subpic );
>      }
> @@ -495,7 +495,7 @@ subpicture_t *filter_chain_SubFilter( 
> filter_chain_t *p_chain, subpicture_t *p_s
>      {
>          filter_t *p_filter = &f->filter;
>  
> -        p_subpic = p_filter->pf_sub_filter( p_filter, p_subpic );
> +        p_subpic = p_filter->ops->filter_sub( p_filter, p_subpic );
>  
>          if( !p_subpic )
>              break;
> @@ -512,13 +512,13 @@ int filter_chain_MouseFilter( filter_chain_t 
> *p_chain, vlc_mouse_t *p_dst, const
>          filter_t *p_filter = &f->filter;
>          vlc_mouse_t *p_mouse = f->mouse;
>  
> -        if( p_filter->pf_video_mouse && p_mouse )
> +        if( p_filter->ops->video_mouse && p_mouse )
>          {
>              vlc_mouse_t old = *p_mouse;
>              vlc_mouse_t filtered = current;
>  
>              *p_mouse = current;
> -            if( p_filter->pf_video_mouse( p_filter, &filtered, &old ) )
> +            if( p_filter->ops->video_mouse( p_filter, &filtered, &old) 
> )
>                  return VLC_EGENERIC;
>              current = filtered;
>          }
> diff --git a/src/misc/image.c b/src/misc/image.c
> index faa05d9b018..e8f508c66b3 100644
> --- a/src/misc/image.c
> +++ b/src/misc/image.c
> @@ -270,7 +270,7 @@ static picture_t *ImageRead( image_handler_t 
> *p_image, block_t *p_block,
>              video_format_Copy( &p_image->p_converter->fmt_out.video, 
> p_fmt_out);
>          }
>  
> -        p_pic = p_image->p_converter->pf_video_filter( 
> p_image->p_converter, p_pic );
> +        p_pic = p_image->p_converter->ops->filter_video( 
> p_image->p_converter, p_pic );
>      }
>      else
>      {
> @@ -434,7 +434,7 @@ static block_t *ImageWrite( image_handler_t 
> *p_image, picture_t *p_pic,
>          picture_Hold( p_pic );
>  
>          p_tmp_pic =
> -            p_image->p_converter->pf_video_filter( 
> p_image->p_converter, p_pic );
> +            p_image->p_converter->ops->filter_video( 
> p_image->p_converter, p_pic );
>  
>          if( likely(p_tmp_pic != NULL) )
>          {
> @@ -569,7 +569,7 @@ static picture_t *ImageConvert( image_handler_t 
> *p_image, picture_t *p_pic,
>  
>      picture_Hold( p_pic );
>  
> -    return p_image->p_converter->pf_video_filter( 
> p_image->p_converter, p_pic );
> +    return p_image->p_converter->ops->filter_video( 
> p_image->p_converter, p_pic );
>  }
>  
>  /**
> diff --git a/src/video_output/vout_subpictures.c 
> b/src/video_output/vout_subpictures.c
> index 8766e7b5820..fefcf21b8eb 100644
> --- a/src/video_output/vout_subpictures.c
> +++ b/src/video_output/vout_subpictures.c
> @@ -343,7 +343,7 @@ static int SpuRenderText(spu_t *spu,
>      text->fmt_out.video.i_height =
>      text->fmt_out.video.i_visible_height = i_original_height;
>  
> -    int i_ret = text->pf_render(text, region, region, chroma_list);
> +    int i_ret = text->ops->render(text, region, region, chroma_list);
>  
>      vlc_mutex_unlock(&sys->textlock);
>      return i_ret;
> @@ -996,7 +996,7 @@ static void SpuRenderRegion(spu_t *spu,
>                  scale_yuvp->fmt_out.video = region->fmt;
>                  scale_yuvp->fmt_out.video.i_chroma = chroma_list[0];
>  
> -                picture = scale_yuvp->pf_video_filter(scale_yuvp, picture);
> +                picture = scale_yuvp->ops->filter_video(scale_yuvp, picture);
>                  if (!picture) {
>                      /* Well we will try conversion+scaling */
>                      msg_Warn(spu, "%4.4s to %4.4s conversion failed",
> @@ -1027,7 +1027,7 @@ static void SpuRenderRegion(spu_t *spu,
>                  scale->fmt_out.video.i_visible_height =
>                      spu_scale_h(region->fmt.i_visible_height, scale_size);
>  
> -                picture = scale->pf_video_filter(scale, picture);
> +                picture = scale->ops->filter_video(scale, picture);
>                  if (!picture)
>                      msg_Err(spu, "scaling failed");
>              }
> -- 
> 2.26.2
> 
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel


More information about the vlc-devel mailing list