PR #24061 opened by Gian Paolo (gianPa0I0)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24061
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24061.patch

Granulate is a Time-Domain Video Granulator. The filter stores past frames in a 
FIFO buffer, and uses rectangular patches of those frames to apply grains in 
the current frame. The filter lets the user choose between different 
granulation methods and alterations of the pixels in the buffered frames. The 
size of the buffer is user-defined, as is the access to buffered frames (delay 
or random).

Signed-off-by: Gian Paolo Gigante <[email protected]>

# Summary of changes

Added all files requested by writing_filters.txt.
All parameters are specified in detail in the .texi.
Originally sent to the mailing list ffmpeg-devel: 
https://lists.ffmpeg.org/archives/list/[email protected]/thread/ZSGBZ2FLU4EWU3RM7W6YJ2P65EASILSF/#ZSGBZ2FLU4EWU3RM7W6YJ2P65EASILSF
Moved here for easier management and due to inactivity in the mailing-list



>From 01e2a867cfb685ff1f151308def12702084148e4 Mon Sep 17 00:00:00 2001
From: Gian Paolo Gigante <[email protected]>
Date: Sun, 9 Aug 2026 14:19:01 +0200
Subject: [PATCH] avfilter: add granulate filter

Granulate is a Time-Domain Video Granulator. The filter stores past frames in a 
FIFO buffer, and uses rectangular patches of those frames to apply grains in 
the current frame. The filter lets the user choose between different 
granulation methods and alterations of the pixels in the buffered frames. The 
size of the buffer is user-defined, as is the access to buffered frames (delay 
or random).

Signed-off-by: Gian Paolo Gigante <[email protected]>
---
 Changelog                             |   1 +
 doc/filters.texi                      |  62 +++
 libavfilter/Makefile                  |   1 +
 libavfilter/allfilters.c              |   1 +
 libavfilter/version.h                 |   4 +-
 libavfilter/vf_granulate.c            | 691 ++++++++++++++++++++++++++
 tests/fate/filter-video.mak           |   6 +
 tests/ref/fate/filter-granulate-adv   | 155 ++++++
 tests/ref/fate/filter-granulate-basic | 155 ++++++
 9 files changed, 1074 insertions(+), 2 deletions(-)
 create mode 100644 libavfilter/vf_granulate.c
 create mode 100644 tests/ref/fate/filter-granulate-adv
 create mode 100644 tests/ref/fate/filter-granulate-basic

diff --git a/Changelog b/Changelog
index 38f1e10263..9eaf0ba97b 100644
--- a/Changelog
+++ b/Changelog
@@ -2,6 +2,7 @@ Entries are sorted chronologically from oldest to youngest 
within each release,
 releases are sorted from youngest to oldest.
 
 version <next>:
+- granulate filter
 - extensively improved AAC encoder
 - APV Vulkan encoder
 - iTerm2 inline image protocol muxer
diff --git a/doc/filters.texi b/doc/filters.texi
index 985a1615a9..c5bc63d12c 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -15420,6 +15420,68 @@ Set upper limit for video rate of output stream, 
Default value is @var{25}.
 This guarantee that output video frame rate will not be higher than this value.
 @end table
 
+@section granulate
+A Time-Domain Video Granulator: granulates past frames in current frame
+
+@example
+ffmpeg -i INPUT -vf 
granulate=mode=3:zoom=2:offset_time=50:buffer=64:n_grains=2:ghosting=1 OUTPUT
+@end example
+
+@table @option
+
+@item mode
+Set the type of granulation @code{[0 - 3]}. Default is @code{0} @*
+0 : PIXELS    Granulates rectangular blocks @*
+1 : INTERLACED_H    Granulates every other line @*
+2 : INTERLACED_V    Granulates every other column @*
+3 : DITHER    Granulates in a noisy manner
+
+@item zoom
+Set the amount of zoom applied to the grains @code{[1 - 256]}. Default is 
@code{1}
+
+@item offset_time
+Set the amount of frames before the offset of the zoom is reset @code{[0 - 
UINT_MAX]}. Default is @code{0 : OFF}
+
+@item n_grains
+Set the number of grains per frame @code{[0 - UINT_MAX]}. Default is @code{0 : 
OFF}
+
+@item buffer
+Set the size of the buffer of frames (FIFO) - Each grain will be generated 
from a random frame @code{[1 - 8192]}. Default is @code{1}
+
+@item grain_w
+Set the width of the grains - If set to 0, each grain will have the width of 
the INPUT file @code{[0 - 8192]}. Default is @code{0}
+
+@item grain_h
+Set the height of the grains - If set to 0, each grain will have the height of 
the INPUT file @code{[0 - 8192]}. Default is @code{0}
+
+@item var_size
+Toggle random grain width and height (Each grain will have a random size 
between grain_w/h and 0) @code{[0 - 1]}. Default is @code{0 : OFF}
+
+@item ghosting
+Set the type of ghosting (separation of components) @code{[0 - 2]}. Default is 
@code{0} @*
+0 : NO_GHOSTING    Grains have both Luma and Chroma components @*
+1 : LUMA_GHOSTING    Grains have only luma component @*
+2 : CHROMA_GHOSTING    Grains have only chroma component @*
+
+@item static_grains
+Toggle stable grain position @code{[0 - 1]}. Default is @code{0 : OFF}
+
+@item reset_time
+Set the number of frames before the stable position are reset @code{[0 - 
UINT_MAX]}. Default is @code{0 : OFF}
+
+@item delay
+Set number of frames before refreshing the delay (stable buffer offset) 
@code{[0 - UINT_MAX]}. Default is @code{0 : OFF}
+
+@item seed
+Set the seed for internal AVlfg - If 0, the seed will be generated randomly 
@code{[0 - UINT32_MAX]}. Default is @code{0}
+
+@end table
+
+@subsection Runtime Params
+
+This filter supports runtime change for specific parameters: @*
+[mode - zoom - offset_time - ghosting - delay]
+
 @section grayworld
 A color constancy filter that applies color correction based on the grayworld 
assumption
 
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index daa4d552f0..903f16743b 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -336,6 +336,7 @@ OBJS-$(CONFIG_GBLUR_FILTER)                  += vf_gblur.o
 OBJS-$(CONFIG_GBLUR_VULKAN_FILTER)           += vf_gblur_vulkan.o vulkan.o 
vulkan_filter.o
 OBJS-$(CONFIG_GEQ_FILTER)                    += vf_geq.o
 OBJS-$(CONFIG_GRADFUN_FILTER)                += vf_gradfun.o
+OBJS-$(CONFIG_GRANULATE_FILTER)              += vf_granulate.o
 OBJS-$(CONFIG_GRAPHMONITOR_FILTER)           += f_graphmonitor.o
 OBJS-$(CONFIG_GRAYWORLD_FILTER)              += vf_grayworld.o
 OBJS-$(CONFIG_GREYEDGE_FILTER)               += vf_colorconstancy.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 4af7a3bbbf..2aeec4a03c 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -310,6 +310,7 @@ extern const FFFilter ff_vf_gblur;
 extern const FFFilter ff_vf_gblur_vulkan;
 extern const FFFilter ff_vf_geq;
 extern const FFFilter ff_vf_gradfun;
+extern const FFFilter ff_vf_granulate;
 extern const FFFilter ff_vf_graphmonitor;
 extern const FFFilter ff_vf_grayworld;
 extern const FFFilter ff_vf_greyedge;
diff --git a/libavfilter/version.h b/libavfilter/version.h
index 48abf6052f..4d8f28e512 100644
--- a/libavfilter/version.h
+++ b/libavfilter/version.h
@@ -31,8 +31,8 @@
 
 #include "version_major.h"
 
-#define LIBAVFILTER_VERSION_MINOR   3
-#define LIBAVFILTER_VERSION_MICRO 101
+#define LIBAVFILTER_VERSION_MINOR   4
+#define LIBAVFILTER_VERSION_MICRO 100
 
 
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
diff --git a/libavfilter/vf_granulate.c b/libavfilter/vf_granulate.c
new file mode 100644
index 0000000000..2b7abbe6e9
--- /dev/null
+++ b/libavfilter/vf_granulate.c
@@ -0,0 +1,691 @@
+/*
+ * Copyright (c) 2026 Gian Paolo Gigante
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Time-Domain Video Granulator: Granulate past frames in current frame
+ *
+ */
+
+#include "avfilter.h"
+#include "libavutil/attributes.h"
+#include "libavutil/frame.h"
+#include "libavutil/mem.h"
+#include "libavutil/opt.h"
+#include "libavutil/lfg.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/random_seed.h"
+#include "formats.h"
+#include "video.h"
+#include "error.h"
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+
+#define N_RGB_PLANES 3
+#define RGB_R_OFFSET 0
+#define RGB_G_OFFSET 1
+#define RGB_B_OFFSET 2
+
+typedef enum FilterMode {
+    MODE_PIXELS,
+    MODE_INTERLACED_H,
+    MODE_INTERLACED_V,
+    MODE_DITHER
+} filter_mode;
+
+typedef enum GhostingMode {
+    NO_GHOSTING,
+    LUMA_GHOSTING,
+    CHROMA_GHOSTING
+} ghosting_mode;
+
+typedef void (*copy_grain)(AVFrame *dst, const AVFrame *src, int sx, int sy, 
int dx, int dy, 
+                            int w, int h, filter_mode mode, ghosting_mode 
ghosting, int zoom, int var_size, 
+                            int pix_fmt, uint8_t log2_chroma_h, uint8_t 
log2_chroma_w, AVLFG *lfg);
+
+typedef struct GrainPos {
+    int pos_x;
+    int pos_y;
+    int g_pos_x;
+    int g_pos_y;
+} GrainPos;
+
+typedef struct GranulateContext {
+    const AVClass *class;
+
+    int PixFmt;
+    AVLFG *lfg;
+    uint32_t seed;
+    filter_mode mode;
+    ghosting_mode ghosting;
+    unsigned int buffer_size, buffer_index, buffer_full;
+    AVFrame **fbuffer;
+    unsigned int zoom_amount, zoom_set;
+    unsigned int zoom_offset_w, zoom_offset_h;
+    unsigned int offset_time;
+    copy_grain copy_grain_fn;
+    unsigned int grain_w, grain_h;
+    int fullscreen;
+    unsigned int n_grains;
+    int static_grains;
+    GrainPos *grain_pos;
+    int grains_set;
+    unsigned int reset_time;
+    int var_size;
+    uint64_t frame_count;
+    uint8_t log2_chroma_h, log2_chroma_w;
+    unsigned int delay;
+    unsigned int delay_set;
+} GranulateContext;
+
+#define OFFSET(x) offsetof(GranulateContext, x)
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+#define R AV_OPT_FLAG_RUNTIME_PARAM
+
+static const AVOption granulate_options[] = {
+    {"mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_PIXELS}, 
MODE_PIXELS, MODE_DITHER, FLAGS | R},
+    {"zoom", "set zoom amount", OFFSET(zoom_amount), AV_OPT_TYPE_UINT, 
{.i64=1}, 1, 256, FLAGS | R},
+    {"offset_time", "set number of frames befor zoom offset is reset", 
OFFSET(offset_time), AV_OPT_TYPE_UINT, {.i64=0}, 0, UINT_MAX, FLAGS | R},
+    {"n_grains", "number of grains per frame", OFFSET(n_grains), 
AV_OPT_TYPE_UINT, {.i64=0}, 0, UINT_MAX, FLAGS},
+    {"buffer", "set the size of the buffer", OFFSET(buffer_size), 
AV_OPT_TYPE_UINT, {.i64=1}, 1, 8192, FLAGS},
+    {"grain_w", "set the width of each grain in px", OFFSET(grain_w), 
AV_OPT_TYPE_UINT, {.i64=0}, 0, 8192, FLAGS},
+    {"grain_h", "set the height of each grain in px", OFFSET(grain_h), 
AV_OPT_TYPE_UINT, {.i64=0}, 0, 8192, FLAGS},
+    {"var_size", "toggle random grain size (grain_size as max size)", 
OFFSET(var_size), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"ghosting", "select type of ghosting", OFFSET(ghosting), AV_OPT_TYPE_INT, 
{.i64=NO_GHOSTING}, NO_GHOSTING, CHROMA_GHOSTING, FLAGS | R},
+    {"static_grains", "toggle stable grain position", OFFSET(static_grains), 
AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"reset_time","set number of frames before grain_pos reset", 
OFFSET(reset_time), AV_OPT_TYPE_UINT, {.i64=0}, 0, UINT_MAX, FLAGS},
+    {"delay", "set number of frames before refresh of delay", OFFSET(delay), 
AV_OPT_TYPE_UINT, {.i64=0}, 0, UINT_MAX, FLAGS | R},
+    {"seed", "set seed for AVlfg", OFFSET(seed), AV_OPT_TYPE_UINT, {.i64=0}, 
0, UINT32_MAX, FLAGS},
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(granulate);
+
+static av_cold int init(AVFilterContext *ctx)
+{
+    int i;
+    GranulateContext *granulate_ctx = ctx->priv;
+
+    granulate_ctx->lfg = av_calloc(1, sizeof(AVLFG));
+    if (!granulate_ctx->lfg)
+        return AVERROR(ENOMEM);
+    
+    if (!granulate_ctx->seed)
+        granulate_ctx->seed = av_get_random_seed();
+    av_lfg_init(granulate_ctx->lfg, granulate_ctx->seed);
+
+    if (granulate_ctx->static_grains && granulate_ctx->n_grains) {
+        granulate_ctx->grain_pos = av_calloc(granulate_ctx->n_grains, 
sizeof(GrainPos));
+
+        if (!granulate_ctx->grain_pos)
+            return AVERROR(ENOMEM);
+    }
+
+    granulate_ctx->fbuffer = av_calloc(granulate_ctx->buffer_size, 
sizeof(AVFrame *));
+
+    if (!granulate_ctx->fbuffer)
+        return AVERROR(ENOMEM);
+
+    for (i = 0; i < granulate_ctx->buffer_size; i++) {
+        granulate_ctx->fbuffer[i] = av_frame_alloc();
+
+        if (!granulate_ctx->fbuffer[i])
+            return AVERROR(ENOMEM);
+    }
+    granulate_ctx->buffer_index = 0;
+    granulate_ctx->zoom_offset_w = 0;
+    granulate_ctx->zoom_offset_h = 0;
+    granulate_ctx->frame_count = 0;
+    granulate_ctx->zoom_set = 0;
+    granulate_ctx->buffer_full = 0;
+    granulate_ctx->delay_set = 0;
+    granulate_ctx->fullscreen = 1;
+
+    return 0;
+}
+
+static int query_formats(const AVFilterContext *ctx, AVFilterFormatsConfig 
**cfg_in, AVFilterFormatsConfig **cfg_out)
+{
+    static const enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_YUV420P, 
AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P, 
+                                                AV_PIX_FMT_GRAY8, 
AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, AV_PIX_FMT_NONE};
+
+    return ff_set_pixel_formats_from_list2(ctx, cfg_in, cfg_out, pix_fmts);
+}
+
+static int granulate_process_command(AVFilterContext *ctx, const char *cmd, 
const char *arg, char *res, int res_len, int flags)
+{
+    av_log(ctx, AV_LOG_INFO, "Received command: %s=%s\n", cmd, arg);
+    return ff_filter_process_command(ctx, cmd, arg, res, res_len, flags);
+}
+
+static void copy_grain_YUV(AVFrame *dst, const AVFrame *src, int sx, int sy, 
int dx, int dy, int grain_w, int grain_h, filter_mode mode, ghosting_mode 
ghosting, int zoom, int var_size, int pix_fmt, uint8_t log2_chroma_h, uint8_t 
log2_chroma_w, AVLFG *lfg);
+
+static void copy_grain_GRAY(AVFrame *dst, const AVFrame *src, int sx, int sy, 
int dx, int dy, int grain_w, int grain_h, filter_mode mode, ghosting_mode 
ghosting, int zoom, int var_size, int pix_fmt, uint8_t log2_chroma_h, uint8_t 
log2_chroma_w, AVLFG *lfg);
+
+static void copy_grain_RGB(AVFrame *dst, const AVFrame *src, int sx, int sy, 
int dx, int dy, int grain_w, int grain_h, filter_mode mode, ghosting_mode 
ghosting, int zoom, int var_size, int pix_fmt, uint8_t log2_chroma_h, uint8_t 
log2_chroma_w, AVLFG *lfg);
+
+static int config_props(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    GranulateContext *granulate_ctx = ctx->priv;
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
+
+    granulate_ctx->log2_chroma_h = desc->log2_chroma_h;
+    granulate_ctx->log2_chroma_w = desc->log2_chroma_w;
+
+    switch (inlink->format) {
+        case (AV_PIX_FMT_YUV420P):
+            granulate_ctx->PixFmt = AV_PIX_FMT_YUV420P; 
granulate_ctx->copy_grain_fn = copy_grain_YUV; break;
+        case (AV_PIX_FMT_YUV422P):
+            granulate_ctx->PixFmt = AV_PIX_FMT_YUV422P; 
granulate_ctx->copy_grain_fn = copy_grain_YUV; break;
+        case (AV_PIX_FMT_YUV444P):
+            granulate_ctx->PixFmt = AV_PIX_FMT_YUV444P; 
granulate_ctx->copy_grain_fn = copy_grain_YUV; break;
+        case (AV_PIX_FMT_GRAY8):
+            granulate_ctx->PixFmt = AV_PIX_FMT_GRAY8; 
granulate_ctx->copy_grain_fn = copy_grain_GRAY; break;
+        case (AV_PIX_FMT_RGB24):
+            granulate_ctx->PixFmt = AV_PIX_FMT_RGB24; 
granulate_ctx->copy_grain_fn = copy_grain_RGB; break;
+        case (AV_PIX_FMT_BGR24):
+            granulate_ctx->PixFmt = AV_PIX_FMT_BGR24; 
granulate_ctx->copy_grain_fn = copy_grain_RGB; break;
+        default: 
+            return AVERROR(EINVAL);
+    }
+
+    for (int i = 0; i < granulate_ctx->buffer_size; i++) {
+        AVFrame *f = granulate_ctx->fbuffer[i];
+
+        av_frame_unref(f);
+        f->format = inlink->format;
+        f->width  = inlink->w;
+        f->height = inlink->h;
+
+        int ret = av_frame_get_buffer(f, 0);
+        if (ret < 0)
+            return ret;
+    }
+
+    return 0;
+}
+
+static av_always_inline void copy_px_data0(AVFrame *dst, const AVFrame *src, 
int d_row_offset, int s_row_offset, int d_col_offset,int s_col_offset) {
+    dst->data[0][d_row_offset * dst->linesize[0] + d_col_offset] = 
src->data[0][s_row_offset * src->linesize[0] + s_col_offset];
+}
+
+static av_always_inline void copy_px_data1(AVFrame *dst, const AVFrame *src, 
int d_row_offset, int s_row_offset, int d_col_offset,int s_col_offset) {
+    dst->data[1][d_row_offset * dst->linesize[1] + d_col_offset] = 
src->data[1][s_row_offset * src->linesize[1] + s_col_offset];
+}
+
+static av_always_inline void copy_px_data2(AVFrame *dst, const AVFrame *src, 
int d_row_offset, int s_row_offset, int d_col_offset,int s_col_offset) {
+    dst->data[2][d_row_offset * dst->linesize[2] + d_col_offset] = 
src->data[2][s_row_offset * src->linesize[2] + s_col_offset];
+}
+
+
+static void copy_grain_YUV(AVFrame *dst, const AVFrame *src, int sx, int sy, 
int dx, int dy,
+                            int grain_w, int grain_h, filter_mode mode, 
ghosting_mode ghosting, 
+                            int zoom, int var_size, int pix_fmt, uint8_t 
log2_chroma_h, uint8_t log2_chroma_w, AVLFG *lfg)
+{
+    if (var_size) {
+        grain_h = av_lfg_get(lfg) % (grain_h + 1);
+        grain_w = av_lfg_get(lfg) % (grain_w + 1);
+    }
+    int grain_w_chroma;
+    int grain_h_chroma;
+    int sx_chroma, sy_chroma;
+    int dx_chroma, dy_chroma;
+
+    if (pix_fmt == AV_PIX_FMT_YUV420P) {
+        grain_w_chroma = AV_CEIL_RSHIFT(grain_w, log2_chroma_w);
+        grain_h_chroma = AV_CEIL_RSHIFT(grain_h, log2_chroma_h);
+        sx_chroma = sx >> 1;
+        sy_chroma = sy >> 1;
+        dx_chroma = dx >> 1;
+        dy_chroma = dy >> 1;
+    }
+    
+
+    if (pix_fmt == AV_PIX_FMT_YUV422P) {
+        grain_w_chroma = AV_CEIL_RSHIFT(grain_w, log2_chroma_w);
+        grain_h_chroma = grain_h;
+        sx_chroma = sx >> 1;
+        sy_chroma = sy;
+        dx_chroma = dx >> 1;
+        dy_chroma = dy;
+    }
+
+    if (pix_fmt == AV_PIX_FMT_YUV444P) {
+        grain_w_chroma = grain_w;
+        grain_h_chroma = grain_h;
+        sx_chroma = sx;
+        sy_chroma = sy;
+        dx_chroma = dx;
+        dy_chroma = dy;
+    }
+
+    int row_step = 1;
+    int col_step = 1;
+
+    if (mode == MODE_INTERLACED_H)
+        row_step++;
+
+    if (mode == MODE_INTERLACED_V)
+        col_step++;
+
+    int d_row_offset, d_col_offset;
+    int s_row_offset, s_col_offset;
+
+    if (ghosting != CHROMA_GHOSTING) {
+        for (int row = 0; row < grain_h; row += row_step) {
+            d_row_offset = dy + row;
+            s_row_offset = sy + row / zoom;
+            for (int col = 0; col < grain_w; col += col_step) {
+                d_col_offset = dx + col;
+                s_col_offset = sx + col / zoom;
+                if (mode != MODE_DITHER) {
+                    copy_px_data0(dst, src, d_row_offset, s_row_offset, 
d_col_offset, s_col_offset);
+                }
+                else {
+                    if (av_lfg_get(lfg) & 1)
+                        copy_px_data0(dst, src, d_row_offset, s_row_offset, 
d_col_offset, s_col_offset);
+                }
+            }
+        }
+    }
+    if (ghosting != LUMA_GHOSTING) {
+        for (int row = 0; row < grain_h_chroma; row += row_step) {
+            d_row_offset = dy_chroma + row;
+            s_row_offset = sy_chroma + row / zoom;
+            for (int col = 0; col < grain_w_chroma; col += col_step) {
+                d_col_offset = dx_chroma + col;
+                s_col_offset = sx_chroma + col / zoom;
+                if (mode != MODE_DITHER) {
+                    copy_px_data1(dst, src, d_row_offset, s_row_offset, 
d_col_offset, s_col_offset);
+                    copy_px_data2(dst, src, d_row_offset, s_row_offset, 
d_col_offset, s_col_offset);
+                }
+                else {
+                    if (av_lfg_get(lfg) & 1) {
+                        copy_px_data1(dst, src, d_row_offset, s_row_offset, 
d_col_offset, s_col_offset);
+                        copy_px_data2(dst, src, d_row_offset, s_row_offset, 
d_col_offset, s_col_offset);
+                    }
+                }
+            }
+        }
+    }
+}
+
+static void copy_grain_GRAY(AVFrame *dst, const AVFrame *src, int sx, int sy, 
int dx, int dy, 
+                            int grain_w, int grain_h, filter_mode mode, 
ghosting_mode ghosting, 
+                            int zoom, int var_size, int pix_fmt, uint8_t 
log2_chroma_h, uint8_t log2_chroma_w, AVLFG *lfg)
+{
+    if (var_size) {
+        grain_h = av_lfg_get(lfg) % (grain_h + 1);
+        grain_w = av_lfg_get(lfg) % (grain_w + 1);
+    }
+
+    int row_step = 1;
+    int col_step = 1;
+
+    if (mode == MODE_INTERLACED_H)
+        row_step++;
+
+    if (mode == MODE_INTERLACED_V)
+        col_step++;
+    
+    int d_row_offset, d_col_offset;
+    int s_row_offset, s_col_offset;
+
+    for (int row = 0; row < grain_h; row += row_step) {
+        d_row_offset = dy + row;
+        s_row_offset = sy + row / zoom;
+        for (int col = 0; col < grain_w; col += col_step) {
+            d_col_offset = dx + col;
+            s_col_offset = sx + col / zoom;
+            if (mode != MODE_DITHER) {
+                copy_px_data0(dst, src, d_row_offset, s_row_offset, 
d_col_offset, s_col_offset);
+            }
+            else {
+                if (av_lfg_get(lfg) & 1)
+                    copy_px_data0(dst, src, d_row_offset, s_row_offset, 
d_col_offset, s_col_offset);
+            }
+        }
+    }
+}
+
+static void copy_grain_RGB(AVFrame *dst, const AVFrame *src, int sx, int sy, 
int dx, int dy,
+                            int grain_w, int grain_h, filter_mode mode, 
ghosting_mode ghosting, 
+                            int zoom, int var_size, int pix_fmt, uint8_t 
log2_chroma_h, uint8_t log2_chroma_w, AVLFG *lfg)
+{
+    if (var_size) {
+        grain_h = av_lfg_get(lfg) % (grain_h + 1);
+        grain_w = av_lfg_get(lfg) % (grain_w + 1);
+    }
+
+    int row_step = 1;
+    int col_step = 1;
+
+    if (mode == MODE_INTERLACED_H)
+        row_step++;
+
+    if (mode == MODE_INTERLACED_V)
+        col_step++;
+    
+    int d_row_offset, s_row_offset;
+    int d1_col_offset, d2_col_offset, d3_col_offset;
+    int s1_col_offset, s2_col_offset, s3_col_offset;
+
+    for (int row = 0; row < grain_h; row += row_step) {
+        d_row_offset = dy + row;
+        s_row_offset = sy + row / zoom;
+        for (int col = 0; col < grain_w; col += col_step) {
+            d1_col_offset = dx + col * N_RGB_PLANES + RGB_R_OFFSET;
+            s1_col_offset = sx + col / zoom * N_RGB_PLANES + RGB_R_OFFSET;
+            d2_col_offset = dx + col * N_RGB_PLANES + RGB_G_OFFSET;
+            s2_col_offset = sx + col / zoom * N_RGB_PLANES + RGB_G_OFFSET;
+            d3_col_offset = dx + col * N_RGB_PLANES + RGB_B_OFFSET;
+            s3_col_offset = sx + col / zoom * N_RGB_PLANES + RGB_B_OFFSET;
+            if (mode != MODE_DITHER) {
+                if (!ghosting) {
+                    copy_px_data0(dst, src, d_row_offset, s_row_offset, 
d1_col_offset, s1_col_offset);
+                    copy_px_data0(dst, src, d_row_offset, s_row_offset, 
d2_col_offset, s2_col_offset);
+                    copy_px_data0(dst, src, d_row_offset, s_row_offset, 
d3_col_offset, s3_col_offset);
+                }
+                else {
+                    uint32_t r = av_lfg_get(lfg);
+                    if (r & 2)
+                        copy_px_data0(dst, src, d_row_offset, s_row_offset, 
d1_col_offset, s1_col_offset);
+                    if (r & 4)
+                        copy_px_data0(dst, src, d_row_offset, s_row_offset, 
d2_col_offset, s2_col_offset);
+                    if (r & 8)
+                        copy_px_data0(dst, src, d_row_offset, s_row_offset, 
d3_col_offset, s3_col_offset);
+                }
+            }
+            else {
+                uint32_t r = av_lfg_get(lfg);
+                if (r & 1) {
+                    if (!ghosting) {
+                        copy_px_data0(dst, src, d_row_offset, s_row_offset, 
d1_col_offset, s1_col_offset);
+                        copy_px_data0(dst, src, d_row_offset, s_row_offset, 
d2_col_offset, s2_col_offset);
+                        copy_px_data0(dst, src, d_row_offset, s_row_offset, 
d3_col_offset, s3_col_offset);
+                        }
+                    else {
+                        if (r & 2)
+                            copy_px_data0(dst, src, d_row_offset, 
s_row_offset, d1_col_offset, s1_col_offset);
+                        if (r & 4)
+                            copy_px_data0(dst, src, d_row_offset, 
s_row_offset, d2_col_offset, s2_col_offset);
+                        if (r & 8)
+                            copy_px_data0(dst, src, d_row_offset, 
s_row_offset, d3_col_offset, s3_col_offset);
+                    }
+                }
+            }
+        }
+    }
+}
+
+
+static void granulate_rand(const GranulateContext *ctx, AVFrame *dst, AVFrame 
**src, int width, int height)
+{
+    AVFrame *src_f = NULL;
+
+    int grain_w = ctx->grain_w;
+    int grain_h = ctx->grain_h;
+    int n_grains = ctx->n_grains;
+    int offset_w = ctx->zoom_offset_w;
+    int offset_h = ctx->zoom_offset_h;
+    int g_src = 0;
+
+    if (ctx->delay_set) {
+        g_src = (ctx->delay_set + ctx->frame_count) % ctx->buffer_size;
+        src_f = src[g_src];
+    }
+    
+    for (int grain_count = 0; grain_count < n_grains; grain_count++) {
+        if (!ctx->delay_set) {
+            if (ctx->buffer_full) {
+                g_src = av_lfg_get(ctx->lfg) % ctx->buffer_size;
+                src_f = src[g_src];
+            }
+            else {
+                g_src = av_lfg_get(ctx->lfg) % (ctx->buffer_index + 1);
+                src_f = src[g_src];
+            }
+        }
+        int sx = av_lfg_get(ctx->lfg) % (width - grain_w + 1);
+        int sy = av_lfg_get(ctx->lfg) % (height - grain_h + 1);
+        int dx = av_lfg_get(ctx->lfg) % (width - grain_w + 1);
+        int dy = av_lfg_get(ctx->lfg) % (height - grain_h + 1);
+
+        ctx->copy_grain_fn(dst, src_f, sx + offset_w, sy + offset_h, dx, dy, 
grain_w, grain_h, ctx->mode, ctx->ghosting, ctx->zoom_amount, ctx->var_size, 
ctx->PixFmt, ctx->log2_chroma_h, ctx->log2_chroma_w, ctx->lfg);
+    }
+}
+
+static void granulate_pos(const GranulateContext *ctx, AVFrame *dst, AVFrame 
**src, int width, int height)
+{
+    AVFrame *src_f = NULL;
+
+    int grain_w = ctx->grain_w;
+    int grain_h = ctx->grain_h;
+    int n_grains = ctx->n_grains;
+    int offset_w = ctx->zoom_offset_w;
+    int offset_h = ctx->zoom_offset_h;
+    GrainPos *grain_pos = ctx->grain_pos;
+    int g_src = 0;
+
+    if (ctx->delay_set) {
+        g_src = (ctx->delay_set + ctx->frame_count) % ctx->buffer_size;
+        src_f = src[g_src];
+    }
+
+    for (int grain_count = 0; grain_count < n_grains; grain_count++) {
+        if (!ctx->delay_set) {
+            if (ctx->buffer_full) {
+                g_src = av_lfg_get(ctx->lfg) % ctx->buffer_size;
+                src_f = src[g_src];
+            }
+            else {
+                g_src = av_lfg_get(ctx->lfg) % (ctx->buffer_index + 1);
+                src_f = src[g_src];
+            }
+        }
+        ctx->copy_grain_fn(dst, src_f, grain_pos[grain_count].g_pos_x + 
offset_w, grain_pos[grain_count].g_pos_y + offset_h, 
grain_pos[grain_count].pos_x, grain_pos[grain_count].pos_y, grain_w, grain_h, 
ctx->mode, ctx->ghosting, ctx->zoom_amount, ctx->var_size, ctx->PixFmt, 
ctx->log2_chroma_h, ctx->log2_chroma_w, ctx->lfg);
+    }
+}
+
+static void granulate_in_frame(const GranulateContext *ctx, AVFrame *dst, int 
width, int height)
+{
+
+    int grain_w = ctx->grain_w;
+    int grain_h = ctx->grain_h;
+    int n_grains = ctx->n_grains;
+    int offset_w = ctx->zoom_offset_w;
+    int offset_h = ctx->zoom_offset_h;
+
+    for (int grain_count = 0; grain_count < n_grains; grain_count++) {
+        int sx = av_lfg_get(ctx->lfg) % (width - grain_w + 1);
+        int sy = av_lfg_get(ctx->lfg) % (height - grain_h + 1);
+        int dx = av_lfg_get(ctx->lfg) % (width - grain_w + 1);
+        int dy = av_lfg_get(ctx->lfg) % (height - grain_h + 1);
+
+        ctx->copy_grain_fn(dst, ctx->fbuffer[0], sx + offset_w, sy + offset_h, 
dx, dy, grain_w, grain_h, ctx->mode, ctx->ghosting, ctx->zoom_amount, 
ctx->var_size, ctx->PixFmt, ctx->log2_chroma_h, ctx->log2_chroma_w, ctx->lfg);
+    }
+}
+
+static void init_granulate_pos(const GranulateContext *ctx, int width, int 
height)
+{
+
+    int grain_w = ctx->grain_w;
+    int grain_h = ctx->grain_h;
+    int n_grains = ctx->n_grains;
+    GrainPos *grain_pos = ctx->grain_pos;
+
+    for (int grain_count = 0; grain_count < n_grains; grain_count++) {
+        grain_pos[grain_count].g_pos_x = av_lfg_get(ctx->lfg) % (width - 
grain_w + 1);
+        grain_pos[grain_count].g_pos_y = av_lfg_get(ctx->lfg) % (height - 
grain_h + 1);
+        grain_pos[grain_count].pos_x = av_lfg_get(ctx->lfg) % (width - grain_w 
+ 1);
+        grain_pos[grain_count].pos_y = av_lfg_get(ctx->lfg) % (height - 
grain_h + 1);
+    }
+}
+
+static void set_offset(GranulateContext *ctx) {
+    ctx->zoom_offset_w = av_lfg_get(ctx->lfg) % (ctx->grain_w - (ctx->grain_w 
/ ctx->zoom_amount));
+    ctx->zoom_offset_h = av_lfg_get(ctx->lfg) % (ctx->grain_h - (ctx->grain_h 
/ ctx->zoom_amount));
+}
+
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
+{
+    AVFilterContext *ctx = inlink->dst;
+    GranulateContext *granulate_ctx = ctx->priv;
+    AVFilterLink *outlink = ctx->outputs[0];
+    AVFrame *out = NULL;
+    int ret;
+
+    ret = ff_inlink_make_frame_writable(inlink, &in);
+    if (ret < 0)
+        return ret;
+
+    out = in;
+
+    if (!granulate_ctx->n_grains)
+        goto filter_end;
+
+    int width = in->width;
+    int height = in->height;
+
+    if (granulate_ctx->grain_h && granulate_ctx->grain_w)
+        granulate_ctx->fullscreen = 0;
+
+    if (!granulate_ctx->fullscreen) {
+        if (granulate_ctx->grain_w > width)
+            granulate_ctx->grain_w = width;
+        if (granulate_ctx->grain_h > height)
+            granulate_ctx->grain_h = height;
+    } else {
+        granulate_ctx->grain_w = width;
+        granulate_ctx->grain_h = height;
+    }
+
+    if (!granulate_ctx->delay)
+        granulate_ctx->delay_set = 0;
+
+    if (granulate_ctx->zoom_amount == 1) {
+        granulate_ctx->zoom_set = 0;
+        granulate_ctx->zoom_offset_w = 0;
+        granulate_ctx->zoom_offset_h = 0;
+    }
+    
+    if (granulate_ctx->zoom_set && granulate_ctx->zoom_amount > 1) {
+        if (granulate_ctx->zoom_offset_w >= (granulate_ctx->grain_w - 
(granulate_ctx->grain_w / granulate_ctx->zoom_amount)) || 
granulate_ctx->zoom_offset_h >= (granulate_ctx->grain_h - 
(granulate_ctx->grain_h / granulate_ctx->zoom_amount)))
+            set_offset(granulate_ctx);
+        if (granulate_ctx->offset_time) {
+            if (!(granulate_ctx->frame_count % granulate_ctx->offset_time))
+                set_offset(granulate_ctx);
+        }
+    }
+
+    if (!granulate_ctx->zoom_set && granulate_ctx->zoom_amount > 1) {
+        granulate_ctx->zoom_set = 1;
+        set_offset(granulate_ctx);
+    }
+
+    if (granulate_ctx->buffer_size > 1) {
+
+        AVFrame *buf = granulate_ctx->fbuffer[granulate_ctx->buffer_index];
+        ret = av_frame_copy(buf, in);
+        if (ret < 0)
+            return ret;
+
+        if (granulate_ctx->delay && granulate_ctx->buffer_full && 
granulate_ctx->buffer_size > 1) {
+            if (!(granulate_ctx->frame_count % granulate_ctx->delay))
+                granulate_ctx->delay_set = 1 + (av_lfg_get(granulate_ctx->lfg) 
% (granulate_ctx->buffer_size - 1));
+        }
+        
+        if (granulate_ctx->static_grains) {
+            if (!granulate_ctx->grains_set) {
+                init_granulate_pos(granulate_ctx, width, height);
+                granulate_ctx->grains_set = 1;
+            }
+            else if (granulate_ctx->reset_time && !(granulate_ctx->frame_count 
% granulate_ctx->reset_time)) {
+                init_granulate_pos(granulate_ctx, width, height);
+                granulate_pos(granulate_ctx, out, granulate_ctx->fbuffer, 
width, height);
+            }
+            else {
+                granulate_pos(granulate_ctx, out, granulate_ctx->fbuffer, 
width, height);
+            }
+        }
+        else {
+            granulate_rand(granulate_ctx, out, granulate_ctx->fbuffer, width, 
height);
+        }
+
+        granulate_ctx->buffer_index = (granulate_ctx->buffer_index + 1) % 
granulate_ctx->buffer_size;
+        
+        if (!granulate_ctx->buffer_index)
+            granulate_ctx->buffer_full = 1;
+    }
+
+    else {
+        AVFrame *buf = granulate_ctx->fbuffer[0];
+        ret = av_frame_copy(buf, in);
+        if (ret < 0)
+            return ret;
+        granulate_in_frame(granulate_ctx, out, width, height);
+    }
+filter_end:
+    granulate_ctx->frame_count++;
+
+
+    return ff_filter_frame(outlink, out);
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    int i;
+    GranulateContext *granulate_ctx = ctx->priv;
+
+    for (i = 0; i < granulate_ctx->buffer_size; i++)
+        av_frame_free(&granulate_ctx->fbuffer[i]);
+
+    if (granulate_ctx->static_grains && granulate_ctx->n_grains)
+        av_freep(&granulate_ctx->grain_pos);
+
+    av_freep(&granulate_ctx->fbuffer);
+    av_freep(&granulate_ctx->lfg);
+}
+
+static const AVFilterPad granulate_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .config_props = config_props,
+        .filter_frame = filter_frame,
+    },
+};
+
+const FFFilter ff_vf_granulate = {
+    .p.name        = "granulate",
+    .p.description = NULL_IF_CONFIG_SMALL("Granulate past frames in current 
frame"),
+    .p.priv_class  = &granulate_class,
+    .p.flags       = 0,
+    .priv_size     = sizeof(GranulateContext),
+    .init          = init,
+    .uninit        = uninit,
+    .process_command = granulate_process_command,
+    FILTER_INPUTS(granulate_inputs),
+    FILTER_OUTPUTS(ff_video_default_filterpad),
+    FILTER_QUERY_FUNC2(query_formats),
+};
\ No newline at end of file
diff --git a/tests/fate/filter-video.mak b/tests/fate/filter-video.mak
index 6f6a3c7391..5bbfcd6bb9 100644
--- a/tests/fate/filter-video.mak
+++ b/tests/fate/filter-video.mak
@@ -554,6 +554,12 @@ FATE_FILTER-$(call ALLYES, SCALE_FILTER COLOR_FILTER 
LAVFI_INDEV WRAPPED_AVFRAME
 fate-filter-scale-zero-dim: CMD = ! run $(FFMPEG) -nostdin -hide_banner -f 
lavfi -i "color=c=red:s=3000x2:d=1" -vf 
"scale=iw/2:-2,scale=iw/3:-2,scale=iw/2:-2" -f null none
 fate-filter-scale-zero-dim: CMP = null
 
+FATE_FILTER-$(call ALLYES, TESTSRC_FILTER GRANULATE_FILTER) += 
fate-filter-granulate-basic
+fate-filter-granulate-basic: CMD = framecrc -f lavfi -i 
testsrc=size=640x480:rate=25:duration=6 -vf 
granulate=mode=0:n_grains=1:buffer=16:zoom=1:seed=1
+
+FATE_FILTER-$(call ALLYES, TESTSRC_FILTER GRANULATE_FILTER) += 
fate-filter-granulate-adv
+fate-filter-granulate-adv: CMD = framecrc -f lavfi -i 
testsrc=size=640x480:rate=25:duration=6 -vf 
granulate=mode=3:zoom=2:offset_time=1:n_grains=64:buffer=64:ghosting=1:grain_w=128:grain_h=128:var_size=1:static_grains=1:reset_time=25:delay=25:seed=1
+
 FATE_FILTER_VSYNTH_VIDEO_FILTER-$(CONFIG_VFLIP_FILTER) += fate-filter-vflip
 fate-filter-vflip: CMD = video_filter "vflip"
 
diff --git a/tests/ref/fate/filter-granulate-adv 
b/tests/ref/fate/filter-granulate-adv
new file mode 100644
index 0000000000..c24e39e6ce
--- /dev/null
+++ b/tests/ref/fate/filter-granulate-adv
@@ -0,0 +1,155 @@
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 640x480
+#sar 0: 1/1
+0,          0,          0,        1,   921600, 0xbac50a6a
+0,          1,          1,        1,   921600, 0xc159658c
+0,          2,          2,        1,   921600, 0xbdd7ab99
+0,          3,          3,        1,   921600, 0xb1513995
+0,          4,          4,        1,   921600, 0xccc684d6
+0,          5,          5,        1,   921600, 0x4471fe96
+0,          6,          6,        1,   921600, 0x3cba4b7a
+0,          7,          7,        1,   921600, 0x6c0c7f1f
+0,          8,          8,        1,   921600, 0x00bdd9d7
+0,          9,          9,        1,   921600, 0x7d824477
+0,         10,         10,        1,   921600, 0x0533704e
+0,         11,         11,        1,   921600, 0xc651e5a6
+0,         12,         12,        1,   921600, 0x3ac6886b
+0,         13,         13,        1,   921600, 0xf9b9e539
+0,         14,         14,        1,   921600, 0x3498f1a0
+0,         15,         15,        1,   921600, 0xc8a335ad
+0,         16,         16,        1,   921600, 0x1a0ac893
+0,         17,         17,        1,   921600, 0xc4ac5827
+0,         18,         18,        1,   921600, 0x4ba57569
+0,         19,         19,        1,   921600, 0x2be3c9d5
+0,         20,         20,        1,   921600, 0xb279e37c
+0,         21,         21,        1,   921600, 0x25ad25b9
+0,         22,         22,        1,   921600, 0xdbd5baee
+0,         23,         23,        1,   921600, 0x354e0dfb
+0,         24,         24,        1,   921600, 0x99afc33b
+0,         25,         25,        1,   921600, 0xe0d1b285
+0,         26,         26,        1,   921600, 0xf3d9e558
+0,         27,         27,        1,   921600, 0x48654ad6
+0,         28,         28,        1,   921600, 0xe4c3d5ff
+0,         29,         29,        1,   921600, 0x3b7e9c0c
+0,         30,         30,        1,   921600, 0x17d62a76
+0,         31,         31,        1,   921600, 0x9a58a4d0
+0,         32,         32,        1,   921600, 0xe13bc9ff
+0,         33,         33,        1,   921600, 0xcb4d4a31
+0,         34,         34,        1,   921600, 0x46f397f5
+0,         35,         35,        1,   921600, 0xe65f0ae3
+0,         36,         36,        1,   921600, 0x42e028bd
+0,         37,         37,        1,   921600, 0x3cba6a79
+0,         38,         38,        1,   921600, 0x2d4e65eb
+0,         39,         39,        1,   921600, 0x405a1b32
+0,         40,         40,        1,   921600, 0x415b8a9a
+0,         41,         41,        1,   921600, 0x625f44b4
+0,         42,         42,        1,   921600, 0xc6198216
+0,         43,         43,        1,   921600, 0x971334cf
+0,         44,         44,        1,   921600, 0x9a193cf0
+0,         45,         45,        1,   921600, 0x84eda093
+0,         46,         46,        1,   921600, 0x6245fc39
+0,         47,         47,        1,   921600, 0xdd4feb78
+0,         48,         48,        1,   921600, 0x08bc5aa7
+0,         49,         49,        1,   921600, 0x0f3951c2
+0,         50,         50,        1,   921600, 0x6c935a90
+0,         51,         51,        1,   921600, 0xcf54c00e
+0,         52,         52,        1,   921600, 0xceb3b3e9
+0,         53,         53,        1,   921600, 0x7d9f145b
+0,         54,         54,        1,   921600, 0x5dcb03bf
+0,         55,         55,        1,   921600, 0x36b56a76
+0,         56,         56,        1,   921600, 0xc2defddf
+0,         57,         57,        1,   921600, 0xd60cf1b6
+0,         58,         58,        1,   921600, 0xb194e624
+0,         59,         59,        1,   921600, 0x8fc9387a
+0,         60,         60,        1,   921600, 0x2dfadd65
+0,         61,         61,        1,   921600, 0xf5faf2fa
+0,         62,         62,        1,   921600, 0x93ecd9f8
+0,         63,         63,        1,   921600, 0x4e917479
+0,         64,         64,        1,   921600, 0xd745aaef
+0,         65,         65,        1,   921600, 0x93d956e0
+0,         66,         66,        1,   921600, 0xcb7a40aa
+0,         67,         67,        1,   921600, 0xa4d9207d
+0,         68,         68,        1,   921600, 0xb027b928
+0,         69,         69,        1,   921600, 0x66dd2d31
+0,         70,         70,        1,   921600, 0x58a6a3c0
+0,         71,         71,        1,   921600, 0x67e21853
+0,         72,         72,        1,   921600, 0x840cf537
+0,         73,         73,        1,   921600, 0xfec90c6c
+0,         74,         74,        1,   921600, 0xc51769b0
+0,         75,         75,        1,   921600, 0x88b8d75d
+0,         76,         76,        1,   921600, 0x208cdf9b
+0,         77,         77,        1,   921600, 0xaaff6457
+0,         78,         78,        1,   921600, 0x3bf17fa7
+0,         79,         79,        1,   921600, 0x90dcaa06
+0,         80,         80,        1,   921600, 0x3a092736
+0,         81,         81,        1,   921600, 0xfad6abfe
+0,         82,         82,        1,   921600, 0x344060d2
+0,         83,         83,        1,   921600, 0xf37b69cf
+0,         84,         84,        1,   921600, 0x3d8df7c0
+0,         85,         85,        1,   921600, 0x8b600b95
+0,         86,         86,        1,   921600, 0x37f7699a
+0,         87,         87,        1,   921600, 0x15067acb
+0,         88,         88,        1,   921600, 0x6313539a
+0,         89,         89,        1,   921600, 0x27e6df66
+0,         90,         90,        1,   921600, 0xe4345170
+0,         91,         91,        1,   921600, 0x97b7ba4b
+0,         92,         92,        1,   921600, 0xf370bae2
+0,         93,         93,        1,   921600, 0x5522add4
+0,         94,         94,        1,   921600, 0x02991f89
+0,         95,         95,        1,   921600, 0x934e4aed
+0,         96,         96,        1,   921600, 0x01767fd6
+0,         97,         97,        1,   921600, 0xe7fae5dd
+0,         98,         98,        1,   921600, 0x36379d54
+0,         99,         99,        1,   921600, 0x2e63cc16
+0,        100,        100,        1,   921600, 0x11a3f1c8
+0,        101,        101,        1,   921600, 0x4a4cc316
+0,        102,        102,        1,   921600, 0x4d3b5bbe
+0,        103,        103,        1,   921600, 0xf5bd91b7
+0,        104,        104,        1,   921600, 0xe993c7c9
+0,        105,        105,        1,   921600, 0x920e57b8
+0,        106,        106,        1,   921600, 0x58fccd6a
+0,        107,        107,        1,   921600, 0xcd6c5671
+0,        108,        108,        1,   921600, 0xf0546cd5
+0,        109,        109,        1,   921600, 0x1de05d9a
+0,        110,        110,        1,   921600, 0x5622ca88
+0,        111,        111,        1,   921600, 0xb136378f
+0,        112,        112,        1,   921600, 0x0af0eaf6
+0,        113,        113,        1,   921600, 0x28daad32
+0,        114,        114,        1,   921600, 0x6e5cb35b
+0,        115,        115,        1,   921600, 0x9aad4bc9
+0,        116,        116,        1,   921600, 0x2ff3d652
+0,        117,        117,        1,   921600, 0xc5d76fac
+0,        118,        118,        1,   921600, 0xe6d03d32
+0,        119,        119,        1,   921600, 0xd99d304c
+0,        120,        120,        1,   921600, 0x2970bc99
+0,        121,        121,        1,   921600, 0x6bed1923
+0,        122,        122,        1,   921600, 0xcdb86e97
+0,        123,        123,        1,   921600, 0x8da5310a
+0,        124,        124,        1,   921600, 0xf3958118
+0,        125,        125,        1,   921600, 0x57ba2c3c
+0,        126,        126,        1,   921600, 0xde4b1815
+0,        127,        127,        1,   921600, 0xa921a45c
+0,        128,        128,        1,   921600, 0xe7ae1a4d
+0,        129,        129,        1,   921600, 0x44392806
+0,        130,        130,        1,   921600, 0x6cf25d1a
+0,        131,        131,        1,   921600, 0x07f5eabb
+0,        132,        132,        1,   921600, 0x27b213e9
+0,        133,        133,        1,   921600, 0xf042d41d
+0,        134,        134,        1,   921600, 0xce766196
+0,        135,        135,        1,   921600, 0x17534ccc
+0,        136,        136,        1,   921600, 0x353f3d35
+0,        137,        137,        1,   921600, 0x1b202b61
+0,        138,        138,        1,   921600, 0x90c9c4d1
+0,        139,        139,        1,   921600, 0xcd3c572a
+0,        140,        140,        1,   921600, 0xa302585a
+0,        141,        141,        1,   921600, 0xa2f1d62f
+0,        142,        142,        1,   921600, 0x49112a57
+0,        143,        143,        1,   921600, 0x6600a53b
+0,        144,        144,        1,   921600, 0x39fa9c95
+0,        145,        145,        1,   921600, 0xe9b6f486
+0,        146,        146,        1,   921600, 0x3480c462
+0,        147,        147,        1,   921600, 0xc478244e
+0,        148,        148,        1,   921600, 0xf948b41b
+0,        149,        149,        1,   921600, 0x08c0519e
diff --git a/tests/ref/fate/filter-granulate-basic 
b/tests/ref/fate/filter-granulate-basic
new file mode 100644
index 0000000000..74cfabc93e
--- /dev/null
+++ b/tests/ref/fate/filter-granulate-basic
@@ -0,0 +1,155 @@
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 640x480
+#sar 0: 1/1
+0,          0,          0,        1,   921600, 0xbac50a6a
+0,          1,          1,        1,   921600, 0xbac50a6a
+0,          2,          2,        1,   921600, 0x9518a5c6
+0,          3,          3,        1,   921600, 0x6d772b45
+0,          4,          4,        1,   921600, 0x9518a5c6
+0,          5,          5,        1,   921600, 0x9518a5c6
+0,          6,          6,        1,   921600, 0xd0a99ac9
+0,          7,          7,        1,   921600, 0x9518a5c6
+0,          8,          8,        1,   921600, 0xbac50a6a
+0,          9,          9,        1,   921600, 0x6d772b45
+0,         10,         10,        1,   921600, 0xa123687b
+0,         11,         11,        1,   921600, 0x1f27eee6
+0,         12,         12,        1,   921600, 0x30667ded
+0,         13,         13,        1,   921600, 0xd0a99ac9
+0,         14,         14,        1,   921600, 0x6d772b45
+0,         15,         15,        1,   921600, 0xd0a99ac9
+0,         16,         16,        1,   921600, 0x30794ac7
+0,         17,         17,        1,   921600, 0x2ff6168a
+0,         18,         18,        1,   921600, 0x5a8762c3
+0,         19,         19,        1,   921600, 0x2bcd941d
+0,         20,         20,        1,   921600, 0x46ac8d78
+0,         21,         21,        1,   921600, 0x30667ded
+0,         22,         22,        1,   921600, 0x2bcd941d
+0,         23,         23,        1,   921600, 0x1f27eee6
+0,         24,         24,        1,   921600, 0x5a8762c3
+0,         25,         25,        1,   921600, 0x9ae7612a
+0,         26,         26,        1,   921600, 0x30794ac7
+0,         27,         27,        1,   921600, 0x3e6207f5
+0,         28,         28,        1,   921600, 0xd6f94db1
+0,         29,         29,        1,   921600, 0x3d8ef1de
+0,         30,         30,        1,   921600, 0xcc172e3a
+0,         31,         31,        1,   921600, 0x9ae7612a
+0,         32,         32,        1,   921600, 0x7d6f7761
+0,         33,         33,        1,   921600, 0x8a9da938
+0,         34,         34,        1,   921600, 0xd6f94db1
+0,         35,         35,        1,   921600, 0x2538c5bf
+0,         36,         36,        1,   921600, 0x9ae7612a
+0,         37,         37,        1,   921600, 0x4784ee8c
+0,         38,         38,        1,   921600, 0xcc172e3a
+0,         39,         39,        1,   921600, 0x1a5c550a
+0,         40,         40,        1,   921600, 0xcc7e3785
+0,         41,         41,        1,   921600, 0x4784ee8c
+0,         42,         42,        1,   921600, 0xa38308d1
+0,         43,         43,        1,   921600, 0xe4d620cd
+0,         44,         44,        1,   921600, 0x52097c9f
+0,         45,         45,        1,   921600, 0x1ff5d52e
+0,         46,         46,        1,   921600, 0xcc7e3785
+0,         47,         47,        1,   921600, 0xb1c3ed98
+0,         48,         48,        1,   921600, 0x3c231de3
+0,         49,         49,        1,   921600, 0x1a5c550a
+0,         50,         50,        1,   921600, 0x1a5c550a
+0,         51,         51,        1,   921600, 0x885ae94a
+0,         52,         52,        1,   921600, 0xf3e27c91
+0,         53,         53,        1,   921600, 0xb42f76fa
+0,         54,         54,        1,   921600, 0xb42f76fa
+0,         55,         55,        1,   921600, 0xf3e27c91
+0,         56,         56,        1,   921600, 0x98b16ec9
+0,         57,         57,        1,   921600, 0x885ae94a
+0,         58,         58,        1,   921600, 0x19ce639f
+0,         59,         59,        1,   921600, 0x5b7f1af9
+0,         60,         60,        1,   921600, 0xe5ce7793
+0,         61,         61,        1,   921600, 0x6bd04dee
+0,         62,         62,        1,   921600, 0x5b7f1af9
+0,         63,         63,        1,   921600, 0x4502c07d
+0,         64,         64,        1,   921600, 0x77683279
+0,         65,         65,        1,   921600, 0x5684abff
+0,         66,         66,        1,   921600, 0xe4fac171
+0,         67,         67,        1,   921600, 0xe4fac171
+0,         68,         68,        1,   921600, 0x77683279
+0,         69,         69,        1,   921600, 0xe52ad0fc
+0,         70,         70,        1,   921600, 0x480cd9db
+0,         71,         71,        1,   921600, 0x7f833571
+0,         72,         72,        1,   921600, 0x7f833571
+0,         73,         73,        1,   921600, 0xe52ad0fc
+0,         74,         74,        1,   921600, 0x9743941f
+0,         75,         75,        1,   921600, 0x77683279
+0,         76,         76,        1,   921600, 0x7f833571
+0,         77,         77,        1,   921600, 0x950e968f
+0,         78,         78,        1,   921600, 0x480cd9db
+0,         79,         79,        1,   921600, 0xe52ad0fc
+0,         80,         80,        1,   921600, 0x1547ecbc
+0,         81,         81,        1,   921600, 0x60a8acc6
+0,         82,         82,        1,   921600, 0x950e968f
+0,         83,         83,        1,   921600, 0xf877639f
+0,         84,         84,        1,   921600, 0xf695381e
+0,         85,         85,        1,   921600, 0x60a8acc6
+0,         86,         86,        1,   921600, 0x73caefa6
+0,         87,         87,        1,   921600, 0x6a4f4b79
+0,         88,         88,        1,   921600, 0x6a4f4b79
+0,         89,         89,        1,   921600, 0x30bbb204
+0,         90,         90,        1,   921600, 0xf877639f
+0,         91,         91,        1,   921600, 0xd2a2b1f6
+0,         92,         92,        1,   921600, 0xf04a6cea
+0,         93,         93,        1,   921600, 0x24150cdc
+0,         94,         94,        1,   921600, 0x24150cdc
+0,         95,         95,        1,   921600, 0xaeb0387e
+0,         96,         96,        1,   921600, 0xa5520aa2
+0,         97,         97,        1,   921600, 0x09361381
+0,         98,         98,        1,   921600, 0xf04a6cea
+0,         99,         99,        1,   921600, 0x24150cdc
+0,        100,        100,        1,   921600, 0xfb419172
+0,        101,        101,        1,   921600, 0x9bd7505e
+0,        102,        102,        1,   921600, 0xfb419172
+0,        103,        103,        1,   921600, 0x6312f4c8
+0,        104,        104,        1,   921600, 0x1254af0c
+0,        105,        105,        1,   921600, 0xceceb24d
+0,        106,        106,        1,   921600, 0x6312f4c8
+0,        107,        107,        1,   921600, 0xa5520aa2
+0,        108,        108,        1,   921600, 0xca11c462
+0,        109,        109,        1,   921600, 0x6312f4c8
+0,        110,        110,        1,   921600, 0x9bd7505e
+0,        111,        111,        1,   921600, 0xa19321e0
+0,        112,        112,        1,   921600, 0x9bd7505e
+0,        113,        113,        1,   921600, 0x11809904
+0,        114,        114,        1,   921600, 0xc9aabb17
+0,        115,        115,        1,   921600, 0xe4650504
+0,        116,        116,        1,   921600, 0xe4650504
+0,        117,        117,        1,   921600, 0x4eb30410
+0,        118,        118,        1,   921600, 0xa246760b
+0,        119,        119,        1,   921600, 0x3fbfef83
+0,        120,        120,        1,   921600, 0x4eb30410
+0,        121,        121,        1,   921600, 0xc9aabb17
+0,        122,        122,        1,   921600, 0x658d78f5
+0,        123,        123,        1,   921600, 0x442e75fd
+0,        124,        124,        1,   921600, 0x7bdb9d92
+0,        125,        125,        1,   921600, 0xe135968f
+0,        126,        126,        1,   921600, 0x7c698efd
+0,        127,        127,        1,   921600, 0x5a14d4b9
+0,        128,        128,        1,   921600, 0x7c698efd
+0,        129,        129,        1,   921600, 0x0a45148f
+0,        130,        130,        1,   921600, 0xb152d1cf
+0,        131,        131,        1,   921600, 0x69f6304f
+0,        132,        132,        1,   921600, 0x680b230c
+0,        133,        133,        1,   921600, 0x5a14d4b9
+0,        134,        134,        1,   921600, 0x7c698efd
+0,        135,        135,        1,   921600, 0x3ab8d7a3
+0,        136,        136,        1,   921600, 0x24d70630
+0,        137,        137,        1,   921600, 0xce4d3339
+0,        138,        138,        1,   921600, 0x7c698efd
+0,        139,        139,        1,   921600, 0x08122400
+0,        140,        140,        1,   921600, 0x4b83639f
+0,        141,        141,        1,   921600, 0x4b83639f
+0,        142,        142,        1,   921600, 0xd59db204
+0,        143,        143,        1,   921600, 0xf681387e
+0,        144,        144,        1,   921600, 0x67db1381
+0,        145,        145,        1,   921600, 0x01860cdc
+0,        146,        146,        1,   921600, 0x5670ac5f
+0,        147,        147,        1,   921600, 0x680b230c
+0,        148,        148,        1,   921600, 0xd93bf4c8
+0,        149,        149,        1,   921600, 0x2ef48a6f
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to