[FFmpeg-devel] [PATCH v2 6/8] avformat/mov: parse ISO-14496-12 ChannelLayout

Zhao Zhili quinkblack at foxmail.com
Fri Feb 24 20:28:47 EET 2023


From: Zhao Zhili <zhilizhao at tencent.com>

Signed-off-by: Zhao Zhili <zhilizhao at tencent.com>
---
 libavformat/mov.c      |  85 +++++++++++-
 libavformat/mov_chan.c | 296 +++++++++++++++++++++++++++++++++++++++++
 libavformat/mov_chan.h |  26 ++++
 3 files changed, 406 insertions(+), 1 deletion(-)

diff --git a/libavformat/mov.c b/libavformat/mov.c
index e75cf2f4b7..f8b424cd7f 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -940,6 +940,88 @@ 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 version, flags;
+    int ret = 0;
+    AVStream *st;
+
+    if (c->fc->nb_streams < 1)
+        return 0;
+    st = c->fc->streams[c->fc->nb_streams-1];
+
+    version = avio_r8(pb);
+    flags   = avio_rb24(pb);
+    if (version != 0 || flags != 0) {
+        av_log(c->fc, AV_LOG_ERROR,
+               "Unsupported 'chnl' box with version %d, flags: %#x",
+               version, flags);
+        return AVERROR_INVALIDDATA;
+    }
+
+    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 = av_malloc(st->codecpar->ch_layout.nb_channels);
+
+            if (!positions)
+                return AVERROR(ENOMEM);
+            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
+                    avpriv_request_sample(c->fc, "explicit position");
+                    av_freep(&positions);
+                    return AVERROR_PATCHWELCOME;
+                } else {
+                    positions[i] = speaker_pos;
+                }
+            }
+
+            ret = ff_mov_get_layout_from_channel_positions(positions,
+                    st->codecpar->ch_layout.nb_channels,
+                    &st->codecpar->ch_layout);
+            av_freep(&positions);
+            if (ret) {
+                av_log(c->fc, AV_LOG_ERROR,
+                        "get channel layout from speaker positions failed, %s\n",
+                        av_err2str(ret));
+                return ret;
+            }
+        } 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);
+    }
+
+    if (avio_tell(pb) != end) {
+        av_log(c->fc, AV_LOG_WARNING, "skip %" PRId64 " bytes of unknown data inside chnl\n",
+                end - avio_tell(pb));
+        avio_seek(pb, end, SEEK_SET);
+    }
+    return ret;
+}
+
 static int mov_read_wfex(MOVContext *c, AVIOContext *pb, MOVAtom atom)
 {
     AVStream *st;
@@ -7815,7 +7897,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..df17976e59 100644
--- a/libavformat/mov_chan.c
+++ b/libavformat/mov_chan.c
@@ -551,3 +551,299 @@ 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)) {
+        av_channel_layout_copy(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);
+
+    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)
+{
+    int ret;
+    enum AVChannel channel;
+
+    av_channel_layout_uninit(layout);
+
+    if (position_num <= 63) {
+        layout->order = AV_CHANNEL_ORDER_NATIVE;
+        layout->nb_channels = position_num;
+        for (int i = 0; i < position_num; i++) {
+            if (position[i] >= FF_ARRAY_ELEMS(iso_channel_position)) {
+                ret = AVERROR_PATCHWELCOME;
+                goto error;
+            }
+
+            channel = iso_channel_position[position[i]];
+            // unsupported layout
+            if (channel == AV_CHAN_NONE) {
+                ret = AVERROR_PATCHWELCOME;
+                goto error;
+            }
+
+            layout->u.mask |= 1ULL << channel;
+        }
+    } else {
+        layout->order = AV_CHANNEL_ORDER_CUSTOM;
+        layout->nb_channels = position_num;
+        layout->u.map = av_calloc(position_num, sizeof(*layout->u.map));
+        if (!layout->u.map) {
+            ret = AVERROR(ENOMEM);
+            goto error;
+        }
+
+        for (int i = 0; i < position_num; i++) {
+            if (position[i] >= FF_ARRAY_ELEMS(iso_channel_position)) {
+                ret = AVERROR_PATCHWELCOME;
+                goto error;
+            }
+
+            channel = iso_channel_position[position[i]];
+            // unsupported layout
+            if (channel == AV_CHAN_NONE) {
+                ret = AVERROR_PATCHWELCOME;
+                goto error;
+            }
+
+            layout->u.map[i].id = channel;
+        }
+    }
+
+
+    return 0;
+
+error:
+    av_channel_layout_uninit(layout);
+    return ret;
+}
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 */
-- 
2.34.1



More information about the ffmpeg-devel mailing list