diff --git a/libavcodec/libdav1d.c b/libavcodec/libdav1d.c
index 3af7ef4edc..319a9f6b47 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;
@@ -137,6 +139,8 @@ static av_cold int libdav1d_init(AVCodecContext *c)
     s.frame_size_limit = c->max_pixels;
     if (dav1d->apply_grain >= 0)
         s.apply_grain = dav1d->apply_grain;
+    else if (c->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN)
+        s.apply_grain = 0;
 
     s.all_layers = dav1d->all_layers;
     if (dav1d->operating_point >= 0)
@@ -162,6 +166,7 @@ static void libdav1d_flush(AVCodecContext *c)
 {
     Libdav1dContext *dav1d = c->priv_data;
 
+    av_buffer_unref(&dav1d->film_grain_params);
     dav1d_data_unref(&dav1d->data);
     dav1d_flush(dav1d->c);
 }
@@ -395,6 +400,80 @@ FF_ENABLE_DEPRECATION_WARNINGS
             break;
         }
     }
+    if (p->frame_hdr->film_grain.present && (!dav1d->apply_grain ||
+        (c->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN))) {
+        AVFilmGrainParams *fgp;
+        if (p->frame_hdr->film_grain.update) {
+            size_t fgp_size;
+            fgp = av_film_grain_params_alloc(&fgp_size);
+            if (!fgp) {
+                res = AVERROR(ENOMEM);
+                goto fail;
+            }
+
+            AVBufferRef *buf = av_buffer_create((uint8_t *)fgp, fgp_size,
+                                                av_buffer_default_free, NULL, 0);
+            if (!buf) {
+                res = AVERROR(ENOMEM);
+                goto fail;
+            }
+
+            av_buffer_unref(&dav1d->film_grain_params);
+            dav1d->film_grain_params = buf;
+
+            fgp->type = AV_FILM_GRAM_PARAMS_AV1;
+            fgp->seed = p->frame_hdr->film_grain.data.seed;
+            fgp->limit_output_range = p->frame_hdr->film_grain.data.clip_to_restricted_range;
+            fgp->codec.aom.num_y_points = p->frame_hdr->film_grain.data.num_y_points;
+            fgp->codec.aom.chroma_scaling_from_luma = p->frame_hdr->film_grain.data.chroma_scaling_from_luma;
+            fgp->codec.aom.scaling_shift = p->frame_hdr->film_grain.data.scaling_shift;
+            fgp->codec.aom.ar_coeff_lag = p->frame_hdr->film_grain.data.ar_coeff_lag;
+            fgp->codec.aom.ar_coeff_shift = p->frame_hdr->film_grain.data.ar_coeff_shift;
+            fgp->codec.aom.grain_scale_shift = p->frame_hdr->film_grain.data.grain_scale_shift;
+            fgp->codec.aom.overlap_flag = p->frame_hdr->film_grain.data.overlap_flag;
+
+            memcpy(&fgp->codec.aom.y_points, &p->frame_hdr->film_grain.data.y_points,
+                   sizeof(fgp->codec.aom.y_points));
+            memcpy(&fgp->codec.aom.num_uv_points, &p->frame_hdr->film_grain.data.num_uv_points,
+                   sizeof(fgp->codec.aom.num_uv_points));
+            memcpy(&fgp->codec.aom.uv_points, &p->frame_hdr->film_grain.data.uv_points,
+                   sizeof(fgp->codec.aom.uv_points));
+            memcpy(&fgp->codec.aom.ar_coeffs_y, &p->frame_hdr->film_grain.data.ar_coeffs_y,
+                   sizeof(fgp->codec.aom.ar_coeffs_y));
+            memcpy(&fgp->codec.aom.ar_coeffs_uv, &p->frame_hdr->film_grain.data.ar_coeffs_uv,
+                   sizeof(fgp->codec.aom.ar_coeffs_uv));
+            memcpy(&fgp->codec.aom.uv_mult, &p->frame_hdr->film_grain.data.uv_mult,
+                   sizeof(fgp->codec.aom.uv_mult));
+            memcpy(&fgp->codec.aom.uv_mult_luma, &p->frame_hdr->film_grain.data.uv_luma_mult,
+                   sizeof(fgp->codec.aom.uv_mult_luma));
+            memcpy(&fgp->codec.aom.uv_offset, &p->frame_hdr->film_grain.data.uv_offset,
+                   sizeof(fgp->codec.aom.uv_offset));
+        } else if (dav1d->film_grain_params) {
+            if (av_buffer_make_writable(&dav1d->film_grain_params) < 0) {
+                res = AVERROR(ENOMEM);
+                goto fail;
+            }
+
+            /* The RNG seed is always updated even if update == 0 */
+            fgp = (AVFilmGrainParams *)dav1d->film_grain_params->data;
+            fgp->seed = p->frame_hdr->film_grain.data.seed;
+        }
+
+        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 +487,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 +500,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", OFFSET(apply_grain), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VD | AV_OPT_FLAG_DEPRECATED },
     { "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 }
