diff --git a/libavformat/isom.c b/libavformat/isom.c
index 300ba927c2..512ffba651 100644
--- a/libavformat/isom.c
+++ b/libavformat/isom.c
@@ -433,19 +433,32 @@ void ff_mov_write_chan(AVIOContext *pb, int64_t channel_layout)
 
 static const struct MP4TrackKindValueMapping dash_role_map[] = {
     { AV_DISPOSITION_HEARING_IMPAIRED|AV_DISPOSITION_CAPTIONS,
-        "caption" },
+        "caption",
+        KindWritingModeCMAF | KindWritingModeUnifiedOrigin },
     { AV_DISPOSITION_COMMENT,
-        "commentary" },
+        "commentary",
+        KindWritingModeCMAF | KindWritingModeUnifiedOrigin },
     { AV_DISPOSITION_VISUAL_IMPAIRED|AV_DISPOSITION_DESCRIPTIONS,
-        "description" },
+        "description",
+        KindWritingModeCMAF },
     { AV_DISPOSITION_DUB,
-        "dub" },
+        "dub",
+        KindWritingModeCMAF | KindWritingModeUnifiedOrigin },
     { AV_DISPOSITION_FORCED,
-        "forced-subtitle" },
+        "forced-subtitle",
+        KindWritingModeCMAF | KindWritingModeUnifiedOrigin },
+    { 0, NULL }
+};
+
+static const struct MP4TrackKindValueMapping html_kind_map[] = {
+    { AV_DISPOSITION_VISUAL_IMPAIRED|AV_DISPOSITION_DESCRIPTIONS,
+        "main-desc",
+         KindWritingModeUnifiedOrigin },
     { 0, NULL }
 };
 
 const struct MP4TrackKindMapping ff_mov_track_kind_table[] = {
     { "urn:mpeg:dash:role:2011", dash_role_map },
+    { "about:html-kind",         html_kind_map },
     { 0, NULL }
 };
diff --git a/libavformat/isom.h b/libavformat/isom.h
index c62fcf2bfe..f0381e9a8e 100644
--- a/libavformat/isom.h
+++ b/libavformat/isom.h
@@ -390,9 +390,16 @@ static inline enum AVCodecID ff_mov_get_lpcm_codec_id(int bps, int flags)
 #define MOV_ISMV_TTML_TAG MKTAG('d', 'f', 'x', 'p')
 #define MOV_MP4_TTML_TAG  MKTAG('s', 't', 'p', 'p')
 
+enum MP4TrackKindWritingMode {
+    KindWritingModeCMAF          = (1 << 0),
+    KindWritingModeUnifiedOrigin = (1 << 1),
+    KindWritingModeNB,
+};
+
 struct MP4TrackKindValueMapping {
     int         disposition;
     const char *value;
+    uint32_t    writing_modes;
 };
 
 struct MP4TrackKindMapping {
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 0c4a24c313..1550bdd16f 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -111,6 +111,9 @@ static const AVOption options[] = {
     { "pts", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = MOV_PRFT_SRC_PTS}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "prft"},
     { "empty_hdlr_name", "write zero-length name string in hdlr atoms within mdia and minf atoms", offsetof(MOVMuxContext, empty_hdlr_name), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
     { "movie_timescale", "set movie timescale", offsetof(MOVMuxContext, movie_timescale), AV_OPT_TYPE_INT, {.i64 = MOV_TIMESCALE}, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
+    { "kind_writing_mode", "set kind box writing mode", offsetof(MOVMuxContext, kind_writing_mode), AV_OPT_TYPE_INT, {.i64 = KindWritingModeCMAF}, KindWritingModeCMAF, KindWritingModeNB - 1, AV_OPT_FLAG_ENCODING_PARAM, "kind_writing_mode"},
+        { "cmaf", "CMAF writing mode", 0, AV_OPT_TYPE_CONST, {.i64 = KindWritingModeCMAF}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "kind_writing_mode"},
+        { "unified_origin", "Compatibility mode for Unified Origin (all DASH except for audio description)", 0, AV_OPT_TYPE_CONST, {.i64 = KindWritingModeUnifiedOrigin}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "kind_writing_mode"},
     { NULL },
 };
 
@@ -3355,7 +3358,8 @@ static int mov_write_track_kind(AVIOContext *pb, const char *scheme_uri,
     return update_size(pb, pos);
 }
 
-static int mov_write_track_kinds(AVIOContext *pb, AVStream *st)
+static int mov_write_track_kinds(AVIOContext *pb, AVStream *st,
+                                 enum MP4TrackKindWritingMode mode)
 {
     int ret = AVERROR_BUG;
 
@@ -3364,7 +3368,8 @@ static int mov_write_track_kinds(AVIOContext *pb, AVStream *st)
 
         for (int j = 0; map.value_maps[j].disposition; j++) {
             const struct MP4TrackKindValueMapping value_map = map.value_maps[j];
-            if (!(st->disposition & value_map.disposition))
+            if (!(st->disposition & value_map.disposition) ||
+                !(value_map.writing_modes & mode))
                 continue;
 
             if ((ret = mov_write_track_kind(pb, map.scheme_uri, value_map.value)) < 0)
@@ -3393,7 +3398,8 @@ static int mov_write_track_udta_tag(AVIOContext *pb, MOVMuxContext *mov,
         mov_write_track_metadata(pb_buf, st, "name", "title");
 
     if (mov->mode & MODE_MP4) {
-        if ((ret = mov_write_track_kinds(pb_buf, st)) < 0)
+        if ((ret = mov_write_track_kinds(pb_buf, st,
+                                         mov->kind_writing_mode)) < 0)
             return ret;
     }
 
diff --git a/libavformat/movenc.h b/libavformat/movenc.h
index 40077b1afe..e02a086b1f 100644
--- a/libavformat/movenc.h
+++ b/libavformat/movenc.h
@@ -25,6 +25,7 @@
 #define AVFORMAT_MOVENC_H
 
 #include "avformat.h"
+#include "isom.h"
 #include "movenccenc.h"
 #include "libavcodec/packet_internal.h"
 
@@ -242,6 +243,7 @@ typedef struct MOVMuxContext {
     MOVPrftBox write_prft;
     int empty_hdlr_name;
     int movie_timescale;
+    enum MP4TrackKindWritingMode kind_writing_mode;
 } MOVMuxContext;
 
 #define FF_MOV_FLAG_RTP_HINT              (1 <<  0)
diff --git a/tests/fate/mov.mak b/tests/fate/mov.mak
index 5ca992e181..20f085803b 100644
--- a/tests/fate/mov.mak
+++ b/tests/fate/mov.mak
@@ -145,6 +145,14 @@ FATE_MOV_FFMPEG_FFPROBE-$(call ALLYES, FILE_PROTOCOL PIPE_PROTOCOL \
                           += fate-mov-mp4-disposition-mpegts-remux
 fate-mov-mp4-disposition-mpegts-remux: CMD = transcode mpegts $(TARGET_SAMPLES)/mpegts/pmtchange.ts mp4 "-map 0:1 -map 0:2 -c copy -disposition:a:0 +hearing_impaired" "-map 0 -c copy" "" "-of json -show_entries stream_disposition:stream=index"
 
+# Same as the previous test, but the audio disposition should now be tagged
+# with the HTML5 media track identifier as opposed to the DASH identifier.
+FATE_MOV_FFMPEG_FFPROBE-$(call ALLYES, FILE_PROTOCOL PIPE_PROTOCOL \
+                                       MPEGTS_DEMUXER MOV_DEMUXER AC3_DECODER \
+                                       MP4_MUXER FRAMECRC_MUXER ) \
+                          += fate-mov-mp4-disposition-unified-origin-mpegts-remux
+fate-mov-mp4-disposition-unified-origin-mpegts-remux: CMD = transcode mpegts $(TARGET_SAMPLES)/mpegts/pmtchange.ts mp4 "-map 0:1 -map 0:2 -c copy -disposition:a:0 +hearing_impaired -kind_writing_mode unified_origin" "-map 0 -c copy" "" "-of json -show_entries stream_disposition:stream=index"
+
 FATE_SAMPLES_FFMPEG_FFPROBE += $(FATE_MOV_FFMPEG_FFPROBE-yes)
 
 fate-mov: $(FATE_MOV) $(FATE_MOV_FFPROBE) $(FATE_MOV_FASTSTART) $(FATE_MOV_FFMPEG_FFPROBE-yes)
diff --git a/tests/ref/fate/mov-mp4-disposition-unified-origin-mpegts-remux b/tests/ref/fate/mov-mp4-disposition-unified-origin-mpegts-remux
new file mode 100644
index 0000000000..0242cffb9b
--- /dev/null
+++ b/tests/ref/fate/mov-mp4-disposition-unified-origin-mpegts-remux
@@ -0,0 +1,81 @@
+99f1d34f8028c674cb10d2604a7f0117 *tests/data/fate/mov-mp4-disposition-unified-origin-mpegts-remux.mp4
+5695 tests/data/fate/mov-mp4-disposition-unified-origin-mpegts-remux.mp4
+#tb 0: 1/48000
+#media_type 0: audio
+#codec_id 0: ac3
+#sample_rate 0: 48000
+#channel_layout 0: 3
+#channel_layout_name 0: stereo
+#tb 1: 1/48000
+#media_type 1: audio
+#codec_id 1: ac3
+#sample_rate 1: 48000
+#channel_layout 1: 3
+#channel_layout_name 1: stereo
+1,          0,          0,     1536,      768, 0xa63778d4, S=1,        4
+1,       1536,       1536,     1536,      768, 0x7d577f3f
+0,       3072,       3072,     1536,      768, 0xc2867884, S=1,        4
+1,       3072,       3072,     1536,      768, 0xd86b7c8f
+0,       4608,       4608,     1536,      690, 0xa2714bf3
+1,       4608,       4608,     1536,      626, 0x09f4382f
+{
+    "programs": [
+
+    ],
+    "streams": [
+        {
+            "index": 0,
+            "disposition": {
+                "default": 1,
+                "dub": 0,
+                "original": 0,
+                "comment": 0,
+                "lyrics": 0,
+                "karaoke": 0,
+                "forced": 0,
+                "hearing_impaired": 1,
+                "visual_impaired": 0,
+                "clean_effects": 0,
+                "attached_pic": 0,
+                "timed_thumbnails": 0,
+                "captions": 1,
+                "descriptions": 0,
+                "metadata": 0,
+                "dependent": 0,
+                "still_image": 0
+            },
+            "side_data_list": [
+                {
+
+                }
+            ]
+        },
+        {
+            "index": 1,
+            "disposition": {
+                "default": 0,
+                "dub": 0,
+                "original": 0,
+                "comment": 0,
+                "lyrics": 0,
+                "karaoke": 0,
+                "forced": 0,
+                "hearing_impaired": 0,
+                "visual_impaired": 1,
+                "clean_effects": 0,
+                "attached_pic": 0,
+                "timed_thumbnails": 0,
+                "captions": 0,
+                "descriptions": 1,
+                "metadata": 0,
+                "dependent": 0,
+                "still_image": 0
+            },
+            "side_data_list": [
+                {
+
+                }
+            ]
+        }
+    ]
+}
