diff --git a/libavcodec/libdav1d.c b/libavcodec/libdav1d.c
index 3af7ef4edc..1aab3da751 100644
--- a/libavcodec/libdav1d.c
+++ b/libavcodec/libdav1d.c
@@ -22,6 +22,7 @@
 #include <dav1d/dav1d.h>
 
 #include "libavutil/avassert.h"
+#include "libavutil/film_grain_params.h"
 #include "libavutil/mastering_display_metadata.h"
 #include "libavutil/imgutils.h"
 #include "libavutil/opt.h"
@@ -37,6 +38,7 @@ typedef struct Libdav1dContext {
     Dav1dContext *c;
     AVBufferPool *pool;
     int pool_size;
+    AVBufferRef *film_grain_params;
 
     Dav1dData data;
     int tile_threads;
@@ -135,6 +137,10 @@ static av_cold int libdav1d_init(AVCodecContext *c)
     s.allocator.alloc_picture_callback = libdav1d_picture_allocator;
     s.allocator.release_picture_callback = libdav1d_picture_release;
     s.frame_size_limit = c->max_pixels;
+
+    if (dav1d->apply_grain < 0 &&
+        c->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN)
+        dav1d->apply_grain = 0;
     if (dav1d->apply_grain >= 0)
         s.apply_grain = dav1d->apply_grain;
 
@@ -395,6 +401,64 @@ FF_ENABLE_DEPRECATION_WARNINGS
             break;
         }
     }
+    if ((!dav1d->apply_grain || (c->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN)) &&
+        p->frame_hdr->film_grain.present) {
+        if (p->frame_hdr->film_grain.update) {
+            AVFilmGrainParams *fgp;
+
+            av_buffer_unref(&dav1d->film_grain_params);
+            dav1d->film_grain_params = av_film_grain_params_buffer_alloc();
+            if (!dav1d->film_grain_params) {
+                res = AVERROR(ENOMEM);
+                goto fail;
+            }
+
+            fgp = (AVFilmGrainParams *)dav1d->film_grain_params->data;
+
+            fgp->type = AV_FILM_GRAM_PARAMS_AV1;
+            fgp->seed = p->frame_hdr->film_grain.data.seed;
+            fgp->num_y_points = p->frame_hdr->film_grain.data.num_y_points;
+            fgp->chroma_scaling_from_luma = p->frame_hdr->film_grain.data.chroma_scaling_from_luma;
+            fgp->scaling_shift = p->frame_hdr->film_grain.data.scaling_shift;
+            fgp->ar_coeff_lag = p->frame_hdr->film_grain.data.ar_coeff_lag;
+            fgp->ar_coeff_shift = p->frame_hdr->film_grain.data.ar_coeff_shift;
+            fgp->grain_scale_shift = p->frame_hdr->film_grain.data.grain_scale_shift;
+            fgp->overlap_flag = p->frame_hdr->film_grain.data.overlap_flag;
+            fgp->clip_to_limited_range = p->frame_hdr->film_grain.data.clip_to_restricted_range;
+
+            memcpy(&fgp->y_points, &p->frame_hdr->film_grain.data.y_points,
+                   sizeof(fgp->y_points));
+            memcpy(&fgp->num_uv_points, &p->frame_hdr->film_grain.data.num_uv_points,
+                   sizeof(fgp->num_uv_points));
+            memcpy(&fgp->uv_points, &p->frame_hdr->film_grain.data.uv_points,
+                   sizeof(fgp->uv_points));
+            memcpy(&fgp->ar_coeffs_y, &p->frame_hdr->film_grain.data.ar_coeffs_y,
+                   sizeof(fgp->ar_coeffs_y));
+            memcpy(&fgp->ar_coeffs_uv, &p->frame_hdr->film_grain.data.ar_coeffs_uv,
+                   sizeof(fgp->ar_coeffs_uv));
+            memcpy(&fgp->uv_mult, &p->frame_hdr->film_grain.data.uv_mult,
+                   sizeof(fgp->uv_mult));
+            memcpy(&fgp->uv_mult_luma, &p->frame_hdr->film_grain.data.uv_luma_mult,
+                   sizeof(fgp->uv_mult_luma));
+            memcpy(&fgp->uv_offset, &p->frame_hdr->film_grain.data.uv_offset,
+                   sizeof(fgp->uv_offset));
+        }
+
+        if (dav1d->film_grain_params) {
+            AVBufferRef *fgp_ref = av_buffer_ref(dav1d->film_grain_params);
+            if (!fgp_ref) {
+                res = AVERROR(ENOMEM);
+                goto fail;
+            }
+            if (!av_frame_new_side_data_from_buf(frame,
+                                                 AV_FRAME_DATA_FILM_GRAIN_PARAMS,
+                                                 fgp_ref)) {
+                av_buffer_unref(&fgp_ref);
+                res = AVERROR(ENOMEM);
+                goto fail;
+            }
+        }
+    }
 
     res = 0;
 fail:
@@ -408,6 +472,7 @@ static av_cold int libdav1d_close(AVCodecContext *c)
 {
     Libdav1dContext *dav1d = c->priv_data;
 
+    av_buffer_unref(&dav1d->film_grain_params);
     av_buffer_pool_uninit(&dav1d->pool);
     dav1d_data_unref(&dav1d->data);
     dav1d_close(&dav1d->c);
@@ -420,7 +485,7 @@ static av_cold int libdav1d_close(AVCodecContext *c)
 static const AVOption libdav1d_options[] = {
     { "tilethreads", "Tile threads", OFFSET(tile_threads), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, DAV1D_MAX_TILE_THREADS, VD },
     { "framethreads", "Frame threads", OFFSET(frame_threads), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, DAV1D_MAX_FRAME_THREADS, VD },
-    { "filmgrain", "Apply Film Grain", OFFSET(apply_grain), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VD },
+    { "filmgrain", "Apply Film Grain (deprecated)", OFFSET(apply_grain), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VD },
     { "oppoint",  "Select an operating point of the scalable bitstream", OFFSET(operating_point), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 31, VD },
     { "alllayers", "Output all spatial layers", OFFSET(all_layers), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VD },
     { NULL }
