[vlc-devel] [PATCH v1 26/33] video_filter: use the filter push mode

Steve Lhomme robux4 at ycbcr.xyz
Fri Sep 25 16:47:02 CEST 2020


---
 modules/hw/d3d11/d3d11_surface.c              | 15 ++---
 modules/hw/d3d9/d3d9_filters.c                | 12 ++--
 modules/hw/d3d9/dxa9.c                        | 15 ++---
 modules/hw/mmal/converter.c                   | 18 +++---
 modules/hw/mmal/deinterlace.c                 |  6 +-
 modules/hw/nvdec/chroma.c                     | 18 +++---
 modules/hw/vaapi/chroma.c                     | 31 +++++-----
 modules/hw/vaapi/filters.c                    | 20 ++++---
 modules/hw/vdpau/adjust.c                     |  8 +--
 modules/hw/vdpau/chroma.c                     | 43 +++++++-------
 modules/hw/vdpau/sharpen.c                    |  8 +--
 modules/spu/audiobargraph_v.c                 |  8 +--
 modules/spu/logo.c                            | 10 ++--
 modules/video_chroma/cvpx.c                   | 56 +++++++++----------
 modules/video_chroma/rv32.c                   | 14 ++---
 modules/video_chroma/swscale.c                | 15 +++--
 modules/video_filter/adjust.c                 | 34 +++++------
 modules/video_filter/alphamask.c              |  8 +--
 modules/video_filter/anaglyph.c               | 14 ++---
 modules/video_filter/antiflicker.c            | 14 ++---
 modules/video_filter/ball.c                   | 12 ++--
 modules/video_filter/blendbench.c             | 14 ++---
 modules/video_filter/bluescreen.c             | 12 ++--
 modules/video_filter/canvas.c                 |  8 +--
 modules/video_filter/colorthres.c             | 26 ++++-----
 modules/video_filter/croppadd.c               | 12 ++--
 .../video_filter/deinterlace/deinterlace.c    |  2 +-
 modules/video_filter/edgedetection.c          | 10 ++--
 modules/video_filter/erase.c                  | 12 ++--
 modules/video_filter/extract.c                | 14 ++---
 modules/video_filter/freeze.c                 | 15 ++---
 modules/video_filter/gaussianblur.c           | 12 ++--
 modules/video_filter/gradfun.c                | 13 ++---
 modules/video_filter/gradient.c               | 12 ++--
 modules/video_filter/grain.c                  | 11 ++--
 modules/video_filter/hqdn3d.c                 | 14 ++---
 modules/video_filter/invert.c                 | 12 ++--
 modules/video_filter/magnify.c                | 10 ++--
 modules/video_filter/mirror.c                 | 12 ++--
 modules/video_filter/motionblur.c             | 12 ++--
 modules/video_filter/motiondetect.c           | 13 +++--
 modules/video_filter/oldmovie.c               | 22 ++++----
 modules/video_filter/posterize.c              | 12 ++--
 modules/video_filter/postproc.c               | 10 ++--
 modules/video_filter/psychedelic.c            | 12 ++--
 modules/video_filter/puzzle.c                 | 22 ++++----
 modules/video_filter/puzzle.h                 |  2 +-
 modules/video_filter/ripple.c                 | 12 ++--
 modules/video_filter/rotate.c                 | 26 ++++-----
 modules/video_filter/scale.c                  | 15 +++--
 modules/video_filter/scene.c                  | 21 +++----
 modules/video_filter/sepia.c                  | 12 ++--
 modules/video_filter/sharpen.c                | 10 ++--
 modules/video_filter/transform.c              | 11 ++--
 modules/video_filter/vhs.c                    | 18 +++---
 modules/video_filter/wave.c                   | 12 ++--
 56 files changed, 416 insertions(+), 426 deletions(-)

diff --git a/modules/hw/d3d11/d3d11_surface.c b/modules/hw/d3d11/d3d11_surface.c
index f8af28a33a1..51db375d182 100644
--- a/modules/hw/d3d11/d3d11_surface.c
+++ b/modules/hw/d3d11/d3d11_surface.c
@@ -32,6 +32,7 @@
 #include <vlc_filter.h>
 #include <vlc_picture.h>
 #include <vlc_modules.h>
+#include "../../video_filter/filter_picture.h"
 
 #include <assert.h>
 
@@ -648,16 +649,16 @@ done:
     return NULL;
 }
 
-static picture_t *NV12_D3D11_Filter( filter_t *p_filter, picture_t *p_pic )
+static int NV12_D3D11_Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink * sink )
 {
     picture_t *p_outpic = AllocateCPUtoGPUTexture( p_filter, p_filter->p_sys );
-    if( p_outpic )
+    if( unlikely(p_outpic == NULL) )
     {
-        NV12_D3D11( p_filter, p_pic, p_outpic );
-        picture_CopyProperties( p_outpic, p_pic );
+        picture_Release( p_pic );
+        return VLC_ENOMEM;
     }
-    picture_Release( p_pic );
-    return p_outpic;
+    NV12_D3D11( p_filter, p_pic, p_outpic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
 
 int D3D11OpenConverter( vlc_object_t *obj )
@@ -753,7 +754,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->pf_video_filter_into = NV12_D3D11_Filter;
         break;
     default:
         return VLC_EGENERIC;
diff --git a/modules/hw/d3d9/d3d9_filters.c b/modules/hw/d3d9/d3d9_filters.c
index 80106bce5b2..0709d0e82fd 100644
--- a/modules/hw/d3d9/d3d9_filters.c
+++ b/modules/hw/d3d9/d3d9_filters.c
@@ -33,6 +33,7 @@
 #include <vlc_filter.h>
 #include <vlc_picture.h>
 #include <vlc_codec.h>
+#include "../../video_filter/filter_picture.h"
 
 #define COBJMACROS
 #include <initguid.h>
@@ -137,7 +138,7 @@ static picture_t *AllocPicture( filter_t *p_filter )
     return pic;
 }
 
-static picture_t *Filter(filter_t *p_filter, picture_t *p_pic)
+static int Filter(filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink * sink )
 {
     filter_sys_t *p_sys = p_filter->p_sys;
 
@@ -151,8 +152,6 @@ static picture_t *Filter(filter_t *p_filter, picture_t *p_pic)
     if( !p_out_sys || !p_out_sys->surface )
         goto failed;
 
-    picture_CopyProperties( p_outpic, p_pic );
-
     RECT area;
     D3DSURFACE_DESC srcDesc, dstDesc;
     HRESULT hr;
@@ -197,11 +196,10 @@ static picture_t *Filter(filter_t *p_filter, picture_t *p_pic)
     if (FAILED(hr))
         goto failed;
 
-    picture_Release( p_pic );
-    return p_outpic;
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 failed:
     picture_Release( p_pic );
-    return NULL;
+    return VLC_EGENERIC;
 }
 
 static LONG StoreLevel(const struct filter_level *range, const DXVA2_ValueRange *Range, float val)
@@ -450,7 +448,7 @@ static int D3D9OpenAdjust(vlc_object_t *obj)
 
     sys->hdecoder_dll = hdecoder_dll;
 
-    filter->pf_video_filter = Filter;
+    filter->pf_video_filter_into = Filter;
     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 fec17111e97..b53ded16e12 100644
--- a/modules/hw/d3d9/dxa9.c
+++ b/modules/hw/d3d9/dxa9.c
@@ -32,6 +32,7 @@
 #include <vlc_filter.h>
 #include <vlc_picture.h>
 #include <vlc_modules.h>
+#include "../../video_filter/filter_picture.h"
 
 #include "d3d9_filters.h"
 
@@ -364,16 +365,16 @@ done:
 VIDEO_FILTER_WRAPPER2 (DXA9_YV12)
 VIDEO_FILTER_WRAPPER2 (DXA9_NV12)
 
-static picture_t *YV12_D3D9_Filter( filter_t *p_filter, picture_t *p_pic )
+static int YV12_D3D9_Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink * sink )
 {
     picture_t *p_outpic = AllocateCPUtoGPUTexture( p_filter );
-    if( p_outpic )
+    if( unlikely(p_outpic == NULL) )
     {
-        YV12_D3D9( p_filter, p_pic, p_outpic );
-        picture_CopyProperties( p_outpic, p_pic );
+        picture_Release( p_pic );
+        return VLC_ENOMEM;
     }
-    picture_Release( p_pic );
-    return p_outpic;
+    YV12_D3D9( p_filter, p_pic, p_outpic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
 
 int D3D9OpenConverter( vlc_object_t *obj )
@@ -452,7 +453,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->pf_video_filter_into = YV12_D3D9_Filter;
         break;
     default:
         return VLC_EGENERIC;
diff --git a/modules/hw/mmal/converter.c b/modules/hw/mmal/converter.c
index 2cb494b3aa8..c422e92d900 100644
--- a/modules/hw/mmal/converter.c
+++ b/modules/hw/mmal/converter.c
@@ -438,11 +438,10 @@ static void conv_flush(filter_t * p_filter)
     sys->err_stream = MMAL_SUCCESS;
 }
 
-static void conv_stash_fixup(filter_t * const p_filter, converter_sys_t * const sys, picture_t * const p_pic)
+static void conv_stash_fixup(converter_sys_t * const sys, picture_t * const p_pic)
 {
     conv_frame_stash_t * const stash = sys->stash + (p_pic->date & 0xf);
     unsigned int sub_no;
-    VLC_UNUSED(p_filter);
 
     p_pic->date = stash->pts;
     for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
@@ -514,10 +513,9 @@ static MMAL_STATUS_T conv_set_output(filter_t * const p_filter, converter_sys_t
     return MMAL_SUCCESS;
 }
 
-static picture_t *conv_filter(filter_t *p_filter, picture_t *p_pic)
+static int conv_filter(filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink * sink)
 {
     converter_sys_t * const sys = p_filter->p_sys;
-    picture_t * ret_pics;
     MMAL_STATUS_T err;
     const uint64_t frame_seq = ++sys->frame_seq;
     conv_frame_stash_t * const stash = sys->stash + (frame_seq & 0xf);
@@ -690,21 +688,21 @@ static picture_t *conv_filter(filter_t *p_filter, picture_t *p_pic)
     // wait for.
     // This means we get a single static pic out
     if (sys->pic_n++ == 1) {
-        return NULL;
+        return VLC_EGENERIC;
     }
     vlc_sem_wait(&sys->sem);
 
     // Return a single pending buffer
     vlc_mutex_lock(&sys->lock);
-    ret_pics = pic_fifo_get(&sys->ret_pics);
+    picture_t * p_outpic = pic_fifo_get(&sys->ret_pics);
     vlc_mutex_unlock(&sys->lock);
 
     if (sys->err_stream != MMAL_SUCCESS)
         goto stream_fail;
 
-    conv_stash_fixup(p_filter, sys, ret_pics);
+    conv_stash_fixup(sys, p_outpic);
 
-    return ret_pics;
+    return vlc_video_sink_PutPicture(sink, p_outpic);
 
 stream_fail:
     msg_Err(p_filter, "MMAL error reported by callback");
@@ -714,7 +712,7 @@ fail:
     if (p_pic != NULL)
         picture_Release(p_pic);
     conv_flush(p_filter);
-    return NULL;
+    return VLC_EGENERIC;
 }
 
 void CloseConverter(vlc_object_t * obj)
@@ -969,7 +967,7 @@ retry:
         }
     }
 
-    p_filter->pf_video_filter = conv_filter;
+    p_filter->pf_video_filter_into = conv_filter;
     p_filter->pf_flush = conv_flush;
     // video_drain NIF in filter structure
 
diff --git a/modules/hw/mmal/deinterlace.c b/modules/hw/mmal/deinterlace.c
index 8305dc90555..e363c4d0f8d 100644
--- a/modules/hw/mmal/deinterlace.c
+++ b/modules/hw/mmal/deinterlace.c
@@ -344,12 +344,12 @@ static void pass_flush(filter_t *p_filter)
     VLC_UNUSED(p_filter);
 }
 
-static picture_t * pass_deinterlace(filter_t * p_filter, picture_t * p_pic)
+static int pass_deinterlace(filter_t * p_filter, picture_t * p_pic, struct vlc_video_sink * sink)
 {
     VLC_UNUSED(p_filter);
 
     p_pic->b_progressive = true;
-    return p_pic;
+    return vlc_video_sink_PutPicture( sink, p_pic );
 }
 
 
@@ -498,7 +498,7 @@ static int OpenMmalDeinterlace(vlc_object_t *p_this)
 
     if (sys->use_passthrough)
     {
-        filter->pf_video_filter = pass_deinterlace;
+        filter->pf_video_filter_into = pass_deinterlace;
         filter->pf_flush = pass_flush;
         return VLC_SUCCESS;
     }
diff --git a/modules/hw/nvdec/chroma.c b/modules/hw/nvdec/chroma.c
index 88353f5cbed..16aaef2abde 100644
--- a/modules/hw/nvdec/chroma.c
+++ b/modules/hw/nvdec/chroma.c
@@ -28,6 +28,7 @@
 #include <vlc_plugin.h>
 #include <vlc_filter.h>
 #include <vlc_codec.h>
+#include "../../video_filter/filter_picture.h"
 
 #include "nvdec_fmt.h"
 
@@ -45,13 +46,13 @@ vlc_module_end()
 #define CALL_CUDA(func, ...) CudaCheckErr(VLC_OBJECT(p_filter), devsys->cudaFunctions, devsys->cudaFunctions->func(__VA_ARGS__), #func)
 
 
-static picture_t * FilterCUDAToCPU( filter_t *p_filter, picture_t *src )
+static int FilterCUDAToCPU( filter_t *p_filter, picture_t *src, struct vlc_video_sink * sink )
 {
     picture_t *dst = filter_NewPicture( p_filter );
     if (unlikely(dst == NULL))
     {
         picture_Release(src);
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     pic_context_nvdec_t *srcpic = container_of(src->context, pic_context_nvdec_t, ctx);
@@ -65,7 +66,7 @@ static picture_t * FilterCUDAToCPU( filter_t *p_filter, picture_t *src )
         picture_Release(dst);
         picture_Release(src);
         vlc_decoder_device_Release(dec_dev);
-        return NULL;
+        return VLC_EGENERIC;
     }
 
     int sync_result;
@@ -93,7 +94,6 @@ static picture_t * FilterCUDAToCPU( filter_t *p_filter, picture_t *src )
             goto done;
         srcY += srcpic->bufferHeight;
     }
-    picture_CopyProperties(dst, src);
 
 done:
     // Always synchronize the cuda stream before releasing src:
@@ -106,15 +106,15 @@ done:
     result = result != VLC_SUCCESS ? result : sync_result;
 
     CALL_CUDA(cuCtxPopCurrent, NULL);
+    vlc_decoder_device_Release(dec_dev);
 
     if (result != VLC_SUCCESS)
     {
         picture_Release(dst);
-        dst = NULL;
+        picture_Release(src);
+        return VLC_EGENERIC;
     }
-    picture_Release(src);
-    vlc_decoder_device_Release(dec_dev);
-    return dst;
+    return CopyInfoAndPush( dst, src, sink );
 }
 
 static int OpenCUDAToCPU( vlc_object_t *p_this )
@@ -137,7 +137,7 @@ static int OpenCUDAToCPU( vlc_object_t *p_this )
            ) )
         return VLC_EGENERIC;
 
-    p_filter->pf_video_filter = FilterCUDAToCPU;
+    p_filter->pf_video_filter_into = FilterCUDAToCPU;
 
     return VLC_SUCCESS;
 }
diff --git a/modules/hw/vaapi/chroma.c b/modules/hw/vaapi/chroma.c
index 38b904ed203..e69aafbf744 100644
--- a/modules/hw/vaapi/chroma.c
+++ b/modules/hw/vaapi/chroma.c
@@ -32,6 +32,7 @@
 #include <vlc_filter.h>
 #include <vlc_plugin.h>
 
+#include "../../video_filter/filter_picture.h"
 #include "../../video_chroma/copy.h"
 #include "filters.h"
 
@@ -121,8 +122,8 @@ FillPictureFromVAImage(picture_t *dest,
     }
 }
 
-static picture_t *
-DownloadSurface(filter_t *filter, picture_t *src_pic)
+static int
+DownloadSurface(filter_t *filter, picture_t *src_pic, struct vlc_video_sink *sink)
 {
     filter_sys_t *const filter_sys = filter->p_sys;
     VADisplay           va_dpy = vlc_vaapi_PicGetDisplay(src_pic);
@@ -174,18 +175,16 @@ DownloadSurface(filter_t *filter, picture_t *src_pic)
     vlc_vaapi_UnmapBuffer(VLC_OBJECT(filter), va_dpy, src_img.buf);
     vlc_vaapi_DestroyImage(VLC_OBJECT(filter), va_dpy, src_img.image_id);
 
-    picture_CopyProperties(dest, src_pic);
-ret:
-    picture_Release(src_pic);
-    return dest;
+    return CopyInfoAndPush(dest, src_pic, sink);
 
 error:
     if (image_fallback_id != VA_INVALID_ID)
         vlc_vaapi_DestroyImage(VLC_OBJECT(filter), va_dpy, image_fallback_id);
 
     picture_Release(dest);
-    dest = NULL;
-    goto ret;
+ret:
+    picture_Release(src_pic);
+    return VLC_EGENERIC;
 }
 
 static inline void
@@ -235,8 +234,8 @@ FillVAImageFromPicture(VAImage *dest_img, uint8_t *dest_buf,
     dest_pic->p[1].p_pixels = tmp[1];
 }
 
-static picture_t *
-UploadSurface(filter_t *filter, picture_t *src)
+static int
+UploadSurface(filter_t *filter, picture_t *src, struct vlc_video_sink *sink)
 {
     filter_sys_t   *p_sys = filter->p_sys;
     VADisplay const va_dpy = p_sys->dpy;
@@ -250,7 +249,6 @@ UploadSurface(filter_t *filter, picture_t *src)
         goto ret;
     }
     vlc_vaapi_PicAttachContext(dest_pic);
-    picture_CopyProperties(dest_pic, src);
 
     if (vlc_vaapi_DeriveImage(VLC_OBJECT(filter), va_dpy,
                               vlc_vaapi_PicGetSurface(dest_pic), &dest_img)
@@ -266,14 +264,13 @@ UploadSurface(filter_t *filter, picture_t *src)
                                   va_dpy, dest_img.image_id))
         goto error;
 
-ret:
-    picture_Release(src);
-    return dest_pic;
+    return CopyInfoAndPush(dest_pic, src, sink);
 
 error:
     picture_Release(dest_pic);
-    dest_pic = NULL;
-    goto ret;
+ret:
+    picture_Release(src);
+    return VLC_EGENERIC;
 }
 
 static int CheckFmt(const video_format_t *in, const video_format_t *out,
@@ -333,7 +330,7 @@ vlc_vaapi_OpenChroma(vlc_object_t *obj)
                  &pixel_bytes))
         return VLC_EGENERIC;
 
-    filter->pf_video_filter = is_upload ? UploadSurface : DownloadSurface;
+    filter->pf_video_filter_into = is_upload ? UploadSurface : DownloadSurface;
 
     if (!(filter_sys = calloc(1, sizeof(filter_sys_t))))
     {
diff --git a/modules/hw/vaapi/filters.c b/modules/hw/vaapi/filters.c
index 2acfc744ef0..5e67034d85a 100644
--- a/modules/hw/vaapi/filters.c
+++ b/modules/hw/vaapi/filters.c
@@ -489,13 +489,15 @@ Adjust_UpdateVAFilterParams(void * p_data, void * va_params)
                                      memory_order_relaxed);
 }
 
-static picture_t *
-Adjust(filter_t * filter, picture_t * src)
+static int
+Adjust(filter_t * filter, picture_t * src, struct vlc_video_sink *sink)
 {
     picture_t *const    dest =
         Filter(filter, src, Adjust_UpdateVAFilterParams, NULL, NULL);
     picture_Release(src);
-    return dest;
+    if (dest == NULL)
+        return VLC_EGENERIC;
+    return vlc_video_sink_PutPicture( sink, dest );
 }
 
 static int
@@ -587,7 +589,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->pf_video_filter_into = Adjust;
 
     return VLC_SUCCESS;
 
@@ -628,13 +630,15 @@ BasicFilter_UpdateVAFilterParams(void * p_data, void * va_params)
     p_va_param->value = atomic_load_explicit(drv_value, memory_order_relaxed);
 }
 
-static picture_t *
-BasicFilter(filter_t * filter, picture_t * src)
+static int
+BasicFilter(filter_t * filter, picture_t * src, struct vlc_video_sink *sink)
 {
     picture_t *const    dest =
         Filter(filter, src, BasicFilter_UpdateVAFilterParams, NULL, NULL);
     picture_Release(src);
-    return dest;
+    if (dest == NULL)
+        return VLC_EGENERIC;
+    return vlc_video_sink_PutPicture(sink, dest);
 }
 
 static int
@@ -708,7 +712,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->pf_video_filter_into = BasicFilter;
 
     return VLC_SUCCESS;
 
diff --git a/modules/hw/vdpau/adjust.c b/modules/hw/vdpau/adjust.c
index 27c8b4bfa7d..da7e714fc49 100644
--- a/modules/hw/vdpau/adjust.c
+++ b/modules/hw/vdpau/adjust.c
@@ -116,13 +116,13 @@ static int HueCallback(vlc_object_t *obj, const char *varname,
     return VLC_SUCCESS;
 }
 
-static picture_t *Adjust(filter_t *filter, picture_t *pic)
+static int Adjust(filter_t *filter, picture_t *pic, struct vlc_video_sink *sink)
 {
     const filter_sys_t *sys = filter->p_sys;
     vlc_vdp_video_field_t *f = VDPAU_FIELD_FROM_PICCTX(pic->context);
 
     if (unlikely(f == NULL))
-        return pic;
+        return vlc_video_sink_PutPicture(sink, pic);
 
     f->procamp.brightness = atomic_load_explicit(&sys->brightness,
                                                  memory_order_relaxed);
@@ -132,7 +132,7 @@ static picture_t *Adjust(filter_t *filter, picture_t *pic)
                                                  memory_order_relaxed);
     f->procamp.hue = atomic_load_explicit(&sys->hue, memory_order_relaxed);
 
-    return pic;
+    return vlc_video_sink_PutPicture(sink, pic);
 }
 
 static const char *const options[] = {
@@ -157,7 +157,7 @@ static int Open(vlc_object_t *obj)
     if (unlikely(sys == NULL))
         return VLC_ENOMEM;
 
-    filter->pf_video_filter = Adjust;
+    filter->pf_video_filter_into = Adjust;
     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..52fd664bee1 100644
--- a/modules/hw/vdpau/chroma.c
+++ b/modules/hw/vdpau/chroma.c
@@ -32,6 +32,8 @@
 #include <vlc_picture.h>
 #include <vlc_picture_pool.h>
 #include "vlc_vdpau.h"
+#include "../../video_filter/filter_picture.h"
+
 
 /* Picture history as recommended by VDPAU documentation */
 #define MAX_PAST   2
@@ -293,8 +295,8 @@ static void Flush(filter_t *filter)
 }
 
 /** Export a VDPAU video surface picture to a normal VLC picture */
-static picture_t *VideoExport(filter_t *filter, picture_t *src, picture_t *dst,
-                              VdpYCbCrFormat format)
+static int VideoExport(filter_t *filter, picture_t *src, picture_t *dst,
+                       struct vlc_video_sink *sink, VdpYCbCrFormat format)
 {
     vlc_vdp_video_field_t *field = VDPAU_FIELD_FROM_PICCTX(src->context);
     VdpStatus err;
@@ -304,7 +306,6 @@ static picture_t *VideoExport(filter_t *filter, picture_t *src, picture_t *dst,
 
     vdpau_decoder_device_t *vdpau_decoder =
         GetVDPAUOpaqueContext(picture_GetVideoContext(src));
-    picture_CopyProperties(dst, src);
 
     for (int i = 0; i < dst->i_planes; i++)
     {
@@ -327,10 +328,10 @@ static picture_t *VideoExport(filter_t *filter, picture_t *src, picture_t *dst,
         msg_Err(filter, "video %s %s failure: %s", "surface", "export",
                 vdp_get_error_string(vdpau_decoder->vdp, err));
         picture_Release(dst);
-        dst = NULL;
+        picture_Release(src);
+        return VLC_EGENERIC;
     }
-    picture_Release(src);
-    return dst;
+    return CopyInfoAndPush(dst, src, sink);
 }
 
 /** Import VLC picture into VDPAU video surface */
@@ -445,7 +446,7 @@ static picture_context_t *OutputSurfaceClone(picture_context_t *ctx)
     return dts_ctx;
 }
 
-static picture_t *Render(filter_t *filter, picture_t *src, bool import)
+static int Render(filter_t *filter, picture_t *src, struct vlc_video_sink *sink, bool import)
 {
     vlc_vdp_mixer_t *sys = filter->p_sys;
     picture_t *dst = NULL;
@@ -455,7 +456,7 @@ static picture_t *Render(filter_t *filter, picture_t *src, bool import)
     {
         msg_Err(filter, "corrupt VDPAU video surface %p", (void *)src);
         picture_Release(src);
-        return NULL;
+        return VLC_EGENERIC;
     }
 
     /* Update history and take "present" picture field */
@@ -671,23 +672,27 @@ skip:
     memmove(sys->history, sys->history + 1, /* Advance history */
             sizeof (sys->history[0]) * (MAX_PAST + MAX_FUTURE));
 
-    return dst;
+    if (dst == NULL)
+        return VLC_EGENERIC;
+    return vlc_video_sink_PutPicture(sink, dst);
 error:
     picture_Release(dst);
     dst = NULL;
     goto skip;
 }
 
-static picture_t *VideoRender(filter_t *filter, picture_t *src)
+static int VideoRender(filter_t *filter, picture_t *src, struct vlc_video_sink *sink)
 {
-    return Render(filter, src, false);
+    return Render(filter, src, sink, false);
 }
 
 
-static picture_t *YCbCrRender(filter_t *filter, picture_t *src)
+static int YCbCrRender(filter_t *filter, picture_t *src, struct vlc_video_sink *sink)
 {
     src = VideoImport(filter, src);
-    return (src != NULL) ? Render(filter, src, true) : NULL;
+    if (src != NULL)
+        return Render(filter, src, sink, true);
+    return VLC_EBADVAR;
 }
 
 static int OutputCheckFormat(vlc_object_t *obj, vdpau_decoder_device_t *vdpau_dev,
@@ -766,7 +771,7 @@ static int OutputOpen(vlc_object_t *obj)
 
     filter->p_sys = sys;
 
-    picture_t *(*video_filter)(filter_t *, picture_t *) = VideoRender;
+    int (*video_filter)(filter_t *, picture_t *, struct vlc_video_sink *sink) = VideoRender;
 
     if (filter->fmt_in.video.i_chroma == VLC_CODEC_VDPAU_VIDEO_444)
     {
@@ -834,7 +839,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_video_filter_into = video_filter;
     filter->pf_flush = Flush;
     return VLC_SUCCESS;
 }
@@ -855,7 +860,7 @@ typedef struct
     VdpYCbCrFormat format;
 } vlc_vdp_yuv_getter_t;
 
-static picture_t *VideoExport_Filter(filter_t *filter, picture_t *src)
+static int VideoExport_Filter(filter_t *filter, picture_t *src, struct vlc_video_sink *sink)
 {
     vlc_vdp_yuv_getter_t *sys = filter->p_sys;
 
@@ -868,9 +873,9 @@ static picture_t *VideoExport_Filter(filter_t *filter, picture_t *src)
 
     picture_t *dst = filter_NewPicture(filter);
     if (dst == NULL)
-        return NULL;
+        return VLC_ENOMEM;
 
-    return VideoExport(filter, src, dst, sys->format);
+    return VideoExport(filter, src, dst, sink, sys->format);
 }
 
 static bool ChromaMatches(VdpChromaType vdp_type, vlc_fourcc_t vlc_chroma)
@@ -913,7 +918,7 @@ static int YCbCrOpen(vlc_object_t *obj)
         return VLC_ENOMEM;
     sys->format = format;
 
-    filter->pf_video_filter = VideoExport_Filter;
+    filter->pf_video_filter_into = VideoExport_Filter;
     filter->p_sys = sys;
     return VLC_SUCCESS;
 }
diff --git a/modules/hw/vdpau/sharpen.c b/modules/hw/vdpau/sharpen.c
index 4aee7e3c406..32977127f57 100644
--- a/modules/hw/vdpau/sharpen.c
+++ b/modules/hw/vdpau/sharpen.c
@@ -58,14 +58,14 @@ static int SharpenCallback(vlc_object_t *obj, const char *varname,
     return VLC_SUCCESS;
 }
 
-static picture_t *Sharpen(filter_t *filter, picture_t *pic)
+static int Sharpen(filter_t *filter, picture_t *pic, struct vlc_video_sink *sink)
 {
     filter_sys_t *sys = filter->p_sys;
     vlc_vdp_video_field_t *f = VDPAU_FIELD_FROM_PICCTX(pic->context);
     union { uint32_t u; float f; } u;
 
     if (unlikely(f == NULL))
-        return pic;
+        return vlc_video_sink_PutPicture(sink, pic);
 
     u.u = atomic_load(&sys->sigma);
     f->sharpen += u.f;
@@ -74,7 +74,7 @@ static picture_t *Sharpen(filter_t *filter, picture_t *pic)
     if (f->sharpen < -1.f)
         f->sharpen = -1.f;
 
-    return pic;
+    return vlc_video_sink_PutPicture(sink, pic);
 }
 
 static const char *const options[] = { "sigma", NULL };
@@ -125,7 +125,7 @@ static int Open(vlc_object_t *obj)
     if (unlikely(sys == NULL))
         return VLC_ENOMEM;
 
-    filter->pf_video_filter = Sharpen;
+    filter->pf_video_filter_into = Sharpen;
     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..ea064ecd53d 100644
--- a/modules/spu/audiobargraph_v.c
+++ b/modules/spu/audiobargraph_v.c
@@ -432,7 +432,7 @@ exit:
 /**
  * Video filter
  */
-static picture_t *FilterVideo(filter_t *p_filter, picture_t *p_src)
+static int FilterVideo(filter_t *p_filter, picture_t *p_src, struct vlc_video_sink *sink)
 {
     filter_sys_t *p_sys = p_filter->p_sys;
     BarGraph_t *p_BarGraph = &(p_sys->p_BarGraph);
@@ -440,7 +440,7 @@ static picture_t *FilterVideo(filter_t *p_filter, picture_t *p_src)
     picture_t *p_dst = filter_NewPicture(p_filter);
     if (!p_dst) {
         picture_Release(p_src);
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     picture_Copy(p_dst, p_src);
@@ -484,7 +484,7 @@ out:
     vlc_mutex_unlock(&p_sys->lock);
 
     picture_Release(p_src);
-    return p_dst;
+    return vlc_video_sink_PutPicture( sink, p_dst );
 }
 
 /**
@@ -560,7 +560,7 @@ static int OpenCommon(vlc_object_t *p_this, bool b_sub)
     if (b_sub)
         p_filter->pf_sub_source = FilterSub;
     else
-        p_filter->pf_video_filter = FilterVideo;
+        p_filter->pf_video_filter_into = FilterVideo;
 
     return VLC_SUCCESS;
 }
diff --git a/modules/spu/logo.c b/modules/spu/logo.c
index c8d1b02445b..fed371316c3 100644
--- a/modules/spu/logo.c
+++ b/modules/spu/logo.c
@@ -187,7 +187,7 @@ static const char *const ppsz_filter_callbacks[] = {
 static int OpenCommon( vlc_object_t *, bool b_sub );
 
 static subpicture_t *FilterSub( filter_t *, vlc_tick_t );
-static picture_t    *FilterVideo( filter_t *, picture_t * );
+static int FilterVideo( filter_t *, picture_t *, struct vlc_video_sink * );
 
 static int Mouse( filter_t *, vlc_mouse_t *, const vlc_mouse_t * );
 
@@ -298,7 +298,7 @@ static int OpenCommon( vlc_object_t *p_this, bool b_sub )
     }
     else
     {
-        p_filter->pf_video_filter = FilterVideo;
+        p_filter->pf_video_filter_into = FilterVideo;
         p_filter->pf_video_mouse = Mouse;
     }
 
@@ -429,7 +429,7 @@ exit:
 /**
  * Video filter
  */
-static picture_t *FilterVideo( filter_t *p_filter, picture_t *p_src )
+static int FilterVideo( filter_t *p_filter, picture_t *p_src, struct vlc_video_sink *sink )
 {
     filter_sys_t *p_sys = p_filter->p_sys;
     logo_list_t *p_list = &p_sys->list;
@@ -509,7 +509,9 @@ static picture_t *FilterVideo( filter_t *p_filter, picture_t *p_src )
 
 exit:
     picture_Release( p_src );
-    return p_dst;
+    if (p_dst == NULL)
+        return VLC_EGENERIC;
+    return vlc_video_sink_PutPicture( sink, p_dst );
 }
 
 static int Mouse( filter_t *p_filter, vlc_mouse_t *p_new,
diff --git a/modules/video_chroma/cvpx.c b/modules/video_chroma/cvpx.c
index f3ece816328..8735215b450 100644
--- a/modules/video_chroma/cvpx.c
+++ b/modules/video_chroma/cvpx.c
@@ -36,6 +36,7 @@
 #include <vlc_modules.h>
 #include "../codec/vt_utils.h"
 #include "../video_chroma/copy.h"
+#include "../video_filter/filter_picture.h"
 
 static int Open(vlc_object_t *);
 static void Close(vlc_object_t *);
@@ -158,7 +159,7 @@ static void Copy(filter_t *p_filter, picture_t *dst, picture_t *src,
 #undef DO_P
 }
 
-static picture_t *CVPX_TO_SW_Filter(filter_t *p_filter, picture_t *src)
+static int CVPX_TO_SW_Filter(filter_t *p_filter, picture_t *src, struct vlc_video_sink *sink)
 {
     filter_sys_t *p_sys = p_filter->p_sys;
 
@@ -168,7 +169,7 @@ static picture_t *CVPX_TO_SW_Filter(filter_t *p_filter, picture_t *src)
     if (!src_sw)
     {
         picture_Release(src);
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     picture_t *dst = filter_NewPicture(p_filter);
@@ -176,7 +177,7 @@ static picture_t *CVPX_TO_SW_Filter(filter_t *p_filter, picture_t *src)
     {
         picture_Release(src_sw);
         picture_Release(src);
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     size_t height = CVPixelBufferGetHeight(cvpx);
@@ -184,13 +185,10 @@ static picture_t *CVPX_TO_SW_Filter(filter_t *p_filter, picture_t *src)
 
     picture_Release(src_sw);
 
-    picture_CopyProperties(dst, src);
-    picture_Release(src);
-
-    return dst;
+    return CopyInfoAndPush(dst, src, sink);
 }
 
-static picture_t *SW_TO_CVPX_Filter(filter_t *p_filter, picture_t *src)
+static int SW_TO_CVPX_Filter(filter_t *p_filter, picture_t *src, struct vlc_video_sink *sink)
 {
     filter_sys_t *p_sys = p_filter->p_sys;
 
@@ -198,7 +196,7 @@ static picture_t *SW_TO_CVPX_Filter(filter_t *p_filter, picture_t *src)
     if (cvpx == NULL)
     {
         picture_Release(src);
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     /* Allocate a CPVX backed picture mapped for read/write */
@@ -208,7 +206,7 @@ static picture_t *SW_TO_CVPX_Filter(filter_t *p_filter, picture_t *src)
     if (!mapped_dst)
     {
         picture_Release(src);
-        return NULL;
+        return VLC_EGENERIC;
     }
 
     /* Allocate a CVPX picture without any context */
@@ -217,7 +215,7 @@ static picture_t *SW_TO_CVPX_Filter(filter_t *p_filter, picture_t *src)
     {
         picture_Release(src);
         picture_Release(mapped_dst);
-        return NULL;
+        return VLC_EGENERIC;
     }
 
     size_t height = CVPixelBufferGetHeight(cvpx);
@@ -229,9 +227,7 @@ static picture_t *SW_TO_CVPX_Filter(filter_t *p_filter, picture_t *src)
     /* Unlock and unmap the dst picture */
     picture_Release(mapped_dst);
 
-    picture_CopyProperties(dst, src);
-    picture_Release(src);
-    return dst;
+    return CopyInfoAndPush(dst, src, sink);
 }
 
 static void Close(vlc_object_t *obj)
@@ -261,9 +257,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->pf_video_filter_into = CVPX_TO_SW_Filter; \
         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->pf_video_filter_into = CVPX_TO_SW_Filter; \
             sw_fmt.i_chroma = VLC_CODEC_##x; \
         } else return VLC_EGENERIC; \
 
@@ -271,10 +267,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->pf_video_filter_into = SW_TO_CVPX_Filter; \
         } \
         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->pf_video_filter_into = SW_TO_CVPX_Filter; \
             sw_fmt.i_chroma = VLC_CODEC_##x; \
         } else return VLC_EGENERIC; \
         b_need_pool = true;
@@ -386,8 +382,8 @@ error:
 
 #if !TARGET_OS_IPHONE
 
-static picture_t *
-Filter(filter_t *filter, picture_t *src)
+static int
+Filter(filter_t *filter, picture_t *src, struct vlc_video_sink *sink)
 {
     filter_sys_t *p_sys = filter->p_sys;
 
@@ -398,7 +394,7 @@ Filter(filter_t *filter, picture_t *src)
     if (!dst)
     {
         picture_Release(src);
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     CVPixelBufferRef dst_cvpx = cvpxpool_new_cvpx(p_sys->pool);
@@ -406,7 +402,7 @@ Filter(filter_t *filter, picture_t *src)
     {
         picture_Release(src);
         picture_Release(dst);
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     if (VTPixelTransferSessionTransferImage(p_sys->vttransfer,
@@ -415,15 +411,13 @@ Filter(filter_t *filter, picture_t *src)
         picture_Release(dst);
         picture_Release(src);
         CVPixelBufferRelease(dst_cvpx);
-        return NULL;
+        return VLC_EGENERIC;
     }
 
     cvpxpic_attach(dst, dst_cvpx, filter->vctx_out, NULL);
 
-    picture_CopyProperties(dst, src);
-    picture_Release(src);
     CVPixelBufferRelease(dst_cvpx);
-    return dst;
+    return CopyInfoAndPush(dst, src, sink);
 }
 
 static vlc_fourcc_t const supported_chromas[] = { VLC_CODEC_CVPX_BGRA,
@@ -480,7 +474,7 @@ Open_CVPX_to_CVPX(vlc_object_t *obj)
     }
 
     filter->p_sys = p_sys;
-    filter->pf_video_filter = Filter;
+    filter->pf_video_filter_into = Filter;
     filter->vctx_out = vlc_video_context_Hold(filter->vctx_in);
     filter->fmt_out.i_codec = filter->fmt_out.video.i_chroma;
     return VLC_SUCCESS;
@@ -499,11 +493,11 @@ Close_CVPX_to_CVPX(vlc_object_t *obj)
     free(filter->p_sys);
 }
 
-static picture_t*
-chain_CVPX_Filter(filter_t *filter, picture_t *pic)
+static int
+chain_CVPX_Filter(filter_t *filter, picture_t *pic, struct vlc_video_sink *sink)
 {
     filter_chain_t *chain = filter->p_sys;
-    return filter_chain_VideoFilter(chain, pic);
+    return filter_chain_VideoFilterInto(chain, pic, sink);
 }
 
 static void
@@ -657,7 +651,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->pf_video_filter_into = chain_CVPX_Filter;
 
     /* Display the current conversion chain in the logs. */
     msg_Dbg(filter, "CVPX conversion chain:");
diff --git a/modules/video_chroma/rv32.c b/modules/video_chroma/rv32.c
index 9546872722b..c9b62190cd8 100644
--- a/modules/video_chroma/rv32.c
+++ b/modules/video_chroma/rv32.c
@@ -31,12 +31,13 @@
 #include <vlc_plugin.h>
 #include <vlc_filter.h>
 #include <vlc_picture.h>
+#include "../video_filter/filter_picture.h"
 
 /****************************************************************************
  * Local prototypes
  ****************************************************************************/
 static int  OpenFilter ( vlc_object_t * );
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 
 /*****************************************************************************
  * Module descriptor
@@ -67,7 +68,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->pf_video_filter_into = Filter;
 
     return VLC_SUCCESS;
 }
@@ -75,7 +76,7 @@ static int OpenFilter( vlc_object_t *p_this )
 /****************************************************************************
  * Filter: the whole thing
  ****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     picture_t *p_pic_dst;
     int i_plane, i;
@@ -86,7 +87,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
     if( !p_pic_dst )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     /* Convert RV24 to RV32 */
@@ -111,9 +112,6 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
         }
     }
 
-    picture_CopyProperties( p_pic_dst, p_pic );
-    picture_Release( p_pic );
-
-    return p_pic_dst;
+    return CopyInfoAndPush( p_pic_dst, p_pic, sink );
 }
 
diff --git a/modules/video_chroma/swscale.c b/modules/video_chroma/swscale.c
index 3c7bdf78ea3..d4492d57d2e 100644
--- a/modules/video_chroma/swscale.c
+++ b/modules/video_chroma/swscale.c
@@ -42,6 +42,7 @@
 # include <TargetConditionals.h>
 #endif
 
+#include "../video_filter/filter_picture.h"
 #include "../codec/avcodec/chroma.h" // Chroma Avutil <-> VLC conversion
 
 /* Gruikkkkkkkkkk!!!!! */
@@ -107,7 +108,7 @@ typedef struct
     bool b_swap_uvo;
 } filter_sys_t;
 
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 static int  Init( filter_t * );
 static void Clean( filter_t * );
 
@@ -192,7 +193,7 @@ static int OpenScaler( vlc_object_t *p_this )
     }
 
     /* */
-    p_filter->pf_video_filter = Filter;
+    p_filter->pf_video_filter_into = Filter;
 
     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,
@@ -625,7 +626,7 @@ static void Convert( filter_t *p_filter, struct SwsContext *ctx,
  ****************************************************************************
  * This function is called just after the thread is launched.
  ****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     filter_sys_t *p_sys = p_filter->p_sys;
     const video_format_t *p_fmti = &p_filter->fmt_in.video;
@@ -636,7 +637,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
     if( Init( p_filter ) )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_EBADVAR;
     }
 
     /* Request output picture */
@@ -644,7 +645,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
     if( !p_pic_dst )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     /* */
@@ -705,9 +706,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
         picture_CopyPixels( p_pic_dst, p_dst );
     }
 
-    picture_CopyProperties( p_pic_dst, p_pic );
-    picture_Release( p_pic );
-    return p_pic_dst;
+    return CopyInfoAndPush( p_pic_dst, p_pic, sink );
 }
 
 #else /* LIBSWSCALE_VERSION_INT >= ((0<<16)+(5<<8)+0) */
diff --git a/modules/video_filter/adjust.c b/modules/video_filter/adjust.c
index 063af77f22a..e01f55efeb6 100644
--- a/modules/video_filter/adjust.c
+++ b/modules/video_filter/adjust.c
@@ -47,8 +47,8 @@
 static int  Create    ( vlc_object_t * );
 static void Destroy   ( vlc_object_t * );
 
-static picture_t *FilterPlanar( filter_t *, picture_t * );
-static picture_t *FilterPacked( filter_t *, picture_t * );
+static int FilterPlanar( filter_t *, picture_t *, struct vlc_video_sink * );
+static int FilterPacked( filter_t *, picture_t *, struct vlc_video_sink * );
 
 /*****************************************************************************
  * Module descriptor
@@ -166,7 +166,7 @@ static int Create( vlc_object_t *p_this )
     {
         CASE_PLANAR_YUV
             /* Planar YUV */
-            p_filter->pf_video_filter = FilterPlanar;
+            p_filter->pf_video_filter_into = FilterPlanar;
             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 +174,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->pf_video_filter_into = FilterPlanar;
             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->pf_video_filter_into = FilterPacked;
             p_sys->pf_process_sat_hue_clip = packed_sat_hue_clip_C;
             p_sys->pf_process_sat_hue = packed_sat_hue_C;
             break;
@@ -243,7 +243,8 @@ static void Destroy( vlc_object_t *p_this )
 /*****************************************************************************
  * Run the filter on a Planar YUV picture
  *****************************************************************************/
-static picture_t *FilterPlanar( filter_t *p_filter, picture_t *p_pic )
+static int FilterPlanar( filter_t *p_filter, picture_t *p_pic,
+                         struct vlc_video_sink *sink )
 {
     /* The full range will only be used for 10-bit */
     int pi_luma[1024];
@@ -253,13 +254,13 @@ static picture_t *FilterPlanar( filter_t *p_filter, picture_t *p_pic )
 
     filter_sys_t *p_sys = p_filter->p_sys;
 
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_EBADVAR;
 
     p_outpic = filter_NewPicture( p_filter );
     if( !p_outpic )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     bool b_16bit;
@@ -434,13 +435,14 @@ static picture_t *FilterPlanar( filter_t *p_filter, picture_t *p_pic )
                                         i_x, i_y );
     }
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
 
 /*****************************************************************************
  * Run the filter on a Packed YUV picture
  *****************************************************************************/
-static picture_t *FilterPacked( filter_t *p_filter, picture_t *p_pic )
+static int FilterPacked( filter_t *p_filter, picture_t *p_pic,
+                         struct vlc_video_sink *sink )
 {
     int pi_luma[256];
     int pi_gamma[256];
@@ -459,7 +461,7 @@ static picture_t *FilterPacked( filter_t *p_filter, picture_t *p_pic )
 
     filter_sys_t *p_sys = p_filter->p_sys;
 
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_EBADVAR;
 
     i_pitch = p_pic->p->i_pitch;
     i_visible_pitch = p_pic->p->i_visible_pitch;
@@ -471,7 +473,7 @@ static picture_t *FilterPacked( filter_t *p_filter, picture_t *p_pic )
                   (char*)&(p_pic->format.i_chroma) );
 
         picture_Release( p_pic );
-        return NULL;
+        return VLC_EGENERIC;
     }
 
     p_outpic = filter_NewPicture( p_filter );
@@ -480,7 +482,7 @@ static picture_t *FilterPacked( filter_t *p_filter, picture_t *p_pic )
         msg_Warn( p_filter, "can't get output picture" );
 
         picture_Release( p_pic );
-        return NULL;
+        return VLC_EGENERIC;
     }
 
     /* Get variables */
@@ -586,7 +588,7 @@ static picture_t *FilterPacked( filter_t *p_filter, picture_t *p_pic )
             msg_Warn( p_filter, "Unsupported input chroma (%4.4s)",
                       (char*)&(p_pic->format.i_chroma) );
             picture_Release( p_pic );
-            return NULL;
+            return VLC_EGENERIC;
         }
     }
     else
@@ -599,9 +601,9 @@ static picture_t *FilterPacked( filter_t *p_filter, picture_t *p_pic )
             msg_Warn( p_filter, "Unsupported input chroma (%4.4s)",
                       (char*)&(p_pic->format.i_chroma) );
             picture_Release( p_pic );
-            return NULL;
+            return VLC_EGENERIC;
         }
     }
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
diff --git a/modules/video_filter/alphamask.c b/modules/video_filter/alphamask.c
index 001f37185c7..6ef18d17fc0 100644
--- a/modules/video_filter/alphamask.c
+++ b/modules/video_filter/alphamask.c
@@ -51,7 +51,7 @@
 static int  Create      ( vlc_object_t * );
 static void Destroy     ( vlc_object_t * );
 
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 static void LoadMask( filter_t *, const char * );
 static int MaskCallback( vlc_object_t *, char const *,
                          vlc_value_t, vlc_value_t, void * );
@@ -122,7 +122,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->pf_video_filter_into = Filter;
 
     return VLC_SUCCESS;
 }
@@ -141,7 +141,7 @@ static void Destroy( vlc_object_t *p_this )
     free( p_sys );
 }
 
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     filter_sys_t *p_sys = p_filter->p_sys;
 
@@ -166,7 +166,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
         plane_CopyPixels( p_apic, p_mask );
     }
     vlc_mutex_unlock( &p_sys->mask_lock );
-    return p_pic;
+    return vlc_video_sink_PutPicture( sink, p_pic );
 }
 
 /* copied from video_filters/erase.c . Gruik ? */
diff --git a/modules/video_filter/anaglyph.c b/modules/video_filter/anaglyph.c
index b17bf7a5256..e460a9e2f6d 100644
--- a/modules/video_filter/anaglyph.c
+++ b/modules/video_filter/anaglyph.c
@@ -32,7 +32,7 @@
 
 static int Create(vlc_object_t *);
 static void Destroy(vlc_object_t *);
-static picture_t *Filter(filter_t *, picture_t *);
+static int Filter(filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink);
 static void combine_side_by_side_yuv420(picture_t *, picture_t *, int, int);
 
 #define SCHEME_TEXT N_("Color scheme")
@@ -156,7 +156,7 @@ static int Create(vlc_object_t *p_this)
             break;
     }
 
-    p_filter->pf_video_filter = Filter;
+    p_filter->pf_video_filter_into = Filter;
     return VLC_SUCCESS;
 }
 
@@ -167,18 +167,18 @@ static void Destroy(vlc_object_t *p_this)
     free(p_sys);
 }
 
-static picture_t *Filter(filter_t *p_filter, picture_t *p_pic)
+static int Filter(filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink)
 {
     filter_sys_t *p_sys = p_filter->p_sys;
 
     if (!p_pic)
-        return NULL;
+        return VLC_EBADVAR;
 
     picture_t *p_outpic = filter_NewPicture(p_filter);
     if (!p_outpic)
     {
         picture_Release(p_pic);
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     switch (p_pic->format.i_chroma)
@@ -194,10 +194,10 @@ static picture_t *Filter(filter_t *p_filter, picture_t *p_pic)
             msg_Warn(p_filter, "Unsupported input chroma (%4.4s)",
                      (char*)&(p_pic->format.i_chroma));
             picture_Release(p_pic);
-            return NULL;
+            return VLC_EGENERIC;
     }
 
-    return CopyInfoAndRelease(p_outpic, p_pic);
+    return CopyInfoAndPush(p_outpic, p_pic, sink);
 }
 
 
diff --git a/modules/video_filter/antiflicker.c b/modules/video_filter/antiflicker.c
index 568a84ae615..107e2bdcfc4 100644
--- a/modules/video_filter/antiflicker.c
+++ b/modules/video_filter/antiflicker.c
@@ -40,7 +40,7 @@
  * Local prototypes
  *****************************************************************************/
 static int GetLuminanceAvg( picture_t * p_pic );
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 static int AntiFlickerCallback( vlc_object_t *p_this, char const *psz_var,
                            vlc_value_t oldval, vlc_value_t newval,
                            void *p_data );
@@ -121,7 +121,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->pf_video_filter_into = Filter;
 
     /* Initialize the arguments */
     atomic_init( &p_sys->i_window_size,
@@ -202,15 +202,15 @@ static int GetLuminanceAvg( picture_t *p_pic )
  * The function uses moving average of past frames to adjust the luminance
  * of current frame also applies temporaral smoothening if enabled.
  *****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_EBADVAR;
 
     picture_t *p_outpic = filter_NewPicture( p_filter );
     if( !p_outpic )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     /****************** Get variables *************************/
@@ -290,7 +290,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
 
     if (scene_changed || i_softening == 0)
     {
-       return CopyInfoAndRelease( p_outpic, p_pic );
+       return CopyInfoAndPush( p_outpic, p_pic, sink );
     }
 
     /******* Temporal softening phase. Adapted from code by Steven Don ******/
@@ -321,7 +321,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
         }
     }
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
 
 /*****************************************************************************
diff --git a/modules/video_filter/ball.c b/modules/video_filter/ball.c
index 563ff8bedc0..4885ca8080c 100644
--- a/modules/video_filter/ball.c
+++ b/modules/video_filter/ball.c
@@ -75,7 +75,7 @@ typedef struct filter_sys_t filter_sys_t;
 static int  Create    ( vlc_object_t * );
 static void Destroy   ( vlc_object_t * );
 
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 
 static void drawBall( filter_sys_t *p_sys, picture_t *p_outpic );
 static void drawPixelRGB24( filter_sys_t *p_sys, picture_t *p_outpic,
@@ -261,7 +261,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->pf_video_filter_into = Filter;
 
     config_ChainParse( p_filter, FILTER_PREFIX, ppsz_filter_options,
                        p_filter->p_cfg );
@@ -349,17 +349,17 @@ static void Destroy( vlc_object_t *p_this )
 * until it is displayed and switch the two rendering buffers, preparing next
 * frame.
  *****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     picture_t *p_outpic;
 
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_EBADVAR;
 
     p_outpic = filter_NewPicture( p_filter );
     if( !p_outpic )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_EGENERIC;
     }
 
     filter_sys_t *p_sys = p_filter->p_sys;
@@ -367,7 +367,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
     FilterBall( p_filter, p_pic, p_outpic );
     vlc_mutex_unlock( &p_sys->lock );
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
 
 
diff --git a/modules/video_filter/blendbench.c b/modules/video_filter/blendbench.c
index f2886385311..e2f35691666 100644
--- a/modules/video_filter/blendbench.c
+++ b/modules/video_filter/blendbench.c
@@ -43,7 +43,7 @@
 static int Create( vlc_object_t * );
 static void Destroy( vlc_object_t * );
 
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 
 /*****************************************************************************
  * Module descriptor
@@ -162,7 +162,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->pf_video_filter_into = Filter;
 
     /* needed to get options passed in transcode using the
      * adjust{name=value} syntax */
@@ -225,19 +225,19 @@ static void Destroy( vlc_object_t *p_this )
 /*****************************************************************************
  * Render: displays previously rendered output
  *****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     filter_sys_t *p_sys = p_filter->p_sys;
     filter_t *p_blend;
 
     if( p_sys->b_done )
-        return p_pic;
+        return vlc_video_sink_PutPicture(sink, p_pic);
 
     p_blend = vlc_object_create( p_filter, sizeof(filter_t) );
     if( !p_blend )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOOBJ;
     }
     p_blend->fmt_out.video = p_sys->p_base_image->format;
     p_blend->fmt_in.video = p_sys->p_blend_image->format;
@@ -246,7 +246,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
     {
         picture_Release( p_pic );
         vlc_object_delete(p_blend);
-        return NULL;
+        return VLC_ENOMOD;
     }
 
     vlc_tick_t time = vlc_tick_now();
@@ -271,5 +271,5 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
     vlc_object_delete(p_blend);
 
     p_sys->b_done = true;
-    return p_pic;
+    return vlc_video_sink_PutPicture(sink, p_pic);
 }
diff --git a/modules/video_filter/bluescreen.c b/modules/video_filter/bluescreen.c
index 3fe83a1a5df..15a40338295 100644
--- a/modules/video_filter/bluescreen.c
+++ b/modules/video_filter/bluescreen.c
@@ -66,7 +66,7 @@
 static int  Create      ( vlc_object_t * );
 static void Destroy     ( vlc_object_t * );
 
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 static int BluescreenCallback( vlc_object_t *, char const *,
                                vlc_value_t, vlc_value_t, void * );
 
@@ -143,7 +143,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->pf_video_filter_into = Filter;
 
     return VLC_SUCCESS;
 }
@@ -162,7 +162,7 @@ static void Destroy( vlc_object_t *p_this )
     free( p_sys );
 }
 
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     filter_sys_t *p_sys = p_filter->p_sys;
 
@@ -174,13 +174,13 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
     uint8_t *p_v = p_pic->p[ V_PLANE ].p_pixels;
     uint8_t umin, umax, vmin, vmax;
 
-    if( p_pic->format.i_chroma != VLC_CODEC_YUVA )
+    if( unlikely(p_pic->format.i_chroma != VLC_CODEC_YUVA) )
     {
         msg_Err( p_filter,
                  "Unsupported input chroma \"%4.4s\". "
                  "Bluescreen can only use \"YUVA\".",
                  (char*)&p_pic->format.i_chroma );
-        return NULL;
+        return VLC_EGENERIC;
     }
 
     p_sys->p_at = xrealloc( p_sys->p_at,
@@ -249,7 +249,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
               if( p_a[i*i_pitch+j] < 0xbf ) p_a[i*i_pitch+j] = 0x00;
         }
     }
-    return p_pic;
+    return vlc_video_sink_PutPicture( sink, p_pic );
 }
 
 /*****************************************************************************
diff --git a/modules/video_filter/canvas.c b/modules/video_filter/canvas.c
index 80010eecc17..1fa135a86f7 100644
--- a/modules/video_filter/canvas.c
+++ b/modules/video_filter/canvas.c
@@ -40,7 +40,7 @@
  *****************************************************************************/
 static int  Activate( vlc_object_t * );
 static void Destroy( vlc_object_t * );
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink *sink );
 
 /* This module effectively implements a form of picture-in-picture.
  *  - The outer picture is called the canvas.
@@ -373,7 +373,7 @@ static int Activate( vlc_object_t *p_this )
                   i_canvas_width, i_canvas_height );
     }
 
-    p_filter->pf_video_filter = Filter;
+    p_filter->pf_video_filter_into = Filter;
 
     return VLC_SUCCESS;
 }
@@ -392,8 +392,8 @@ static void Destroy( vlc_object_t *p_this )
 /*****************************************************************************
  *
  *****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     filter_sys_t *p_sys = p_filter->p_sys;
-    return filter_chain_VideoFilter( p_sys->p_chain, p_pic );
+    return filter_chain_VideoFilterInto( p_sys->p_chain, p_pic, sink );
 }
diff --git a/modules/video_filter/colorthres.c b/modules/video_filter/colorthres.c
index 3011b94f99d..c884e65d08d 100644
--- a/modules/video_filter/colorthres.c
+++ b/modules/video_filter/colorthres.c
@@ -45,8 +45,8 @@
 static int  Create    ( vlc_object_t * );
 static void Destroy   ( vlc_object_t * );
 
-static picture_t *Filter( filter_t *, picture_t * );
-static picture_t *FilterPacked( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
+static int FilterPacked( filter_t *, picture_t *, struct vlc_video_sink * );
 
 /*****************************************************************************
  * Module descriptor
@@ -115,11 +115,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->pf_video_filter_into = Filter;
             break;
 
         CASE_PACKED_YUV_422
-            p_filter->pf_video_filter = FilterPacked;
+            p_filter->pf_video_filter_into = FilterPacked;
             break;
 
         default:
@@ -204,7 +204,7 @@ static bool IsSimilar( int u, int v,
  * waits until it is displayed and switch the two rendering buffers, preparing
  * next frame.
  *****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     picture_t *p_outpic;
     filter_sys_t *p_sys = p_filter->p_sys;
@@ -212,13 +212,13 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
     int i_satthres = atomic_load( &p_sys->i_satthres );
     int i_color = atomic_load( &p_sys->i_color );
 
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_ENOMEM;
 
     p_outpic = filter_NewPicture( p_filter );
     if( !p_outpic )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     /* Copy the Y plane */
@@ -257,10 +257,10 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
         }
     }
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
 
-static picture_t *FilterPacked( filter_t *p_filter, picture_t *p_pic )
+static int FilterPacked( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     picture_t *p_outpic;
     filter_sys_t *p_sys = p_filter->p_sys;
@@ -268,13 +268,13 @@ static picture_t *FilterPacked( filter_t *p_filter, picture_t *p_pic )
     int i_satthres = atomic_load( &p_sys->i_satthres );
     int i_color = atomic_load( &p_sys->i_color );
 
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_EBADVAR;
 
     p_outpic = filter_NewPicture( p_filter );
     if( !p_outpic )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     int i_y_offset, i_u_offset, i_v_offset;
@@ -283,7 +283,7 @@ static picture_t *FilterPacked( filter_t *p_filter, picture_t *p_pic )
     if( i_ret == VLC_EGENERIC )
     {
         picture_Release( p_pic );
-        return NULL;
+        return i_ret;
     }
 
     /*
@@ -320,7 +320,7 @@ static picture_t *FilterPacked( filter_t *p_filter, picture_t *p_pic )
         }
     }
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
 
 static int FilterCallback ( vlc_object_t *p_this, char const *psz_var,
diff --git a/modules/video_filter/croppadd.c b/modules/video_filter/croppadd.c
index 1d2f27fbe45..161abb06678 100644
--- a/modules/video_filter/croppadd.c
+++ b/modules/video_filter/croppadd.c
@@ -41,7 +41,7 @@
 static int  OpenFilter ( vlc_object_t * );
 static void CloseFilter( vlc_object_t * );
 
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 
 #define CROPTOP_TEXT N_( "Pixels to crop from top" )
 #define CROPTOP_LONGTEXT N_( \
@@ -185,7 +185,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->pf_video_filter_into = Filter;
 
     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,
@@ -214,7 +214,7 @@ static void CloseFilter( vlc_object_t *p_this )
 /****************************************************************************
  * Filter: the whole thing
  ****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     filter_sys_t *p_sys = p_filter->p_sys;
     picture_t *p_outpic;
@@ -223,14 +223,14 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
 
     const int p_padd_color[] = { 0x00, 0x80, 0x80, 0xff };
 
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_EBADVAR;
 
     /* Request output picture */
     p_outpic = filter_NewPicture( p_filter );
     if( !p_outpic )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     for( int i_plane = 0; i_plane < p_pic->i_planes; i_plane++ )
@@ -308,5 +308,5 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
                  ( i_outheight - i_ypadd - i_height ) * p_outplane->i_pitch );
     }
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
diff --git a/modules/video_filter/deinterlace/deinterlace.c b/modules/video_filter/deinterlace/deinterlace.c
index a26591e4490..885c9cb0c3a 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 (pf_video_filter_into)
  * in the filter structure.
  *
  * Note that there is no guarantee that the returned picture directly
diff --git a/modules/video_filter/edgedetection.c b/modules/video_filter/edgedetection.c
index 935189122f1..436cead6f0a 100644
--- a/modules/video_filter/edgedetection.c
+++ b/modules/video_filter/edgedetection.c
@@ -48,7 +48,7 @@
 static int Open( vlc_object_t * );
 static void Close( vlc_object_t * );
 static picture_t *new_frame( filter_t * );
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 static uint8_t sobel( const uint8_t *, const int, const int, int, int);
 
 /* Kernel for X axis */
@@ -123,7 +123,7 @@ static int Open( vlc_object_t *p_this )
         return VLC_EGENERIC;
     }
     /* Set callback function */
-    p_filter->pf_video_filter = Filter;
+    p_filter->pf_video_filter_into = Filter;
     p_filter->p_sys = sys;
     return VLC_SUCCESS;
 }
@@ -154,7 +154,7 @@ static picture_t *new_frame( filter_t *p_filter )
  * edges in a black-and-white image. The sobel operator is applied to
  * every pixel in the frame for both X and Y axis.
  ******************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     picture_t *p_filtered_frame =
         filter_chain_VideoFilter( (filter_chain_t *)p_filter->p_sys, p_pic );
@@ -163,7 +163,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
     {
         picture_Release( p_filtered_frame );
         msg_Err( p_filter, "Could not allocate memory for new frame" );
-        return NULL;
+        return VLC_ENOMEM;
     }
     const int i_lines = p_filtered_frame->p[Y_PLANE].i_visible_lines;
     const int i_pitch = p_filtered_frame->p[Y_PLANE].i_pitch;
@@ -179,7 +179,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
         }
     }
     picture_Release( p_filtered_frame );
-    return p_out_frame;
+    return vlc_video_sink_PutPicture(sink, p_out_frame);
 }
 
 /******************************************************************************
diff --git a/modules/video_filter/erase.c b/modules/video_filter/erase.c
index 7e6683b14e4..f585c1b164a 100644
--- a/modules/video_filter/erase.c
+++ b/modules/video_filter/erase.c
@@ -43,7 +43,7 @@
 static int  Create    ( vlc_object_t * );
 static void Destroy   ( vlc_object_t * );
 
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 static void FilterErase( filter_t *, picture_t *, picture_t * );
 static int EraseCallback( vlc_object_t *, char const *,
                           vlc_value_t, vlc_value_t, void * );
@@ -153,7 +153,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->pf_video_filter_into = Filter;
 
     config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
                        p_filter->p_cfg );
@@ -203,18 +203,18 @@ static void Destroy( vlc_object_t *p_this )
 /*****************************************************************************
  * Filter
  *****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     picture_t *p_outpic;
     filter_sys_t *p_sys = p_filter->p_sys;
 
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_EBADVAR;
 
     p_outpic = filter_NewPicture( p_filter );
     if( !p_outpic )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     /* If the mask is empty: just copy the image */
@@ -225,7 +225,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
         picture_CopyPixels( p_outpic, p_pic );
     vlc_mutex_unlock( &p_sys->lock );
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
 
 /*****************************************************************************
diff --git a/modules/video_filter/extract.c b/modules/video_filter/extract.c
index 0e849642a95..873982f410e 100644
--- a/modules/video_filter/extract.c
+++ b/modules/video_filter/extract.c
@@ -42,7 +42,7 @@
 static int  Create      ( vlc_object_t * );
 static void Destroy     ( vlc_object_t * );
 
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 static int ExtractCallback( vlc_object_t *, char const *,
                             vlc_value_t, vlc_value_t, void * );
 
@@ -142,7 +142,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->pf_video_filter_into = Filter;
 
     return VLC_SUCCESS;
 }
@@ -164,18 +164,18 @@ static void Destroy( vlc_object_t *p_this )
 /*****************************************************************************
  * Render
  *****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     picture_t *p_outpic;
     filter_sys_t *p_sys = p_filter->p_sys;
 
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_EBADVAR;
 
     p_outpic = filter_NewPicture( p_filter );
     if( !p_outpic )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     vlc_mutex_lock( &p_sys->lock );
@@ -200,11 +200,11 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
         msg_Warn( p_filter, "Unsupported input chroma (%4.4s)",
                   (char*)&(p_pic->format.i_chroma) );
         picture_Release( p_pic );
-        return NULL;
+        return VLC_EGENERIC;
     }
     vlc_mutex_unlock( &p_sys->lock );
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
 
 #define U8 128
diff --git a/modules/video_filter/freeze.c b/modules/video_filter/freeze.c
index be5fde8e9d9..6d36734bef5 100644
--- a/modules/video_filter/freeze.c
+++ b/modules/video_filter/freeze.c
@@ -57,7 +57,7 @@ typedef struct
  * Prototypes
  *****************************************************************************/
 
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 
 static int  freeze_mouse( filter_t *, vlc_mouse_t *,
                           const vlc_mouse_t * );
@@ -121,7 +121,7 @@ static int Open( vlc_object_t *p_this )
 
     /* init data */
 
-    p_filter->pf_video_filter = Filter;
+    p_filter->pf_video_filter_into = Filter;
     p_filter->pf_video_mouse  = freeze_mouse;
 
     return VLC_SUCCESS;
@@ -142,15 +142,16 @@ static void Close( vlc_object_t *p_this ) {
 /**
  * Filter a picture
  */
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
-    if( !p_pic_in || !p_filter) return NULL;
+static int Filter( filter_t *p_filter, picture_t *p_pic_in, struct vlc_video_sink *sink )
+{
+    if( !p_pic_in || !p_filter) return VLC_EBADVAR;
 
     filter_sys_t *p_sys = p_filter->p_sys;
 
     picture_t *p_pic_out = filter_NewPicture( p_filter );
     if( unlikely(!p_pic_out) ) {
         picture_Release( p_pic_in );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
    /*
@@ -160,7 +161,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
         if (freeze_allocate_data( p_filter, p_pic_in ) != VLC_SUCCESS)
         {
             picture_Release( p_pic_in );
-            return NULL;
+            return VLC_EGENERIC;
         }
     p_sys->b_init = true;
 
@@ -216,7 +217,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
                         = p_sys->pi_freezed_picture[i_p][i_r][i_c];
             }
 
-    return CopyInfoAndRelease( p_pic_out, p_pic_in );
+    return CopyInfoAndPush( p_pic_out, p_pic_in, sink );
 }
 
 /*
diff --git a/modules/video_filter/gaussianblur.c b/modules/video_filter/gaussianblur.c
index 3a97dbcd34e..731c02a01ff 100644
--- a/modules/video_filter/gaussianblur.c
+++ b/modules/video_filter/gaussianblur.c
@@ -72,7 +72,7 @@ vlc_module_end ()
 /*****************************************************************************
  * Local prototypes
  *****************************************************************************/
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 
 static const char *const ppsz_filter_options[] = {
     "sigma", NULL
@@ -155,7 +155,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->pf_video_filter_into = Filter;
 
     p_sys->f_sigma =
         var_CreateGetFloat( p_filter, FILTER_PREFIX "sigma" );
@@ -186,7 +186,7 @@ static void Destroy( vlc_object_t *p_this )
     free( p_sys );
 }
 
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     picture_t *p_outpic;
     filter_sys_t *p_sys = p_filter->p_sys;
@@ -195,13 +195,13 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
     type_t *pt_scale;
     const type_t *pt_distribution = p_sys->pt_distribution;
 
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_EBADVAR;
 
     p_outpic = filter_NewPicture( p_filter );
     if( !p_outpic )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
     if( !p_sys->pt_buffer )
     {
@@ -293,5 +293,5 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
         }
     }
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
diff --git a/modules/video_filter/gradfun.c b/modules/video_filter/gradfun.c
index 093d207523e..cab9dfef77c 100644
--- a/modules/video_filter/gradfun.c
+++ b/modules/video_filter/gradfun.c
@@ -35,6 +35,7 @@
 #include <vlc_cpu.h>
 #include <vlc_filter.h>
 #include <vlc_picture.h>
+#include "filter_picture.h"
 
 /*****************************************************************************
  * Module descriptor
@@ -98,7 +99,7 @@ vlc_module_end()
 #include <stdalign.h>
 #include "gradfun.h"
 
-static picture_t *Filter(filter_t *, picture_t *);
+static int Filter(filter_t *, picture_t *, struct vlc_video_sink *);
 static int Callback(vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void *);
 
 typedef struct
@@ -157,7 +158,7 @@ static int Open(vlc_object_t *object)
         cfg->filter_line = filter_line_c;
 
     filter->p_sys           = sys;
-    filter->pf_video_filter = Filter;
+    filter->pf_video_filter_into = Filter;
     return VLC_SUCCESS;
 }
 
@@ -172,14 +173,14 @@ static void Close(vlc_object_t *object)
     free(sys);
 }
 
-static picture_t *Filter(filter_t *filter, picture_t *src)
+static int Filter(filter_t *filter, picture_t *src, struct vlc_video_sink *sink)
 {
     filter_sys_t *sys = filter->p_sys;
 
     picture_t *dst = filter_NewPicture(filter);
     if (!dst) {
         picture_Release(src);
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     vlc_mutex_lock(&sys->lock);
@@ -215,9 +216,7 @@ static picture_t *Filter(filter_t *filter, picture_t *src)
         }
     }
 
-    picture_CopyProperties(dst, src);
-    picture_Release(src);
-    return dst;
+    return CopyInfoAndPush(dst, src, sink);
 }
 
 static int Callback(vlc_object_t *object, char const *cmd,
diff --git a/modules/video_filter/gradient.c b/modules/video_filter/gradient.c
index 22675305f5e..b08b4022188 100644
--- a/modules/video_filter/gradient.c
+++ b/modules/video_filter/gradient.c
@@ -46,7 +46,7 @@ enum { GRADIENT, EDGE, HOUGH };
 static int  Create    ( vlc_object_t * );
 static void Destroy   ( vlc_object_t * );
 
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 static int GradientCallback( vlc_object_t *, char const *,
                              vlc_value_t, vlc_value_t,
                              void * );
@@ -151,7 +151,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->pf_video_filter_into = Filter;
 
     p_sys->p_pre_hough = NULL;
 
@@ -239,17 +239,17 @@ static void Destroy( vlc_object_t *p_this )
  * until it is displayed and switch the two rendering buffers, preparing next
  * frame.
  *****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     picture_t *p_outpic;
 
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_EBADVAR;
 
     p_outpic = filter_NewPicture( p_filter );
     if( !p_outpic )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     filter_sys_t *p_sys = p_filter->p_sys;
@@ -274,7 +274,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
     }
     vlc_mutex_unlock( &p_sys->lock );
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
 
 /*****************************************************************************
diff --git a/modules/video_filter/grain.c b/modules/video_filter/grain.c
index 174de0a822a..ef4d4a4c054 100644
--- a/modules/video_filter/grain.c
+++ b/modules/video_filter/grain.c
@@ -36,6 +36,7 @@
 #include <vlc_cpu.h>
 #include <vlc_picture.h>
 #include <vlc_rand.h>
+#include "filter_picture.h"
 
 /*****************************************************************************
  * Module descriptor
@@ -249,14 +250,14 @@ static void PlaneFilter(filter_t *filter,
         sys->emms();
 }
 
-static picture_t *Filter(filter_t *filter, picture_t *src)
+static int Filter(filter_t *filter, picture_t *src, struct vlc_video_sink *sink)
 {
     filter_sys_t *sys = filter->p_sys;
 
     picture_t *dst = filter_NewPicture(filter);
     if (!dst) {
         picture_Release(src);
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     vlc_mutex_lock(&sys->cfg.lock);
@@ -284,9 +285,7 @@ static picture_t *Filter(filter_t *filter, picture_t *src)
         }
     }
 
-    picture_CopyProperties(dst, src);
-    picture_Release(src);
-    return dst;
+    return CopyInfoAndPush(dst, src, sink);
 }
 
 /**
@@ -420,7 +419,7 @@ static int Open(vlc_object_t *object)
     var_AddCallback(filter, CFG_PREFIX "variance", Callback, NULL);
 
     filter->p_sys           = sys;
-    filter->pf_video_filter = Filter;
+    filter->pf_video_filter_into = Filter;
     return VLC_SUCCESS;
 }
 
diff --git a/modules/video_filter/hqdn3d.c b/modules/video_filter/hqdn3d.c
index beb57974d8e..912e105b438 100644
--- a/modules/video_filter/hqdn3d.c
+++ b/modules/video_filter/hqdn3d.c
@@ -42,7 +42,7 @@
  *****************************************************************************/
 static int  Open         (vlc_object_t *);
 static void Close        (vlc_object_t *);
-static picture_t *Filter (filter_t *, picture_t *);
+static int Filter(filter_t *, picture_t *, struct vlc_video_sink *);
 static int DenoiseCallback( vlc_object_t *p_this, char const *psz_var,
                             vlc_value_t oldval, vlc_value_t newval,
                             void *p_data );
@@ -155,7 +155,7 @@ static int Open(vlc_object_t *this)
     sys->chroma_temp = var_CreateGetFloatCommand(filter, FILTER_PREFIX "chroma-temp");
 
     filter->p_sys = sys;
-    filter->pf_video_filter = Filter;
+    filter->pf_video_filter_into = Filter;
 
     var_AddCallback( filter, FILTER_PREFIX "luma-spat", DenoiseCallback, sys );
     var_AddCallback( filter, FILTER_PREFIX "chroma-spat", DenoiseCallback, sys );
@@ -189,19 +189,19 @@ static void Close(vlc_object_t *this)
 /*****************************************************************************
  * Filter
  *****************************************************************************/
-static picture_t *Filter(filter_t *filter, picture_t *src)
+static int Filter(filter_t *filter, picture_t *src, struct vlc_video_sink *sink)
 {
     picture_t *dst;
     filter_sys_t *sys = filter->p_sys;
     struct vf_priv_s *cfg = &sys->cfg;
     bool recalc = false;
 
-    if (!src) return NULL;
+    if (!src) return VLC_EBADVAR;
 
     dst = filter_NewPicture(filter);
     if ( unlikely(!dst) ) {
         picture_Release(src);
-        return NULL;
+        return VLC_ENOMEM;
     }
     vlc_mutex_lock( &sys->coefs_mutex );
     recalc = sys->b_recalc_coefs;
@@ -241,10 +241,10 @@ static picture_t *Filter(filter_t *filter, picture_t *src)
     {
         picture_Release( src );
         picture_Release( dst );
-        return NULL;
+        return VLC_EGENERIC;
     }
 
-    return CopyInfoAndRelease(dst, src);
+    return CopyInfoAndPush(dst, src, sink);
 }
 
 
diff --git a/modules/video_filter/invert.c b/modules/video_filter/invert.c
index 67f96977c78..7be53820e61 100644
--- a/modules/video_filter/invert.c
+++ b/modules/video_filter/invert.c
@@ -40,7 +40,7 @@
 static int  Create      ( vlc_object_t * );
 static void Destroy     ( vlc_object_t * );
 
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 
 /*****************************************************************************
  * Module descriptor
@@ -76,7 +76,7 @@ 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;
+    p_filter->pf_video_filter_into = Filter;
     return VLC_SUCCESS;
 }
 
@@ -97,19 +97,19 @@ static void Destroy( vlc_object_t *p_this )
  * until it is displayed and switch the two rendering buffers, preparing next
  * frame.
  *****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     picture_t *p_outpic;
     int i_planes;
 
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_EBADVAR;
 
     p_outpic = filter_NewPicture( p_filter );
     if( !p_outpic )
     {
         msg_Warn( p_filter, "can't get output picture" );
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     if( p_pic->format.i_chroma == VLC_CODEC_YUVA )
@@ -169,5 +169,5 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
         }
     }
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
diff --git a/modules/video_filter/magnify.c b/modules/video_filter/magnify.c
index e5582bbc0ff..e7c4ba3cd55 100644
--- a/modules/video_filter/magnify.c
+++ b/modules/video_filter/magnify.c
@@ -58,7 +58,7 @@ vlc_module_end ()
 /*****************************************************************************
  * Local prototypes
  *****************************************************************************/
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 static int Mouse( filter_t *, vlc_mouse_t *, const vlc_mouse_t * );
 
 /* */
@@ -129,7 +129,7 @@ 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_filter_into = Filter;
     p_filter->pf_video_mouse = Mouse;
     return VLC_SUCCESS;
 }
@@ -150,7 +150,7 @@ static void Destroy( vlc_object_t *p_this )
 /*****************************************************************************
  * Render: displays previously rendered output
  *****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     filter_sys_t *p_sys = p_filter->p_sys;
     picture_t *p_outpic;
@@ -163,7 +163,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
     if( !p_outpic )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     /* */
@@ -268,7 +268,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
         }
     }
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
 
 static void DrawZoomStatus( uint8_t *pb_dst, int i_pitch, int i_width, int i_height,
diff --git a/modules/video_filter/mirror.c b/modules/video_filter/mirror.c
index ab8f9dcdd04..07f6872571d 100644
--- a/modules/video_filter/mirror.c
+++ b/modules/video_filter/mirror.c
@@ -43,7 +43,7 @@
 static int  Create      ( vlc_object_t * );
 static void Destroy     ( vlc_object_t * );
 
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 static void VerticalMirror( picture_t *, picture_t *, int plane, bool );
 static void HorizontalMirror( picture_t *, picture_t *, int, bool );
 static void PlanarVerticalMirror( picture_t *, picture_t *, int plane, bool );
@@ -155,7 +155,7 @@ 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;
+    p_filter->pf_video_filter_into = Filter;
 
     return VLC_SUCCESS;
 }
@@ -182,12 +182,12 @@ static void Destroy( vlc_object_t *p_this )
  * until it is displayed and switch the two rendering buffers, preparing next
  * frame.
  *****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     picture_t *p_outpic;
     bool b_vertical_split, b_left_to_right;
 
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_EBADVAR;
 
     filter_sys_t *p_sys = p_filter->p_sys;
     b_vertical_split = !atomic_load( &p_sys->i_split );
@@ -198,7 +198,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
     {
         msg_Warn( p_filter, "can't get output picture" );
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     for( int i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
@@ -209,7 +209,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
             HorizontalMirror( p_pic, p_outpic, i_index, b_left_to_right );
     }
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
 
 /*****************************************************************************
diff --git a/modules/video_filter/motionblur.c b/modules/video_filter/motionblur.c
index 4bd504598c8..cd184441c74 100644
--- a/modules/video_filter/motionblur.c
+++ b/modules/video_filter/motionblur.c
@@ -45,7 +45,7 @@ typedef struct filter_sys_t filter_sys_t;
 
 static int  Create       ( vlc_object_t * );
 static void Destroy      ( vlc_object_t * );
-static picture_t *Filter ( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 static void RenderBlur   ( filter_sys_t *, picture_t *, picture_t * );
 static int MotionBlurCallback( vlc_object_t *, char const *,
                                vlc_value_t, vlc_value_t, void * );
@@ -113,7 +113,7 @@ static int Create( vlc_object_t *p_this )
     }
     p_sys->b_first = true;
 
-    p_filter->pf_video_filter = Filter;
+    p_filter->pf_video_filter_into = Filter;
 
     config_ChainParse( p_filter, FILTER_PREFIX, ppsz_filter_options,
                        p_filter->p_cfg );
@@ -145,18 +145,18 @@ static void Destroy( vlc_object_t *p_this )
 /*****************************************************************************
  * Filter
  *****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     picture_t * p_outpic;
     filter_sys_t *p_sys = p_filter->p_sys;
 
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_EBADVAR;
 
     p_outpic = filter_NewPicture( p_filter );
     if( !p_outpic )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     if( p_sys->b_first )
@@ -170,7 +170,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
 
     picture_CopyPixels( p_sys->p_tmp, p_outpic );
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
 
 /*****************************************************************************
diff --git a/modules/video_filter/motiondetect.c b/modules/video_filter/motiondetect.c
index 9906132a616..ffb50082460 100644
--- a/modules/video_filter/motiondetect.c
+++ b/modules/video_filter/motiondetect.c
@@ -58,7 +58,7 @@ vlc_module_end ()
 /*****************************************************************************
  * Local prototypes
  *****************************************************************************/
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 static void GaussianConvolution( uint32_t *, uint32_t *, int, int, int );
 static int FindShapes( uint32_t *, uint32_t *, int, int, int,
                        int *, int *, int *, int *, int *);
@@ -106,7 +106,7 @@ static int Create( vlc_object_t *p_this )
                      (char*)&(p_fmt->i_chroma) );
             return VLC_EGENERIC;
     }
-    p_filter->pf_video_filter = Filter;
+    p_filter->pf_video_filter_into = Filter;
 
     /* Allocate structure */
     p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
@@ -253,18 +253,19 @@ static int PreparePacked( filter_t *p_filter, picture_t *p_inpic, int *pi_pix_of
     return VLC_SUCCESS;
 }
 
-static picture_t *Filter( filter_t *p_filter, picture_t *p_inpic )
+static int Filter( filter_t *p_filter, picture_t *p_inpic,
+                   struct vlc_video_sink * sink )
 {
     filter_sys_t *p_sys = p_filter->p_sys;
 
     if( !p_inpic )
-        return NULL;
+        return VLC_EBADVAR;
 
     picture_t *p_outpic = filter_NewPicture( p_filter );
     if( !p_outpic )
     {
         picture_Release( p_inpic );
-        return NULL;
+        return VLC_ENOMEM;
     }
     picture_Copy( p_outpic, p_inpic );
 
@@ -308,7 +309,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_inpic )
 
 exit:
     picture_Release( p_inpic );
-    return p_outpic;
+    return vlc_video_sink_PutPicture( sink, p_outpic );
 }
 
 
diff --git a/modules/video_filter/oldmovie.c b/modules/video_filter/oldmovie.c
index f7c6a710fc0..de5c782e585 100644
--- a/modules/video_filter/oldmovie.c
+++ b/modules/video_filter/oldmovie.c
@@ -149,7 +149,7 @@ typedef struct
  * Prototypes
  *****************************************************************************/
 
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 
 static int  oldmovie_allocate_data( filter_t *, picture_t * );
 static void oldmovie_free_allocated_data( filter_t * );
@@ -216,7 +216,7 @@ static int Open( vlc_object_t *p_this ) {
         return VLC_ENOMEM;
 
     /* init data */
-    p_filter->pf_video_filter = Filter;
+    p_filter->pf_video_filter_into = Filter;
     p_sys->i_start_time = p_sys->i_cur_time = p_sys->i_last_time = vlc_tick_now();
 
     return VLC_SUCCESS;
@@ -237,16 +237,16 @@ static void Close( vlc_object_t *p_this ) {
 /**
  * Filter a picture
  */
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
+static int Filter( filter_t *p_filter, picture_t *p_pic_in, struct vlc_video_sink *sink ) {
     if( unlikely( !p_pic_in || !p_filter ) )
-        return NULL;
+        return VLC_EBADVAR;
 
     filter_sys_t *p_sys = p_filter->p_sys;
 
     picture_t *p_pic_out = filter_NewPicture( p_filter );
     if( unlikely( !p_pic_out ) ) {
         picture_Release( p_pic_in );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
    /*
@@ -261,7 +261,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
     if ( unlikely( !p_sys->b_init ) )
         if ( unlikely( oldmovie_allocate_data( p_filter, p_pic_in ) != VLC_SUCCESS ) ) {
             picture_Release( p_pic_in );
-            return NULL;
+            return VLC_EGENERIC;
         }
     p_sys->b_init = true;
 
@@ -279,24 +279,24 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
     oldmovie_shutter_effect(p_filter, p_pic_out);
 
     if ( unlikely( oldmovie_sliding_offset_effect( p_filter, p_pic_out ) != VLC_SUCCESS ) )
-        return CopyInfoAndRelease( p_pic_out, p_pic_in );
+        return CopyInfoAndPush( p_pic_out, p_pic_in, sink );
 
     oldmovie_dark_border_effect( p_filter, p_pic_out );
 
     if ( unlikely( oldmovie_film_scratch_effect(p_filter, p_pic_out) != VLC_SUCCESS ) )
-        return CopyInfoAndRelease( p_pic_out, p_pic_in );
+        return CopyInfoAndPush( p_pic_out, p_pic_in, sink );
 
     oldmovie_film_blotch_effect(p_filter, p_pic_out);
 
     if ( unlikely( oldmovie_lens_hair_effect( p_filter, p_pic_out ) != VLC_SUCCESS ) )
-        return CopyInfoAndRelease( p_pic_out, p_pic_in );
+        return CopyInfoAndPush( p_pic_out, p_pic_in, sink );
 
     if ( unlikely( oldmovie_lens_dust_effect( p_filter, p_pic_out ) != VLC_SUCCESS ) )
-        return CopyInfoAndRelease( p_pic_out, p_pic_in );
+        return CopyInfoAndPush( p_pic_out, p_pic_in, sink );
 
     oldmovie_film_dust_effect( p_filter, p_pic_out );
 
-    return CopyInfoAndRelease( p_pic_out, p_pic_in );
+    return CopyInfoAndPush( p_pic_out, p_pic_in, sink );
 }
 
 /*
diff --git a/modules/video_filter/posterize.c b/modules/video_filter/posterize.c
index 91762a8591c..01f70c82229 100644
--- a/modules/video_filter/posterize.c
+++ b/modules/video_filter/posterize.c
@@ -43,7 +43,7 @@
 static int  Create      ( vlc_object_t * );
 static void Destroy     ( vlc_object_t * );
 
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 static void PlanarYUVPosterize( picture_t *, picture_t *, int);
 static void PackedYUVPosterize( picture_t *, picture_t *, int);
 static void RVPosterize( picture_t *, picture_t *, bool, int );
@@ -134,7 +134,7 @@ static int Create( vlc_object_t *p_this )
 
     var_AddCallback( p_filter, CFG_PREFIX "level", FilterCallback, p_sys );
 
-    p_filter->pf_video_filter = Filter;
+    p_filter->pf_video_filter_into = Filter;
 
     return VLC_SUCCESS;
 }
@@ -160,11 +160,11 @@ static void Destroy( vlc_object_t *p_this )
  * until it is displayed and switch the two rendering buffers, preparing next
  * frame.
  *****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     picture_t *p_outpic;
 
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_EBADVAR;
 
     filter_sys_t *p_sys = p_filter->p_sys;
     int level = atomic_load( &p_sys->i_level );
@@ -174,7 +174,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
     {
         msg_Warn( p_filter, "can't get output picture" );
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     switch( p_pic->format.i_chroma )
@@ -195,7 +195,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
             vlc_assert_unreachable();
     }
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
 
 /*****************************************************************************
diff --git a/modules/video_filter/postproc.c b/modules/video_filter/postproc.c
index bf0192de2b0..02820610415 100644
--- a/modules/video_filter/postproc.c
+++ b/modules/video_filter/postproc.c
@@ -58,7 +58,7 @@
 static int OpenPostproc( vlc_object_t * );
 static void ClosePostproc( vlc_object_t * );
 
-static picture_t *PostprocPict( filter_t *, picture_t * );
+static int PostprocPict( filter_t *, picture_t *, struct vlc_video_sink * );
 
 static int PPQCallback( vlc_object_t *, char const *,
                         vlc_value_t, vlc_value_t, void * );
@@ -252,7 +252,7 @@ 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;
+    p_filter->pf_video_filter_into = PostprocPict;
 
     msg_Warn( p_filter, "Quantification table was not set by video decoder. "
                         "Postprocessing won't look good." );
@@ -280,7 +280,7 @@ static void ClosePostproc( vlc_object_t *p_this )
 /*****************************************************************************
  * PostprocPict
  *****************************************************************************/
-static picture_t *PostprocPict( filter_t *p_filter, picture_t *p_pic )
+static int PostprocPict( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     filter_sys_t *p_sys = p_filter->p_sys;
 
@@ -288,7 +288,7 @@ static picture_t *PostprocPict( filter_t *p_filter, picture_t *p_pic )
     if( !p_outpic )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     /* Lock to prevent issues if pp_mode is changed */
@@ -319,7 +319,7 @@ static picture_t *PostprocPict( filter_t *p_filter, picture_t *p_pic )
         picture_CopyPixels( p_outpic, p_pic );
     vlc_mutex_unlock( &p_sys->lock );
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
 
 /*****************************************************************************
diff --git a/modules/video_filter/psychedelic.c b/modules/video_filter/psychedelic.c
index 94c57641227..cbfdea7b46d 100644
--- a/modules/video_filter/psychedelic.c
+++ b/modules/video_filter/psychedelic.c
@@ -44,7 +44,7 @@
 static int  Create    ( vlc_object_t * );
 static void Destroy   ( vlc_object_t * );
 
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 
 /*****************************************************************************
  * Module descriptor
@@ -96,7 +96,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->pf_video_filter_into = Filter;
 
     p_sys->x = 10;
     p_sys->y = 10;
@@ -135,7 +135,7 @@ static void Destroy( vlc_object_t *p_this )
  * until it is displayed and switch the two rendering buffers, preparing next
  * frame.
  *****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     picture_t *p_outpic;
 
@@ -145,13 +145,13 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
     picture_t *p_converted;
     video_format_t fmt_out;
 
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_EBADVAR;
 
     p_outpic = filter_NewPicture( p_filter );
     if( !p_outpic )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     filter_sys_t *p_sys = p_filter->p_sys;
@@ -256,5 +256,5 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
             p_sys->v += 1;
     }
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
diff --git a/modules/video_filter/puzzle.c b/modules/video_filter/puzzle.c
index bef39368bc4..5a65e7f0a45 100644
--- a/modules/video_filter/puzzle.c
+++ b/modules/video_filter/puzzle.c
@@ -202,7 +202,7 @@ 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_filter_into = Filter;
     p_filter->pf_video_mouse = puzzle_mouse;
 
     return VLC_SUCCESS;
@@ -244,8 +244,8 @@ static void Close( vlc_object_t *p_this ) {
 /**
  * Filter a picture
  */
-picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
-    if( !p_pic_in || !p_filter) return NULL;
+int Filter( filter_t *p_filter, picture_t *p_pic_in, struct vlc_video_sink * sink ) {
+    if( !p_pic_in || !p_filter) return VLC_EBADVAR;
 
     const video_format_t  *p_fmt_in = &p_filter->fmt_in.video;
     filter_sys_t *p_sys = p_filter->p_sys;
@@ -253,7 +253,7 @@ picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
     picture_t *p_pic_out = filter_NewPicture( p_filter );
     if( !p_pic_out ) {
         picture_Release( p_pic_in );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     int i_ret = 0;
@@ -291,7 +291,7 @@ picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
          || p_sys->s_current_param.i_pict_height != (int) p_fmt_in->i_visible_height ) {
         picture_Release(p_pic_in);
         picture_Release(p_pic_out);
-        return NULL;
+        return VLC_EGENERIC;
     }
 
     vlc_mutex_lock( &p_sys->lock );
@@ -394,13 +394,13 @@ picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
              */
             save_game_t *ps_save_game = puzzle_save(p_filter);
             if (!ps_save_game)
-                return CopyInfoAndRelease( p_pic_out, p_pic_in );
+                return CopyInfoAndPush( p_pic_out, p_pic_in, sink );
             i_ret = puzzle_bake( p_filter, p_pic_out, p_pic_in );
             if ( i_ret != VLC_SUCCESS )
             {
                 free(ps_save_game->ps_pieces);
                 free(ps_save_game);
-                return CopyInfoAndRelease( p_pic_out, p_pic_in );
+                return CopyInfoAndPush( p_pic_out, p_pic_in, sink );
             }
             puzzle_load( p_filter, ps_save_game);
             free(ps_save_game->ps_pieces);
@@ -409,7 +409,7 @@ picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
         else {
             i_ret = puzzle_bake( p_filter, p_pic_out, p_pic_in );
             if ( i_ret != VLC_SUCCESS )
-                return CopyInfoAndRelease( p_pic_out, p_pic_in );
+                return CopyInfoAndPush( p_pic_out, p_pic_in, sink );
         }
     }
 
@@ -417,7 +417,7 @@ picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
     if ( p_sys->b_shuffle_rqst && p_sys->b_init ) {
         i_ret = puzzle_bake_piece ( p_filter );
         if (i_ret != VLC_SUCCESS)
-            return CopyInfoAndRelease( p_pic_out, p_pic_in );
+            return CopyInfoAndPush( p_pic_out, p_pic_in, sink );
     }
 
     /* preset output pic */
@@ -456,7 +456,7 @@ picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
         if (i_ret != VLC_SUCCESS)
         {
             vlc_mutex_unlock( &p_sys->pce_lock );
-            return CopyInfoAndRelease( p_pic_out, p_pic_in );
+            return CopyInfoAndPush( p_pic_out, p_pic_in, sink );
         }
     }
 
@@ -572,7 +572,7 @@ picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
         }
     }
 
-    return CopyInfoAndRelease( p_pic_out, p_pic_in );
+    return CopyInfoAndPush( p_pic_out, p_pic_in, sink );
 }
 
 /*****************************************************************************
diff --git a/modules/video_filter/puzzle.h b/modules/video_filter/puzzle.h
index 8874310edab..8a95db5bd97 100644
--- a/modules/video_filter/puzzle.h
+++ b/modules/video_filter/puzzle.h
@@ -72,7 +72,7 @@ struct filter_sys_t {
 
 struct vlc_mouse_t;
 
-picture_t *Filter( filter_t *, picture_t * );
+int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 int puzzle_Callback( vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void * );
 int puzzle_mouse( filter_t *, struct vlc_mouse_t *, const struct vlc_mouse_t * );
 
diff --git a/modules/video_filter/ripple.c b/modules/video_filter/ripple.c
index a190c350039..1c0236d860f 100644
--- a/modules/video_filter/ripple.c
+++ b/modules/video_filter/ripple.c
@@ -43,7 +43,7 @@
 static int  Create    ( vlc_object_t * );
 static void Destroy   ( vlc_object_t * );
 
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 
 /*****************************************************************************
  * Module descriptor
@@ -90,7 +90,7 @@ 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;
+    p_filter->pf_video_filter_into = Filter;
 
     p_sys->f_angle = 0.0;
     p_sys->last_date = 0;
@@ -116,19 +116,19 @@ static void Destroy( vlc_object_t *p_this )
  * until it is displayed and switch the two rendering buffers, preparing next
  * frame.
  *****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     picture_t *p_outpic;
     double f_angle;
     vlc_tick_t new_date = vlc_tick_now();
 
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_EBADVAR;
 
     p_outpic = filter_NewPicture( p_filter );
     if( !p_outpic )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     filter_sys_t *p_sys = p_filter->p_sys;
@@ -229,5 +229,5 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
         }
     }
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
diff --git a/modules/video_filter/rotate.c b/modules/video_filter/rotate.c
index 005ba96ec9b..ded2a152a71 100644
--- a/modules/video_filter/rotate.c
+++ b/modules/video_filter/rotate.c
@@ -45,8 +45,8 @@
 static int  Create    ( vlc_object_t * );
 static void Destroy   ( vlc_object_t * );
 
-static picture_t *Filter( filter_t *, picture_t * );
-static picture_t *FilterPacked( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
+static int FilterPacked( filter_t *, picture_t *, struct vlc_video_sink * );
 
 static int RotateCallback( vlc_object_t *p_this, char const *psz_var,
                            vlc_value_t oldval, vlc_value_t newval,
@@ -135,11 +135,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->pf_video_filter_into = Filter;
             break;
 
         CASE_PACKED_YUV_422
-            p_filter->pf_video_filter = FilterPacked;
+            p_filter->pf_video_filter_into = FilterPacked;
             break;
 
         default:
@@ -200,18 +200,18 @@ static void Destroy( vlc_object_t *p_this )
 /*****************************************************************************
  *
  *****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     picture_t *p_outpic;
     filter_sys_t *p_sys = p_filter->p_sys;
 
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_EBADVAR;
 
     p_outpic = filter_NewPicture( p_filter );
     if( !p_outpic )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     if( p_sys->p_motion != NULL )
@@ -322,18 +322,18 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
         }
     }
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
 
 /*****************************************************************************
  *
  *****************************************************************************/
-static picture_t *FilterPacked( filter_t *p_filter, picture_t *p_pic )
+static int FilterPacked( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     picture_t *p_outpic;
     filter_sys_t *p_sys = p_filter->p_sys;
 
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_EBADVAR;
 
     int i_u_offset, i_v_offset, i_y_offset;
 
@@ -343,14 +343,14 @@ static picture_t *FilterPacked( filter_t *p_filter, picture_t *p_pic )
         msg_Warn( p_filter, "Unsupported input chroma (%4.4s)",
                   (char*)&(p_pic->format.i_chroma) );
         picture_Release( p_pic );
-        return NULL;
+        return VLC_EGENERIC;
     }
 
     p_outpic = filter_NewPicture( p_filter );
     if( !p_outpic )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     const int i_visible_pitch = p_pic->p->i_visible_pitch>>1; /* In fact it's i_visible_pixels */
@@ -429,7 +429,7 @@ static picture_t *FilterPacked( filter_t *p_filter, picture_t *p_pic )
         }
     }
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
 
 /*****************************************************************************
diff --git a/modules/video_filter/scale.c b/modules/video_filter/scale.c
index ef5189e2e13..f601ae5cb7d 100644
--- a/modules/video_filter/scale.c
+++ b/modules/video_filter/scale.c
@@ -33,12 +33,13 @@
 #include <vlc_plugin.h>
 #include <vlc_filter.h>
 #include <vlc_picture.h>
+#include "filter_picture.h"
 
 /****************************************************************************
  * Local prototypes
  ****************************************************************************/
 static int  OpenFilter ( vlc_object_t * );
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 
 /*****************************************************************************
  * Module descriptor
@@ -74,7 +75,7 @@ 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;
+    p_filter->pf_video_filter_into = Filter;
 
     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,
@@ -86,11 +87,11 @@ static int OpenFilter( vlc_object_t *p_this )
 /****************************************************************************
  * Filter: the whole thing
  ****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink * sink )
 {
     picture_t *p_pic_dst;
 
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_EBADVAR;
 
 #warning Converter cannot (really) change output format.
     video_format_ScaleCropAr( &p_filter->fmt_out.video, &p_filter->fmt_in.video );
@@ -100,7 +101,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
     if( !p_pic_dst )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     if( p_filter->fmt_in.video.i_chroma != VLC_CODEC_RGBA &&
@@ -197,7 +198,5 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
         }
     }
 
-    picture_CopyProperties( p_pic_dst, p_pic );
-    picture_Release( p_pic );
-    return p_pic_dst;
+    return CopyInfoAndPush( p_pic_dst, p_pic, sink );
 }
diff --git a/modules/video_filter/scene.c b/modules/video_filter/scene.c
index 48ca076dfd8..1e733444fe3 100644
--- a/modules/video_filter/scene.c
+++ b/modules/video_filter/scene.c
@@ -47,9 +47,8 @@
 static int  Create      ( vlc_object_t * );
 static void Destroy     ( vlc_object_t * );
 
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * sink );
 
-static void SnapshotRatio( filter_t *p_filter, picture_t *p_pic );
 static void SavePicture( filter_t *, picture_t * );
 
 /*****************************************************************************
@@ -148,7 +147,7 @@ typedef struct
 } filter_sys_t;
 
 /*****************************************************************************
- * Create: initialize and set pf_video_filter()
+ * Create: initialize and set pf_video_filter_into()
  *****************************************************************************/
 static int Create( vlc_object_t *p_this )
 {
@@ -197,7 +196,7 @@ 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;
+    p_filter->pf_video_filter_into = Filter;
 
     return VLC_SUCCESS;
 }
@@ -223,23 +222,16 @@ static void Destroy( vlc_object_t *p_this )
 /*****************************************************************************
  * Filter: Apply filtering logic to picture.
  *****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
-{
-    /* TODO: think of some funky algorithm to detect scene changes. */
-    SnapshotRatio( p_filter, p_pic );
-    return p_pic;
-}
-
-static void SnapshotRatio( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink * sink )
 {
     filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;
 
-    if( !p_pic ) return;
+    if( !p_pic ) return VLC_EBADVAR;
 
     if( p_sys->i_frames % p_sys->i_ratio != 0 )
     {
         p_sys->i_frames++;
-        return;
+        return VLC_EGENERIC;
     }
     p_sys->i_frames++;
 
@@ -266,6 +258,7 @@ static void SnapshotRatio( filter_t *p_filter, picture_t *p_pic )
         picture_Copy( p_sys->scene.p_pic, p_pic );
         SavePicture( p_filter, p_sys->scene.p_pic );
     }
+    return vlc_video_sink_PutPicture(sink, p_pic);
 }
 
 /*****************************************************************************
diff --git a/modules/video_filter/sepia.c b/modules/video_filter/sepia.c
index 9ca567c614e..4489c5f5af0 100644
--- a/modules/video_filter/sepia.c
+++ b/modules/video_filter/sepia.c
@@ -47,7 +47,7 @@ static void Destroy     ( vlc_object_t * );
 static void RVSepia( picture_t *, picture_t *, int );
 static void PlanarI420Sepia( picture_t *, picture_t *, int);
 static void PackedYUVSepia( picture_t *, picture_t *, int);
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 static const char *const ppsz_filter_options[] = {
     "intensity", NULL
 };
@@ -143,7 +143,7 @@ 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;
+    p_filter->pf_video_filter_into = Filter;
 
     return VLC_SUCCESS;
 }
@@ -169,11 +169,11 @@ static void Destroy( vlc_object_t *p_this )
  * until it is displayed and switch the two rendering buffers, preparing next
  * frame.
  *****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     picture_t *p_outpic;
 
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_EBADVAR;
 
     filter_sys_t *p_sys = p_filter->p_sys;
     int intensity = atomic_load( &p_sys->i_intensity );
@@ -183,12 +183,12 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
     {
         msg_Warn( p_filter, "can't get output picture" );
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     p_sys->pf_sepia( p_pic, p_outpic, intensity );
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
 
 #if defined(CAN_COMPILE_SSE2)
diff --git a/modules/video_filter/sharpen.c b/modules/video_filter/sharpen.c
index 76972de44af..9d49f4f5e4b 100644
--- a/modules/video_filter/sharpen.c
+++ b/modules/video_filter/sharpen.c
@@ -53,7 +53,7 @@
 static int  Create    ( vlc_object_t * );
 static void Destroy   ( vlc_object_t * );
 
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 static int SharpenCallback( vlc_object_t *, char const *,
                             vlc_value_t, vlc_value_t, void * );
 
@@ -118,7 +118,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->pf_video_filter_into = Filter;
 
     config_ChainParse( p_filter, FILTER_PREFIX, ppsz_filter_options,
                    p_filter->p_cfg );
@@ -204,7 +204,7 @@ static void Destroy( vlc_object_t *p_this )
                i_visible_pitch);                                        \
     } while (0)
 
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     picture_t *p_outpic;
     const int v1 = -1;
@@ -216,7 +216,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
     if( !p_outpic )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     filter_sys_t *p_sys = p_filter->p_sys;
@@ -229,7 +229,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
     plane_CopyPixels( &p_outpic->p[U_PLANE], &p_pic->p[U_PLANE] );
     plane_CopyPixels( &p_outpic->p[V_PLANE], &p_pic->p[V_PLANE] );
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
 
 static int SharpenCallback( vlc_object_t *p_this, char const *psz_var,
diff --git a/modules/video_filter/transform.c b/modules/video_filter/transform.c
index a9e8dcb1479..14f1a53ab08 100644
--- a/modules/video_filter/transform.c
+++ b/modules/video_filter/transform.c
@@ -37,6 +37,7 @@
 #include <vlc_filter.h>
 #include <vlc_mouse.h>
 #include <vlc_picture.h>
+#include "filter_picture.h"
 
 /*****************************************************************************
  * Module descriptor
@@ -269,23 +270,21 @@ typedef struct
     convert_t convert;
 } filter_sys_t;
 
-static picture_t *Filter(filter_t *filter, picture_t *src)
+static int Filter( filter_t *filter, picture_t *src, struct vlc_video_sink * sink )
 {
     filter_sys_t *sys = filter->p_sys;
 
     picture_t *dst = filter_NewPicture(filter);
     if (!dst) {
         picture_Release(src);
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     const vlc_chroma_description_t *chroma = sys->chroma;
     for (unsigned i = 0; i < chroma->plane_count; i++)
          (sys->plane[i])(&dst->p[i], &src->p[i]);
 
-    picture_CopyProperties(dst, src);
-    picture_Release(src);
-    return dst;
+    return CopyInfoAndPush(dst, src, sink);
 }
 
 static int Mouse(filter_t *filter, vlc_mouse_t *mouse,
@@ -433,7 +432,7 @@ static int Open(vlc_object_t *object)
     }
 
     filter->p_sys           = sys;
-    filter->pf_video_filter = Filter;
+    filter->pf_video_filter_into = Filter;
     filter->pf_video_mouse  = Mouse;
     return VLC_SUCCESS;
 error:
diff --git a/modules/video_filter/vhs.c b/modules/video_filter/vhs.c
index 6d5c13db8e8..68cf85f5ed3 100644
--- a/modules/video_filter/vhs.c
+++ b/modules/video_filter/vhs.c
@@ -83,7 +83,7 @@ typedef struct
  * Prototypes
  *****************************************************************************/
 
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 
 static int  vhs_allocate_data( filter_t *, picture_t * );
 static void vhs_free_allocated_data( filter_t * );
@@ -143,7 +143,7 @@ static int Open( vlc_object_t *p_this )
         return VLC_ENOMEM;
 
     /* init data */
-    p_filter->pf_video_filter = Filter;
+    p_filter->pf_video_filter_into = Filter;
     p_sys->i_start_time = p_sys->i_cur_time = p_sys->i_last_time = vlc_tick_now();
 
     return VLC_SUCCESS;
@@ -164,16 +164,16 @@ static void Close( vlc_object_t *p_this ) {
 /**
  * Filter a picture
  */
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
+static int Filter( filter_t *p_filter, picture_t *p_pic_in, struct vlc_video_sink * sink ) {
     if( unlikely( !p_pic_in || !p_filter) )
-        return NULL;
+        return VLC_EBADVAR;
 
     filter_sys_t *p_sys = p_filter->p_sys;
 
     picture_t *p_pic_out = filter_NewPicture( p_filter );
     if( unlikely( !p_pic_out ) ) {
         picture_Release( p_pic_in );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
    /*
@@ -188,7 +188,7 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
     if ( unlikely( !p_sys->b_init ) )
         if ( unlikely( vhs_allocate_data( p_filter, p_pic_in ) != VLC_SUCCESS ) ) {
             picture_Release( p_pic_in );
-            return NULL;
+            return VLC_EGENERIC;
         }
     p_sys->b_init = true;
 
@@ -201,14 +201,14 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
     * apply effects on picture
     */
     if ( unlikely( vhs_blue_red_line_effect( p_filter, p_pic_out ) != VLC_SUCCESS ) )
-        return CopyInfoAndRelease( p_pic_out, p_pic_in );
+        return CopyInfoAndPush( p_pic_out, p_pic_in, sink );
 
     if ( unlikely( vhs_sliding_effect(p_filter, p_pic_out ) != VLC_SUCCESS ) )
-        return CopyInfoAndRelease( p_pic_out, p_pic_in );
+        return CopyInfoAndPush( p_pic_out, p_pic_in, sink );
 
     vhs_blue_red_dots_effect( p_filter, p_pic_out );
 
-    return CopyInfoAndRelease( p_pic_out, p_pic_in );
+    return CopyInfoAndPush( p_pic_out, p_pic_in, sink );
 }
 
 /*
diff --git a/modules/video_filter/wave.c b/modules/video_filter/wave.c
index eae6c961b74..c483cb3be33 100644
--- a/modules/video_filter/wave.c
+++ b/modules/video_filter/wave.c
@@ -42,7 +42,7 @@
  *****************************************************************************/
 static int  Create    ( vlc_object_t * );
 
-static picture_t *Filter( filter_t *, picture_t * );
+static int Filter( filter_t *, picture_t *, struct vlc_video_sink * );
 
 /*****************************************************************************
  * Module descriptor
@@ -90,7 +90,7 @@ static int Create( vlc_object_t *p_this )
     if( !p_sys )
         return VLC_ENOMEM;
 
-    p_filter->pf_video_filter = Filter;
+    p_filter->pf_video_filter_into = Filter;
 
     p_sys->f_angle = 0.0;
     p_sys->last_date = 0;
@@ -105,19 +105,19 @@ static int Create( vlc_object_t *p_this )
  * until it is displayed and switch the two rendering buffers, preparing next
  * frame.
  *****************************************************************************/
-static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+static int Filter( filter_t *p_filter, picture_t *p_pic, struct vlc_video_sink *sink )
 {
     picture_t *p_outpic;
     double f_angle;
     vlc_tick_t new_date = vlc_tick_now();
 
-    if( !p_pic ) return NULL;
+    if( !p_pic ) return VLC_EBADVAR;
 
     p_outpic = filter_NewPicture( p_filter );
     if( !p_outpic )
     {
         picture_Release( p_pic );
-        return NULL;
+        return VLC_ENOMEM;
     }
 
     filter_sys_t *p_sys = p_filter->p_sys;
@@ -203,5 +203,5 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
         }
     }
 
-    return CopyInfoAndRelease( p_outpic, p_pic );
+    return CopyInfoAndPush( p_outpic, p_pic, sink );
 }
-- 
2.26.2



More information about the vlc-devel mailing list