diff --git a/libavcodec/cbs_av1.c b/libavcodec/cbs_av1.c
index 0c958c81c2..281382c792 100644
--- a/libavcodec/cbs_av1.c
+++ b/libavcodec/cbs_av1.c
@@ -17,6 +17,7 @@
  */
 
 #include "libavutil/avassert.h"
+#include "libavutil/mastering_display_metadata.h"
 #include "libavutil/opt.h"
 #include "libavutil/pixfmt.h"
 
@@ -1301,6 +1302,10 @@ static uint64_t cbs_av1_find_metadata_type(enum CBSMetadataType type)
         enum CBSMetadataType cbs_type;
         uint64_t av1_type;
     } map[] = {
+        { CBS_METADATA_CONTENT_LIGHT_LEVEL,
+          AV1_METADATA_TYPE_HDR_CLL },
+        { CBS_METADATA_MASTERING_DISPLAY,
+          AV1_METADATA_TYPE_HDR_MDCV },
     };
 
     for (int i = 0; i < FF_ARRAY_ELEMS(map); i++) {
@@ -1337,6 +1342,13 @@ static int cbs_av1_remove_metadata(CodedBitstreamContext *ctx,
     return 0;
 }
 
+static uint32_t rescale_clip(AVRational value, uint32_t scale,
+                             uint32_t bits)
+{
+    int64_t scaled = av_rescale(scale, value.num, value.den);
+    return av_clip64(scaled, 0, INT64_C(1) << bits);
+}
+
 static int cbs_av1_insert_metadata(CodedBitstreamContext *ctx,
                                    CodedBitstreamFragment *tu,
                                    enum CBSMetadataType type,
@@ -1363,6 +1375,47 @@ static int cbs_av1_insert_metadata(CodedBitstreamContext *ctx,
     obu->obu.metadata.metadata_type = metadata_type;
 
     switch (metadata_type) {
+    case AV1_METADATA_TYPE_HDR_CLL:
+        {
+            const AVContentLightMetadata *clm = data;
+            AV1RawMetadataHDRCLL *cll =
+                &obu->obu.metadata.metadata.hdr_cll;
+
+            cll->max_cll  = av_clip_uintp2(clm->MaxCLL,  16);
+            cll->max_fall = av_clip_uintp2(clm->MaxFALL, 16);
+        }
+        break;
+    case AV1_METADATA_TYPE_HDR_MDCV:
+        {
+            const AVMasteringDisplayMetadata *mdm = data;
+            AV1RawMetadataHDRMDCV *mdcv =
+                &obu->obu.metadata.metadata.hdr_mdcv;
+
+            if (mdm->has_primaries) {
+                for (int i = 0; i < 3; i++) {
+                    mdcv->primary_chromaticity_x[i] =
+                        rescale_clip(mdm->display_primaries[i][0],
+                                     1 << 16, 16);
+                    mdcv->primary_chromaticity_y[i] =
+                        rescale_clip(mdm->display_primaries[i][1],
+                                     1 << 16, 16);
+                }
+
+                mdcv->white_point_chromaticity_x =
+                    rescale_clip(mdm->white_point[0], 1 << 16, 16);
+                mdcv->white_point_chromaticity_y =
+                    rescale_clip(mdm->white_point[1], 1 << 16, 16);
+            }
+
+            if (mdm->has_luminance) {
+                uint32_t value;
+                value = rescale_clip(mdm->max_luminance, 1 <<  8, 32);
+                mdcv->luminance_max = value;
+                mdcv->luminance_min =
+                    rescale_clip(mdm->min_luminance, 1 << 14, 32);
+            }
+        }
+        break;
     default:
         av_assert0(0);
     }
@@ -1398,6 +1451,44 @@ static int cbs_av1_extract_metadata(CodedBitstreamContext *ctx,
     obu = unit->content;
 
     switch (metadata_type) {
+    case AV1_METADATA_TYPE_HDR_CLL:
+        {
+            AVContentLightMetadata *clm = data;
+            const AV1RawMetadataHDRCLL *cll =
+                &obu->obu.metadata.metadata.hdr_cll;
+
+            clm->MaxCLL  = cll->max_cll;
+            clm->MaxFALL = cll->max_fall;
+        }
+        break;
+    case AV1_METADATA_TYPE_HDR_MDCV:
+        {
+            AVMasteringDisplayMetadata *mdm = data;
+            const AV1RawMetadataHDRMDCV *mdcv =
+                &obu->obu.metadata.metadata.hdr_mdcv;
+
+            for (int i = 0; i < 3; i++) {
+                mdm->display_primaries[i][0] =
+                    av_make_q(mdcv->primary_chromaticity_x[i], 1 << 16);
+                mdm->display_primaries[i][1] =
+                    av_make_q(mdcv->primary_chromaticity_y[i], 1 << 16);
+            }
+
+            mdm->white_point[0] =
+                av_make_q(mdcv->white_point_chromaticity_x, 1 << 16);
+            mdm->white_point[1] =
+                av_make_q(mdcv->white_point_chromaticity_y, 1 << 16);
+
+            mdm->max_luminance =
+                av_make_q(mdcv->luminance_max, 1 <<  8);
+            mdm->min_luminance =
+                av_make_q(mdcv->luminance_min, 1 << 14);
+
+            // The standard doesn't allow partially-filled MDCV blocks, so
+            // if the metadata appears it must be complete.
+            mdm->has_primaries = mdm->has_luminance = 1;
+        }
+        break;
     default:
         av_assert0(0);
     }
