diff --git a/doc/APIchanges b/doc/APIchanges
index 256d9c7757a..f91f4fdcec4 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -2,6 +2,9 @@ The last version increases of all libraries were on 2024-03-07
 
 API changes, most recent first:
 
+2024-03-xx - xxxxxxxxxx - lavu 59.4.100 - film_grain_params.h
+  Add av_film_grain_params_select().
+
 2024-03-xx - xxxxxxxxxx - lavu 59.3.100 - film_grain_params.h
   Add AVFilmGrainParams.color_range, color_primaries, color_trc, color_space,
   width, height, subsampling_x, subsampling_y, bit_depth_luma and
diff --git a/libavutil/film_grain_params.c b/libavutil/film_grain_params.c
index 230ce8d701c..fff7252f2f5 100644
--- a/libavutil/film_grain_params.c
+++ b/libavutil/film_grain_params.c
@@ -17,6 +17,7 @@
  */
 
 #include "film_grain_params.h"
+#include "pixdesc.h"
 
 AVFilmGrainParams *av_film_grain_params_alloc(size_t *size)
 {
@@ -47,3 +48,63 @@ AVFilmGrainParams *av_film_grain_params_create_side_data(AVFrame *frame)
 
     return fgp;
 }
+
+const AVFilmGrainParams *av_film_grain_params_select(const AVFrame *frame)
+{
+    const AVFilmGrainParams *fgp, *best = NULL;
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
+    const AVFilmGrainAOMParams *aom;
+    const AVFilmGrainH274Params *h274;
+    int bit_depth_luma, bit_depth_chroma;
+    if (!desc)
+        return NULL;
+
+    /* There are no YUV formats with different bit depth per component,
+     * so just check both against the first component for simplicity */
+    bit_depth_luma = bit_depth_chroma = desc->comp[0].depth;
+
+    for (int i = 0; i < frame->nb_side_data; i++) {
+        if (frame->side_data[i]->type != AV_FRAME_DATA_FILM_GRAIN_PARAMS)
+            continue;
+        fgp = (const AVFilmGrainParams*)frame->side_data[i]->data;
+        if (fgp->width  && fgp->width  > frame->width ||
+            fgp->height && fgp->height > frame->height)
+            continue;
+
+#define CHECK(a, b, unspec)                                     \
+    do {                                                        \
+        if ((a) != (unspec) && (b) != (unspec) && (a) != (b))   \
+            continue;                                           \
+    } while (0)
+
+        CHECK(fgp->bit_depth_luma,   bit_depth_luma,         0);
+        CHECK(fgp->bit_depth_chroma, bit_depth_chroma,       0);
+        CHECK(fgp->color_range,      frame->color_range,     AVCOL_RANGE_UNSPECIFIED);
+        CHECK(fgp->color_primaries,  frame->color_primaries, AVCOL_PRI_UNSPECIFIED);
+        CHECK(fgp->color_trc,        frame->color_trc,       AVCOL_TRC_UNSPECIFIED);
+        CHECK(fgp->color_space,      frame->colorspace,      AVCOL_SPC_UNSPECIFIED);
+
+        switch (fgp->type) {
+        case AV_FILM_GRAIN_PARAMS_NONE:
+            continue;
+        case AV_FILM_GRAIN_PARAMS_AV1:
+            aom = &fgp->codec.aom;
+            /* AOM FGS needs an exact match for the chroma resolution */
+            if (fgp->subsampling_x != desc->log2_chroma_w ||
+                fgp->subsampling_y != desc->log2_chroma_h)
+                continue;
+            break;
+        case AV_FILM_GRAIN_PARAMS_H274:
+            /* H.274 FGS can be adapted to any lower chroma resolution */
+            if (fgp->subsampling_x > desc->log2_chroma_w ||
+                fgp->subsampling_y > desc->log2_chroma_h)
+                continue;
+            break;
+        }
+
+        if (!best || best->width < fgp->width || best->height < fgp->height)
+            best = fgp;
+    }
+
+    return best;
+}
diff --git a/libavutil/film_grain_params.h b/libavutil/film_grain_params.h
index a9f243351c9..ccacab88fed 100644
--- a/libavutil/film_grain_params.h
+++ b/libavutil/film_grain_params.h
@@ -308,4 +308,15 @@ AVFilmGrainParams *av_film_grain_params_alloc(size_t *size);
  */
 AVFilmGrainParams *av_film_grain_params_create_side_data(AVFrame *frame);
 
+/**
+ * Select the most appropriate film grain parameters set for the frame,
+ * taking into account the frame's format, resolution and video signal
+ * characteristics.
+ *
+ * @note, for H.274, this may select a film grain parameter set with
+ * greater chroma resolution than the frame. Users should take care to
+ * correctly adjust the chroma grain frequency to the frame.
+ */
+const AVFilmGrainParams *av_film_grain_params_select(const AVFrame *frame);
+
 #endif /* AVUTIL_FILM_GRAIN_PARAMS_H */
diff --git a/libavutil/version.h b/libavutil/version.h
index 23351316b58..5248207ec7d 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -79,7 +79,7 @@
  */
 
 #define LIBAVUTIL_VERSION_MAJOR  59
-#define LIBAVUTIL_VERSION_MINOR   3
+#define LIBAVUTIL_VERSION_MINOR   4
 #define LIBAVUTIL_VERSION_MICRO 100
 
 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
