[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