diff --git a/libavformat/mov.c b/libavformat/mov.c
index b125343f84..1db869aa2e 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -940,6 +940,82 @@ static int mov_read_chan(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     return 0;
 }
 
+static int mov_read_chnl(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+    int64_t end = av_sat_add64(avio_tell(pb), atom.size);
+    int stream_structure;
+    int ret = 0;
+    AVStream *st;
+
+    if (c->fc->nb_streams < 1)
+        return 0;
+    st = c->fc->streams[c->fc->nb_streams-1];
+
+    /* skip version and flags */
+    avio_skip(pb, 4);
+
+    stream_structure = avio_r8(pb);
+
+    // stream carries channels
+    if (stream_structure & 1) {
+        int layout = avio_r8(pb);
+
+        av_log(c->fc, AV_LOG_TRACE, "'chnl' layout %d\n", layout);
+        if (!layout) {
+            uint8_t positions[64] = {};
+            int enable = 1;
+
+            for (int i = 0; i < st->codecpar->ch_layout.nb_channels; i++) {
+                int speaker_pos = avio_r8(pb);
+
+                av_log(c->fc, AV_LOG_TRACE, "speaker_position %d\n", speaker_pos);
+                if (speaker_pos == 126) { // explicit position
+                    int16_t azimuth = avio_rb16(pb);
+                    int8_t elevation = avio_r8(pb);
+
+                    av_log(c->fc, AV_LOG_TRACE, "azimuth %d, elevation %d\n",
+                           azimuth, elevation);
+                    // Don't support explicit position
+                    enable = 0;
+                } else if (i < FF_ARRAY_ELEMS(positions)) {
+                    positions[i] = speaker_pos;
+                } else {
+                    // number of channel out of our supported range
+                    enable = 0;
+                }
+            }
+
+            if (enable) {
+                ret = ff_mov_get_layout_from_channel_positions(positions,
+                        st->codecpar->ch_layout.nb_channels,
+                        &st->codecpar->ch_layout);
+                if (ret) {
+                    av_log(c->fc, AV_LOG_WARNING, "unsupported speaker positions\n");
+                    ret = 0;
+                }
+            }
+        } else {
+            uint64_t omitted_channel_map = avio_rb64(pb);
+
+            if (omitted_channel_map) {
+                avpriv_request_sample(c->fc, "omitted_channel_map 0x%" PRIx64 " != 0",
+                                      omitted_channel_map);
+                return AVERROR_PATCHWELCOME;
+            }
+            ff_mov_get_channel_layout_from_config(layout, &st->codecpar->ch_layout);
+        }
+    }
+
+    // stream carries objects
+    if (stream_structure & 2) {
+        int obj_count = avio_r8(pb);
+        av_log(c->fc, AV_LOG_TRACE, "'chnl' with object_count %d\n", obj_count);
+    }
+
+    avio_seek(pb, end, SEEK_SET);
+    return ret;
+}
+
 static int mov_read_wfex(MOVContext *c, AVIOContext *pb, MOVAtom atom)
 {
     AVStream *st;
@@ -7784,7 +7860,8 @@ static const MOVParseTableEntry mov_default_parse_table[] = {
 { MKTAG('w','i','d','e'), mov_read_wide }, /* place holder */
 { MKTAG('w','f','e','x'), mov_read_wfex },
 { MKTAG('c','m','o','v'), mov_read_cmov },
-{ MKTAG('c','h','a','n'), mov_read_chan }, /* channel layout */
+{ MKTAG('c','h','a','n'), mov_read_chan }, /* channel layout from quicktime */
+{ MKTAG('c','h','n','l'), mov_read_chnl }, /* channel layout from ISO-14496-12 */
 { MKTAG('d','v','c','1'), mov_read_dvc1 },
 { MKTAG('s','g','p','d'), mov_read_sgpd },
 { MKTAG('s','b','g','p'), mov_read_sbgp },
diff --git a/libavformat/mov_chan.c b/libavformat/mov_chan.c
index f66bf0df7f..10ebcdc08f 100644
--- a/libavformat/mov_chan.c
+++ b/libavformat/mov_chan.c
@@ -551,3 +551,268 @@ int ff_mov_read_chan(AVFormatContext *s, AVIOContext *pb, AVStream *st,
 
     return 0;
 }
+
+/* ISO/IEC 23001-8, 8.2 */
+static const AVChannelLayout iso_channel_configuration[] = {
+    // 0: any setup
+    {},
+
+    // 1: centre front
+    AV_CHANNEL_LAYOUT_MONO,
+
+    // 2: left front, right front
+    AV_CHANNEL_LAYOUT_STEREO,
+
+    // 3: centre front, left front, right front
+    AV_CHANNEL_LAYOUT_SURROUND,
+
+    // 4: centre front, left front, right front, rear centre
+    AV_CHANNEL_LAYOUT_4POINT0,
+
+    // 5: centre front, left front, right front, left surround, right surround
+    AV_CHANNEL_LAYOUT_5POINT0,
+
+    // 6: 5 + LFE
+    AV_CHANNEL_LAYOUT_5POINT1,
+
+    // 7: centre front, left front centre, right front centre,
+    // left front, right front, left surround, right surround, LFE
+    AV_CHANNEL_LAYOUT_7POINT1_WIDE,
+
+    // 8: channel1, channel2
+    AV_CHANNEL_LAYOUT_STEREO_DOWNMIX,
+
+    // 9: left front, right front, rear centre
+    AV_CHANNEL_LAYOUT_2_1,
+
+    // 10: left front, right front, left surround, right surround
+    AV_CHANNEL_LAYOUT_2_2,
+
+    // 11: centre front, left front, right front, left surround, right surround, rear centre, LFE
+    AV_CHANNEL_LAYOUT_6POINT1,
+
+    // 12: centre front, left front, right front
+    // left surround, right surround
+    // rear surround left, rear surround right
+    // LFE
+    AV_CHANNEL_LAYOUT_7POINT1,
+
+    // 13:
+    AV_CHANNEL_LAYOUT_22POINT2,
+
+    // 14:
+    AV_CHANNEL_LAYOUT_7POINT1_TOP_BACK,
+
+    // TODO: 15 - 20
+};
+
+/* ISO/IEC 23001-8, table 8 */
+static const enum AVChannel iso_channel_position[] = {
+    // 0: left front
+    AV_CHAN_FRONT_LEFT,
+
+    // 1: right front
+    AV_CHAN_FRONT_RIGHT,
+
+    // 2: centre front
+    AV_CHAN_FRONT_CENTER,
+
+    // 3: low frequence enhancement
+    AV_CHAN_LOW_FREQUENCY,
+
+    // 4: left surround
+    // TODO
+    AV_CHAN_NONE,
+
+    // 5: right surround
+    // TODO
+    AV_CHAN_NONE,
+
+    // 6: left front centre
+    AV_CHAN_FRONT_LEFT_OF_CENTER,
+
+    // 7: right front centre
+    AV_CHAN_FRONT_RIGHT_OF_CENTER,
+
+    // 8: rear surround left
+    AV_CHAN_BACK_LEFT,
+
+    // 9: rear surround right
+    AV_CHAN_BACK_RIGHT,
+
+    // 10: rear centre
+    AV_CHAN_BACK_CENTER,
+
+    // 11: left surround direct
+    AV_CHAN_SURROUND_DIRECT_LEFT,
+
+    // 12: right surround direct
+    AV_CHAN_SURROUND_DIRECT_RIGHT,
+
+    // 13: left side surround
+    AV_CHAN_SIDE_LEFT,
+
+    // 14: right side surround
+    AV_CHAN_SIDE_RIGHT,
+
+    // 15: left wide front
+    AV_CHAN_WIDE_LEFT,
+
+    // 16: right wide front
+    AV_CHAN_WIDE_RIGHT,
+
+    // 17: left front vertical height
+    AV_CHAN_TOP_FRONT_LEFT,
+
+    // 18: right front vertical height
+    AV_CHAN_TOP_FRONT_RIGHT,
+
+    // 19: centre front vertical height
+    AV_CHAN_TOP_FRONT_CENTER,
+
+    // 20: left surround vertical height rear
+    AV_CHAN_TOP_BACK_LEFT,
+
+    // 21: right surround vertical height rear
+    AV_CHAN_TOP_BACK_RIGHT,
+
+    // 22: centre vertical height rear
+    AV_CHAN_TOP_BACK_CENTER,
+
+    // 23: left vertical height side surround
+    AV_CHAN_TOP_SIDE_LEFT,
+
+    // 24: right vertical height side surround
+    AV_CHAN_TOP_SIDE_RIGHT,
+
+    // 25: top centre surround
+    AV_CHAN_TOP_CENTER,
+
+    // 26: low frequency enhancement 2
+    AV_CHAN_LOW_FREQUENCY_2,
+
+    // 27: left front vertical bottom
+    AV_CHAN_BOTTOM_FRONT_LEFT,
+
+    // 28: right front vertical bottom
+    AV_CHAN_BOTTOM_FRONT_RIGHT,
+
+    // 29: centre front vertical bottom
+    AV_CHAN_BOTTOM_FRONT_CENTER,
+
+    // 30: left vertical height surround
+    // TODO
+    AV_CHAN_NONE,
+
+    // 31: right vertical height surround
+    // TODO
+    AV_CHAN_NONE,
+
+    // 32, 33, 34, 35, reserved
+    AV_CHAN_NONE,
+    AV_CHAN_NONE,
+    AV_CHAN_NONE,
+    AV_CHAN_NONE,
+
+    // 36: low frequency enhancement 3
+    AV_CHAN_NONE,
+
+    // 37: left edge of screen
+    AV_CHAN_NONE,
+    // 38: right edge of screen
+    AV_CHAN_NONE,
+    // 39: half-way between centre of screen and left edge of screen
+    AV_CHAN_NONE,
+    // 40: half-way between centre of screen and right edge of screen
+    AV_CHAN_NONE,
+
+    // 41: left back surround
+    AV_CHAN_NONE,
+
+    // 42: right back surround
+    AV_CHAN_NONE,
+
+    // 43 - 125: reserved
+    // 126: explicit position
+    // 127: unknown /undefined
+};
+
+int ff_mov_get_channel_config_from_layout(const AVChannelLayout *layout, int *config)
+{
+    // Set default value which means any setup in 23001-8
+    *config = 0;
+    for (int i = 0; i < FF_ARRAY_ELEMS(iso_channel_configuration); i++) {
+        if (!av_channel_layout_compare(layout, iso_channel_configuration + i)) {
+            *config = i;
+            break;
+        }
+    }
+
+    return 0;
+}
+
+int ff_mov_get_channel_layout_from_config(int config, AVChannelLayout *layout)
+{
+    if (config > 0 && config < FF_ARRAY_ELEMS(iso_channel_configuration)) {
+        *layout = iso_channel_configuration[config];
+        return 0;
+    }
+
+    return -1;
+}
+
+int ff_mov_get_channel_positions_from_layout(const AVChannelLayout *layout,
+                                             uint8_t *position, int position_num)
+{
+    enum AVChannel channel;
+
+    if (position_num < layout->nb_channels)
+        return AVERROR(EINVAL);
+
+    if (layout->order != AV_CHANNEL_ORDER_NATIVE)
+        return AVERROR_PATCHWELCOME;
+
+    for (int i = 0; i < layout->nb_channels; i++) {
+        position[i] = 127;
+        channel = av_channel_layout_channel_from_index(layout, i);
+        if (channel == AV_CHAN_NONE)
+            return AVERROR(EINVAL);
+
+        for (int j = 0; j < FF_ARRAY_ELEMS(iso_channel_position); j++) {
+            if (iso_channel_position[j] == channel) {
+                position[i] = j;
+                break;
+            }
+        }
+        if (position[i] == 127)
+            return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
+
+int ff_mov_get_layout_from_channel_positions(const uint8_t *position, int position_num,
+                                             AVChannelLayout *layout)
+{
+    AVChannelLayout tmp = {
+        .order = AV_CHANNEL_ORDER_NATIVE,
+        .nb_channels = position_num,
+    };
+    enum AVChannel channel;
+
+    for (int i = 0; i < position_num; i++) {
+        if (position[i] >= FF_ARRAY_ELEMS(iso_channel_position))
+            return AVERROR_PATCHWELCOME;
+
+        channel = iso_channel_position[position[i]];
+        // unsupported layout
+        if (channel == AV_CHAN_NONE)
+            return AVERROR_PATCHWELCOME;
+
+        tmp.u.mask |= 1ULL << channel;
+    }
+
+    *layout = tmp;
+
+    return 0;
+}
diff --git a/libavformat/mov_chan.h b/libavformat/mov_chan.h
index 93d9878798..8c807798ab 100644
--- a/libavformat/mov_chan.h
+++ b/libavformat/mov_chan.h
@@ -163,4 +163,30 @@ int ff_mov_get_channel_layout_tag(const AVCodecParameters *par,
 int ff_mov_read_chan(AVFormatContext *s, AVIOContext *pb, AVStream *st,
                      int64_t size);
 
+/**
+ * Get ISO/IEC 23001-8 ChannelConfiguration from AVChannelLayout.
+ *
+ */
+int ff_mov_get_channel_config_from_layout(const AVChannelLayout *layout, int *config);
+
+/**
+ * Get AVChannelLayout from ISO/IEC 23001-8 ChannelConfiguration.
+ *
+ * @return 0 for success, -1 for doesn't match, layout is untouched on failure
+ */
+
+int ff_mov_get_channel_layout_from_config(int config, AVChannelLayout *layout);
+
+/**
+ * Get ISO/IEC 23001-8 OutputChannelPosition from AVChannelLayout.
+ */
+int ff_mov_get_channel_positions_from_layout(const AVChannelLayout *layout,
+                                             uint8_t *position, int position_num);
+
+/**
+ * Get AVChannelLayout from ISO/IEC 23001-8 OutputChannelPosition.
+ */
+int ff_mov_get_layout_from_channel_positions(const uint8_t *position, int position_num,
+                                             AVChannelLayout *layout);
+
 #endif /* AVFORMAT_MOV_CHAN_H */
