diff --git a/libavformat/Makefile b/libavformat/Makefile
index 61a1fecf6c..680030014d 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -317,7 +317,7 @@ OBJS-$(CONFIG_MATROSKA_DEMUXER)          += matroskadec.o matroska.o  \
 OBJS-$(CONFIG_MATROSKA_MUXER)            += matroskaenc.o matroska.o \
                                             av1.o avc.o hevc.o isom_tags.o \
                                             flacenc_header.o avlanguage.o \
-                                            vorbiscomment.o wv.o
+                                            vorbiscomment.o wv.o dovi_isom.o
 OBJS-$(CONFIG_MCA_DEMUXER)               += mca.o
 OBJS-$(CONFIG_MCC_DEMUXER)               += mccdec.o subtitles.o
 OBJS-$(CONFIG_MD5_MUXER)                 += hashenc.o
diff --git a/libavformat/dovi_isom.c b/libavformat/dovi_isom.c
index 2c0a49c993..747ffc8b2c 100644
--- a/libavformat/dovi_isom.c
+++ b/libavformat/dovi_isom.c
@@ -21,6 +21,8 @@
 
 #include "libavutil/dovi_meta.h"
 
+#include "libavcodec/put_bits.h"
+
 #include "avformat.h"
 #include "dovi_isom.h"
 
@@ -78,3 +80,41 @@ int ff_isom_parse_dvcc_dvvc(AVFormatContext *s, AVStream *st, const uint8_t *buf
 
     return 0;
 }
+
+int ff_isom_put_dvcc_dvvc(AVFormatContext *s, uint8_t out[ISOM_DVCC_DVVC_SIZE], uint64_t size,
+                         AVDOVIDecoderConfigurationRecord *dovi)
+{
+    PutBitContext pb;
+    init_put_bits(&pb, out, size);
+
+    if (size < ISOM_DVCC_DVVC_SIZE)
+        return AVERROR(EINVAL);
+
+    put_bits(&pb, 8, dovi->dv_version_major);
+    put_bits(&pb, 8, dovi->dv_version_minor);
+    put_bits(&pb, 7, dovi->dv_profile);
+    put_bits(&pb, 6, dovi->dv_level);
+    put_bits(&pb, 1, dovi->rpu_present_flag);
+    put_bits(&pb, 1, dovi->el_present_flag);
+    put_bits(&pb, 1, dovi->bl_present_flag);
+    put_bits(&pb, 4, dovi->dv_bl_signal_compatibility_id);
+
+    put_bits(&pb, 28, 0); /* reserved */
+    put_bits32(&pb, 0); /* reserved */
+    put_bits32(&pb, 0); /* reserved */
+    put_bits32(&pb, 0); /* reserved */
+    put_bits32(&pb, 0); /* reserved */
+    flush_put_bits(&pb);
+
+    av_log(s, AV_LOG_DEBUG, "DOVI in %s box, version: %d.%d, profile: %d, level: %d, "
+           "rpu flag: %d, el flag: %d, bl flag: %d, compatibility id: %d\n",
+           dovi->dv_profile > 7 ? "dvvC" : "dvcC",
+           dovi->dv_version_major, dovi->dv_version_minor,
+           dovi->dv_profile, dovi->dv_level,
+           dovi->rpu_present_flag,
+           dovi->el_present_flag,
+           dovi->bl_present_flag,
+           dovi->dv_bl_signal_compatibility_id);
+
+    return put_bytes_output(&pb);
+}
diff --git a/libavformat/dovi_isom.h b/libavformat/dovi_isom.h
index 4c313046a7..8ff59ef495 100644
--- a/libavformat/dovi_isom.h
+++ b/libavformat/dovi_isom.h
@@ -22,8 +22,14 @@
 #ifndef AVFORMAT_DOVI_ISOM_H
 #define AVFORMAT_DOVI_ISOM_H
 
+#include "libavutil/dovi_meta.h"
+
 #include "avformat.h"
 
+#define ISOM_DVCC_DVVC_SIZE 24
+
 int ff_isom_parse_dvcc_dvvc(AVFormatContext *s, AVStream *st, const uint8_t *buf_ptr, uint64_t size);
+int ff_isom_put_dvcc_dvvc(AVFormatContext *s, uint8_t out[ISOM_DVCC_DVVC_SIZE], uint64_t size,
+                         AVDOVIDecoderConfigurationRecord *dovi);
 
 #endif /* AVFORMAT_DOVI_ISOM_H */
diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
index 039f20988a..067474d8ed 100644
--- a/libavformat/matroskaenc.c
+++ b/libavformat/matroskaenc.c
@@ -54,6 +54,8 @@
 #include "libavcodec/xiph.h"
 #include "libavcodec/mpeg4audio.h"
 
+#include "libavformat/dovi_isom.h"
+
 /* Level 1 elements we create a SeekHead entry for:
  * Info, Tracks, Chapters, Attachments, Tags (potentially twice) and Cues */
 #define MAX_SEEKHEAD_ENTRIES 7
@@ -1115,6 +1117,41 @@ static int mkv_write_stereo_mode(AVFormatContext *s, AVIOContext *pb,
     return 0;
 }
 
+static int mkv_write_dovi(AVFormatContext *s, AVIOContext *pb, AVStream *st)
+{
+    int ret;
+    AVDOVIDecoderConfigurationRecord *dovi = (AVDOVIDecoderConfigurationRecord *)
+                                             av_stream_get_side_data(st, AV_PKT_DATA_DOVI_CONF, NULL);
+
+    if (dovi) {
+        ebml_master mapping;
+        uint8_t buf[ISOM_DVCC_DVVC_SIZE];
+        uint32_t type;
+
+        uint64_t size;
+        uint64_t expected_size = (2 + 1 + (sizeof("Dolby Vision configuration") - 1))
+                                + (2 + 1 + 4) + (2 + 1 + ISOM_DVCC_DVVC_SIZE);
+
+        if ((ret = ff_isom_put_dvcc_dvvc(s, buf, sizeof(buf), dovi)) < 0)
+            return ret;
+
+        size = ret;
+
+        if (dovi->dv_profile > 7)
+            type = MKBETAG('d', 'v', 'v', 'C');
+        else
+            type = MKBETAG('d', 'v', 'c', 'C');
+
+        mapping = start_ebml_master(pb, MATROSKA_ID_TRACKBLKADDMAPPING, expected_size);
+        put_ebml_string(pb, MATROSKA_ID_BLKADDIDNAME, "Dolby Vision configuration");
+        put_ebml_uint(pb, MATROSKA_ID_BLKADDIDTYPE, type);
+        put_ebml_binary(pb, MATROSKA_ID_BLKADDIDEXTRADATA, buf, size);
+        end_ebml_master(pb, mapping);
+    }
+
+    return 0;
+}
+
 static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv,
                            AVStream *st, mkv_track *track, AVIOContext *pb,
                            int is_default)
@@ -1314,6 +1351,12 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv,
         mkv_write_video_projection(s, pb, st);
 
         end_ebml_master(pb, subinfo);
+
+        if (mkv->mode != MODE_WEBM) {
+            if ((ret = mkv_write_dovi(s, pb, st)) < 0)
+                return ret;
+        }
+
         break;
 
     case AVMEDIA_TYPE_AUDIO:
