diff mbox

[FFmpeg-devel,v3,3/3] avcodec/aacdec: Translate PCE to avutil channel layouts

Message ID 665f9e88-1c11-c4f1-8c94-a78321ead798@gmail.com
State New
Headers show

Commit Message

pkv.stream Oct. 24, 2018, 8:03 p.m. UTC
Patch updated ; passes all FATE tests.

Comments from Alex Converse review incorporated (much thanks to him).
From 168411206bc07d6a74097c52b3b88063591787ae Mon Sep 17 00:00:00 2001
From: pkviet <pkv.stream@gmail.com>
Date: Wed, 24 Oct 2018 06:31:52 +0200
Subject: [PATCH 3/3] avcodec/aacdec: Translate pce to avutil channel_layout

This commit makes additions to the capabilties of native aac
decoder to translate pce to ffmpeg channel layouts.
For all pce generated by the native aac encoder a table is used.
For more general pce (up to 22 channels) a custom layout is built
when chan_config == 0.

Signed-off-by: pkviet <pkv.stream@gmail.com>
---
 libavcodec/aacdec_template.c | 229 ++++++++++++++++++++++++++++++++++++++++++-
 libavcodec/aacdectab.h       | 126 ++++++++++++++++++++++++
 2 files changed, 351 insertions(+), 4 deletions(-)

Comments

Alex Converse Oct. 24, 2018, 10:16 p.m. UTC | #1
On Wed, Oct 24, 2018 at 1:03 PM pkv.stream <pkv.stream@gmail.com> wrote:
>
> Patch updated ; passes all FATE tests.
>
> Comments from Alex Converse review incorporated (much thanks to him).
>

Hey,

I'm a bit concerned with the outputs this generates for low channel
count streams.

al17_44.mp4 has two front SCEs.

"ISO/IEC 13818-7:2005(E), 8.5.3.2 Explicit channel mapping using a
program_config_element" says:

Included in the PCE are “list of front channels”, using the rule
center outwards, left before right. In this list, a center channel
SCE, if any, must come first, and any other SCE’s must appear in
pairs, constituting an LR pair. If only two SCE’s are specified, this
signifies one LR stereophonic pair.

Before the patch we were getting L+R. With the patch we are getting
(FC+TFC) (0x2004).

I know of at least one commercial encoder that supports unpaired dual mono.

Do you know which spec the contains the eratta for top channels? There
doesn't seem to be much of value in Amd.6. Do you have any real
examples of a multichannel stream with a top center speaker or any
supporting documentation you could point me at?

Thanks,
Alex
pkv.stream Oct. 25, 2018, 5:47 p.m. UTC | #2
Le 25/10/2018 à 12:16 AM, Alex Converse a écrit :
> On Wed, Oct 24, 2018 at 1:03 PM pkv.stream <pkv.stream@gmail.com> wrote:
>> Patch updated ; passes all FATE tests.
>>
>> Comments from Alex Converse review incorporated (much thanks to him).
>>
> Hey,
>
> I'm a bit concerned with the outputs this generates for low channel
> count streams.
>
> al17_44.mp4 has two front SCEs.
>
> "ISO/IEC 13818-7:2005(E), 8.5.3.2 Explicit channel mapping using a
> program_config_element" says:
>
> Included in the PCE are “list of front channels”, using the rule
> center outwards, left before right. In this list, a center channel
> SCE, if any, must come first, and any other SCE’s must appear in
> pairs, constituting an LR pair. If only two SCE’s are specified, this
> signifies one LR stereophonic pair.
>
> Before the patch we were getting L+R. With the patch we are getting
> (FC+TFC) (0x2004).
>
> I know of at least one commercial encoder that supports unpaired dual mono.
>
> Do you know which spec the contains the eratta for top channels? There
> doesn't seem to be much of value in Amd.6. Do you have any real
> examples of a multichannel stream with a top center speaker or any
> supporting documentation you could point me at?

Hi Alex

I'm only aware of 22.2 (chan_config = 13).

So given that top channels are not common, I 'm thinking of these 
options in sniff_channel_order function :

- for chan_config == 0, keep only the decoding table and remove my 
custom layout code;

- OR modify the custom layout code for chan_config == 0 , along lines 
like that :

switch (num_front_channels_SCE + num_front_channels_CPE) {
         case 1:
             layout |= AV_CH_FRONT_CENTER;
             break;
         case 2:
             layout |= AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT ; // will 
set the vector al17_44.mp4 back to FL+FR
             break;
etc adding FLC, FRC, then TFC, and all the top channels
         }

- OR keep the custom layout code so that SCE +SCE can be a dual mono as 
FC + TFC.

Any option suits me; I'm agnostic on the matter.

As you wrote in your blog 
<http://spectralhole.blogspot.com/2010/08/aac-bistream-flaws-part-1-channel-model.html>, 
the channel spec is a big mess. ;-)

Best regards

pkv

> Thanks,
> Alex
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
pkv.stream Oct. 27, 2018, 5:24 p.m. UTC | #3
> Do you know which spec the contains the eratta for top channels? There
> doesn't seem to be much of value in Amd.6. Do you have any real
> examples of a multichannel stream with a top center speaker or any
> supporting documentation you could point me at?

Hi

The relevant spec is ISO/IEC 14496-3:2009/AMD.4:2013  and  can be found 
here:

http://www.doc88.com/p-7754944656492.html

pce has a new element in the comment field :

height_extension_element ;

when it is not specified, the channel elements default to normal height.

So this removes any ambiguity on the channel positions.

I'll give a shot at adding that both on encoder and decoder in the PCE 
bits next week.

Best


>
> Thanks,
> Alex
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
diff mbox

Patch

diff --git a/libavcodec/aacdec_template.c b/libavcodec/aacdec_template.c
index dce6035d67..c1748d3673 100644
--- a/libavcodec/aacdec_template.c
+++ b/libavcodec/aacdec_template.c
@@ -155,6 +155,34 @@  static av_cold int che_configure(AACContext *ac,
     return 0;
 }
 
+static uint8_t *pce_reorder_map(uint64_t layout, int channels)
+{
+    if (layout == AV_CH_LAYOUT_7POINT1_WIDE || layout == AV_CH_LAYOUT_7POINT1 ||
+        layout == AV_CH_LAYOUT_7POINT1_WIDE_BACK)
+        return aac_chan_maps[7];
+    if (layout == AV_CH_LAYOUT_6POINT1 || layout == AV_CH_LAYOUT_6POINT1_BACK ||
+        layout == AV_CH_LAYOUT_6POINT1_FRONT)
+        return aac_chan_maps[6];
+    if (layout == AV_CH_LAYOUT_5POINT1 || layout == AV_CH_LAYOUT_5POINT1_BACK)
+        return aac_chan_maps[5];
+    if (layout == AV_CH_LAYOUT_4POINT1 || layout == AV_CH_LAYOUT_5POINT0_BACK)
+        return aac_chan_maps[4];
+    if (layout == AV_CH_LAYOUT_3POINT1 || layout == AV_CH_LAYOUT_4POINT0)
+        return aac_chan_maps[3];
+    return normal_chan_maps[channels - 1];
+}
+
+static int has_CCE(AVCodecContext *avctx)
+{
+    AACContext *ac = avctx->priv_data;
+    int         i;
+    for (i = 0; i < MAX_ELEM_ID * 4; i++) {
+        if (ac->oc[1].layout_map[i][0] == TYPE_CCE)
+            return 1;
+    }
+    return 0;
+}
+
 static int frame_configure_elements(AVCodecContext *avctx)
 {
     AACContext *ac = avctx->priv_data;
@@ -180,10 +208,17 @@  static int frame_configure_elements(AVCodecContext *avctx)
     if ((ret = ff_get_buffer(avctx, ac->frame, 0)) < 0)
         return ret;
 
+    /* reorder channels in case pce table was used with LFE channel */
+    uint8_t reorder[8] = {0, 1, 2, 3, 4, 5, 6, 7};
+    if (ac->oc[1].m4ac.chan_config == 0 && avctx->channels < 9
+        && avctx->channels > 4 && !has_CCE(avctx))
+        memcpy(reorder, pce_reorder_map(ac->oc[1].channel_layout, avctx->channels),
+               avctx->channels * sizeof(uint8_t));
     /* map output channel pointers to AVFrame data */
     for (ch = 0; ch < avctx->channels; ch++) {
+        int ch_remapped = avctx->channels < 9 ? reorder[ch] : ch;
         if (ac->output_element[ch])
-            ac->output_element[ch]->ret = (INTFLOAT *)ac->frame->extended_data[ch];
+            ac->output_element[ch]->ret = (INTFLOAT *)ac->frame->extended_data[ch_remapped];
     }
 
     return 0;
@@ -257,13 +292,193 @@  static int count_paired_channels(uint8_t (*layout_map)[3], int tags, int pos,
     return num_pos_channels;
 }
 
-static uint64_t sniff_channel_order(uint8_t (*layout_map)[3], int tags)
+static int count_channels_per_type(uint8_t (*layout_map)[3], int tags, int pos, enum RawDataBlockType type)
+{
+    int num_pos_channels = 0;
+    int i;
+    for (i = 0; i < tags; i++) {
+        if (layout_map[i][2] != pos)
+            continue;
+        if (layout_map[i][0] == type) {
+            if (type == TYPE_CPE)
+                num_pos_channels += 2;
+            if (type == TYPE_SCE || type == TYPE_LFE || type == TYPE_CCE) // used to detect CCE
+                num_pos_channels += 1;
+        }
+    }
+
+    return num_pos_channels;
+}
+
+static uint64_t convert_layout_map_to_av_layout(uint8_t layout_map[MAX_ELEM_ID * 4][3])
+{
+    int i, config;
+    config = 0;
+    // look up pce table for channel layout correspondence used by native encoder and decoder
+    for (i = 1; i < PCE_LAYOUT_NBR; i++) {
+        if (memcmp(layout_map, pce_channel_layout_map[i], sizeof(uint8_t) * 3 * PCE_MAX_TAG) == 0) {
+            config = i;
+            break;
+        }
+    }
+    return pce_channel_layout_list[config];
+}
+
+static uint64_t sniff_channel_order(AACContext *ac, uint8_t (*layout_map)[3], int tags)
 {
     int i, n, total_non_cc_elements;
     struct elem_to_channel e2c_vec[4 * MAX_ELEM_ID] = { { 0 } };
     int num_front_channels, num_side_channels, num_back_channels;
+    int num_front_channels_SCE, num_front_channels_CPE, num_LFE_channels;
+    int num_side_channels_CPE, num_back_channels_SCE, num_back_channels_CPE;
+    int has_CCE_channels;
+    int channels;
     uint64_t layout;
 
+    num_front_channels_SCE = count_channels_per_type(layout_map, tags, AAC_CHANNEL_FRONT, TYPE_SCE);
+    num_back_channels_SCE  = count_channels_per_type(layout_map, tags, AAC_CHANNEL_BACK, TYPE_SCE);
+    num_front_channels_CPE = count_channels_per_type(layout_map, tags, AAC_CHANNEL_FRONT, TYPE_CPE);
+    num_side_channels_CPE  = count_channels_per_type(layout_map, tags, AAC_CHANNEL_SIDE, TYPE_CPE);
+    num_back_channels_CPE  = count_channels_per_type(layout_map, tags, AAC_CHANNEL_BACK, TYPE_CPE);
+    num_LFE_channels       = count_channels_per_type(layout_map, tags, AAC_CHANNEL_LFE, TYPE_LFE);
+    has_CCE_channels       = count_channels_per_type(layout_map, tags, AAC_CHANNEL_CC, TYPE_CCE);
+    channels = num_front_channels_SCE + num_back_channels_SCE + num_LFE_channels + num_front_channels_CPE +
+               num_side_channels_CPE + num_back_channels_CPE;
+    if (ac->oc[1].m4ac.chan_config == 0 && !has_CCE_channels) {
+        layout = 0;
+        // first use table to find layout
+        if (PCE_MAX_TAG >= tags)
+            layout = convert_layout_map_to_av_layout(layout_map);
+        if (layout > 0) {
+            char buf[64];
+            av_get_channel_layout_string(buf, sizeof(buf), -1, layout);
+            av_log(ac->avctx, AV_LOG_WARNING, "Using PCE table: channel layout "
+                   "decoded as %s (%#llx)\n", buf, layout);
+            return layout;
+        }
+        /* Build a custom layout directly from pce (CC elements are not taken into account).
+         * Channel_layout.h lists up to 24 indivudal channel masks of which we use
+         * 22, discounting AV_CH_STEREO_LEFT and AV_CH_STEREO_RIGHT
+         */
+        if (channels > 22)
+            goto fallback;
+        switch (num_LFE_channels) {
+        case 1:
+            layout |= AV_CH_LOW_FREQUENCY;
+            break;
+        case 2:
+            layout |= AV_CH_LOW_FREQUENCY | AV_CH_LOW_FREQUENCY_2;
+            break;
+        case 0:
+            break;
+        default:
+            goto fallback;
+        }
+        switch (num_front_channels_SCE) {
+        case 1:
+            layout |= AV_CH_FRONT_CENTER;
+            break;
+        case 2:
+            layout |= AV_CH_FRONT_CENTER | AV_CH_TOP_FRONT_CENTER;
+            break;
+        case 0:
+            break;
+        default:
+            goto fallback;
+        }
+        switch (num_front_channels_CPE) {
+        case 2:
+            layout |= AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT;
+            break;
+        case 4:
+            layout |= AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_LEFT_OF_CENTER |
+                      AV_CH_FRONT_RIGHT_OF_CENTER;
+            break;
+        case 6:
+            layout |= AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_LEFT_OF_CENTER |
+                      AV_CH_FRONT_RIGHT_OF_CENTER | AV_CH_TOP_FRONT_LEFT | AV_CH_TOP_FRONT_RIGHT;
+            break;
+        case 0:
+            break;
+        default:
+            goto fallback;
+        }
+        switch (num_back_channels_SCE) {
+        case 1:
+            layout |= AV_CH_BACK_CENTER;
+            break;
+        case 2:
+            layout |= AV_CH_BACK_CENTER | AV_CH_TOP_BACK_CENTER;
+            break;
+        case 0:
+            break;
+        default:
+            goto fallback;
+        }
+        switch (num_back_channels_CPE) {
+        case 2:
+            layout |= AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT;
+            break;
+        case 4:
+            layout |= AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT | AV_CH_TOP_BACK_LEFT | AV_CH_TOP_BACK_RIGHT;
+            break;
+        case 0:
+            break;
+        default:
+            goto fallback;
+        }
+        switch (num_side_channels_CPE) {
+        case 2:
+            layout |= AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT;
+            break;
+        case 4:
+            layout |= AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT | AV_CH_WIDE_LEFT | AV_CH_WIDE_RIGHT;
+            break;
+        case 6:
+            layout |= AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT | AV_CH_WIDE_LEFT | AV_CH_WIDE_RIGHT |
+                      AV_CH_SURROUND_DIRECT_LEFT | AV_CH_SURROUND_DIRECT_RIGHT;
+        case 0:
+            break;
+        default:
+            goto fallback;
+        }
+        // fix for layout 3.0 (reorders layout_map from [SCE, CPE] to [CPE, SCE])
+        // because the table has the correspondence 'Front: [CPE, SCE]' to '3.0'
+        if (layout == AV_CH_LAYOUT_SURROUND) {
+            layout_map[0][0] = TYPE_CPE;
+            layout_map[0][1] = 0;
+            layout_map[0][2] = AAC_CHANNEL_FRONT;
+            layout_map[1][0] = TYPE_SCE;
+            layout_map[1][1] = 0;
+            layout_map[1][2] = AAC_CHANNEL_FRONT;
+        }
+        /* 'fix' for layout 0xcf (this is to be consistent with previous code,
+         *  and fate-test, see 'fallback' below); this changes the channel ordering.
+         */
+        if (layout == (AV_CH_LAYOUT_3POINT1 | AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER)) {
+            layout_map[0][0] = TYPE_CPE;
+            layout_map[0][1] = 1;
+            layout_map[0][2] = AAC_CHANNEL_FRONT;
+            layout_map[1][0] = TYPE_SCE;
+            layout_map[1][1] = 0;
+            layout_map[1][2] = AAC_CHANNEL_FRONT;
+            layout_map[2][0] = TYPE_LFE;
+            layout_map[2][1] = 0;
+            layout_map[2][2] = AAC_CHANNEL_LFE;
+            layout_map[3][0] = TYPE_CPE;
+            layout_map[3][1] = 0;
+            layout_map[3][2] = AAC_CHANNEL_FRONT;
+        }
+        if (layout) {
+            char buf[64];
+            av_get_channel_layout_string(buf, sizeof(buf), -1, layout);
+            av_log(ac->avctx, AV_LOG_WARNING, "Decoding PCE: "
+                   "using custom channel layout %s (%#llx)\n", buf, layout);
+            return layout;
+        }
+    }
+
+fallback:
     if (FF_ARRAY_ELEMS(e2c_vec) < tags)
         return 0;
 
@@ -463,7 +678,7 @@  static int output_configure(AACContext *ac,
     // Try to sniff a reasonable channel order, otherwise output the
     // channels in the order the PCE declared them.
     if (avctx->request_channel_layout != AV_CH_LAYOUT_NATIVE)
-        layout = sniff_channel_order(layout_map, tags);
+        layout = sniff_channel_order(ac, layout_map, tags);
     for (i = 0; i < tags; i++) {
         int type =     layout_map[i][0];
         int id =       layout_map[i][1];
@@ -738,7 +953,7 @@  static int decode_pce(AVCodecContext *avctx, MPEG4AudioConfig *m4ac,
     int num_front, num_side, num_back, num_lfe, num_assoc_data, num_cc;
     int sampling_index;
     int comment_len;
-    int tags;
+    int i, j, tags;
 
     skip_bits(gb, 2);  // object_type
 
@@ -781,6 +996,12 @@  static int decode_pce(AVCodecContext *avctx, MPEG4AudioConfig *m4ac,
     decode_channel_map(layout_map + tags, AAC_CHANNEL_CC,    gb, num_cc);
     tags += num_cc;
 
+    /* zeroes layout_map beyond tags*/
+    for (i = tags; i < MAX_ELEM_ID * 4; i++) {
+        for (j = 0; j < 3; j++)
+                layout_map[i][j] = 0;
+    }
+
     relative_align_get_bits(gb, byte_align_ref);
 
     /* comment field, first byte is length */
diff --git a/libavcodec/aacdectab.h b/libavcodec/aacdectab.h
index baf51a74bf..17df04469c 100644
--- a/libavcodec/aacdectab.h
+++ b/libavcodec/aacdectab.h
@@ -35,6 +35,7 @@ 
 
 #include <stdint.h>
 
+#define AAC_MAX_CHANNELS 8
 static const int8_t tags_per_config[16] = { 0, 1, 1, 2, 3, 3, 4, 5, 0, 0, 0, 4, 5, 0, 5, 0 };
 
 static const uint8_t aac_channel_layout_map[16][5][3] = {
@@ -71,4 +72,129 @@  static const uint64_t aac_channel_layout[16] = {
     /* AV_CH_LAYOUT_7POINT1_TOP, */
 };
 
+#define PCE_LAYOUT_NBR 35
+#define PCE_MAX_TAG 10
+
+/* number of tags for the pce_channel_layout_map table */
+static const int8_t tags_pce_config[PCE_LAYOUT_NBR] = {0, 10, 9, 8, 7, 8, 7, 6, 6, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2,  1, 1};
+
+static const uint8_t pce_channel_layout_map[PCE_LAYOUT_NBR][10][3] = {
+    { { 0, } },
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_SCE, 1, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_FRONT }, { TYPE_CPE, 2, AAC_CHANNEL_SIDE }, { TYPE_CPE, 3, AAC_CHANNEL_SIDE }, { TYPE_CPE, 4, AAC_CHANNEL_BACK }, { TYPE_CPE, 5, AAC_CHANNEL_BACK }, { TYPE_SCE, 2, AAC_CHANNEL_BACK }, { TYPE_SCE, 3, AAC_CHANNEL_BACK }, },//AV_CH_LAYOUT_HEXADECAGONAL
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_SCE, 1, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_FRONT }, { TYPE_CPE, 2, AAC_CHANNEL_SIDE }, { TYPE_CPE, 3, AAC_CHANNEL_SIDE }, { TYPE_CPE, 4, AAC_CHANNEL_BACK }, { TYPE_CPE, 5, AAC_CHANNEL_BACK }, { TYPE_SCE, 2, AAC_CHANNEL_BACK }, },// 0x3FF37, 15ch
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_SCE, 1, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_FRONT }, { TYPE_CPE, 2, AAC_CHANNEL_SIDE }, { TYPE_CPE, 3, AAC_CHANNEL_SIDE }, { TYPE_CPE, 4, AAC_CHANNEL_BACK }, { TYPE_CPE, 5, AAC_CHANNEL_BACK }, },// 0x2FF37, 14ch
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_FRONT }, { TYPE_CPE, 2, AAC_CHANNEL_SIDE }, { TYPE_CPE, 3, AAC_CHANNEL_SIDE }, { TYPE_CPE, 4, AAC_CHANNEL_BACK }, { TYPE_CPE, 5, AAC_CHANNEL_BACK }, },// 0x17F37, 13ch
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_SCE, 1, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_FRONT }, { TYPE_CPE, 2, AAC_CHANNEL_SIDE }, { TYPE_SCE, 2, AAC_CHANNEL_SIDE }, { TYPE_CPE, 3, AAC_CHANNEL_BACK }, { TYPE_SCE, 3, AAC_CHANNEL_BACK }, },// 0x7F37, 12ch
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_SCE, 1, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_FRONT }, { TYPE_CPE, 2, AAC_CHANNEL_SIDE }, { TYPE_SCE, 2, AAC_CHANNEL_SIDE }, { TYPE_CPE, 3, AAC_CHANNEL_BACK }, },// 0x5F37, 11ch
+    { { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_FRONT }, { TYPE_CPE, 2, AAC_CHANNEL_SIDE }, { TYPE_SCE, 0, AAC_CHANNEL_SIDE }, { TYPE_CPE, 3, AAC_CHANNEL_BACK }, { TYPE_SCE, 1, AAC_CHANNEL_BACK }, },//0xFF3, 10ch
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_SCE, 1, AAC_CHANNEL_SIDE }, { TYPE_CPE, 1, AAC_CHANNEL_SIDE }, { TYPE_CPE, 2, AAC_CHANNEL_BACK }, { TYPE_SCE, 2, AAC_CHANNEL_BACK }, },//AV_CH_LAYOUT_OCTAGONAL | AV_CH_TOP_CENTER , 9 ch
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_SIDE }, { TYPE_CPE, 2, AAC_CHANNEL_BACK }, { TYPE_SCE, 1, AAC_CHANNEL_BACK }, },//AV_CH_LAYOUT_OCTAGONAL
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_SIDE }, { TYPE_CPE, 2, AAC_CHANNEL_BACK }, { TYPE_LFE, 0, AAC_CHANNEL_LFE }, },//AV_CH_LAYOUT_7POINT1
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_FRONT }, { TYPE_CPE, 2, AAC_CHANNEL_SIDE }, { TYPE_LFE, 0, AAC_CHANNEL_LFE }, },//AV_CH_LAYOUT_7POINT1_WIDE
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_FRONT }, { TYPE_CPE, 2, AAC_CHANNEL_BACK }, { TYPE_LFE, 0, AAC_CHANNEL_LFE }, },//AV_CH_LAYOUT_7POINT1_WIDE_BACK
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_SIDE }, { TYPE_SCE, 1, AAC_CHANNEL_BACK }, { TYPE_LFE, 0, AAC_CHANNEL_LFE }, },//AV_CH_LAYOUT_6POINT1
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_BACK }, { TYPE_SCE, 1, AAC_CHANNEL_BACK }, { TYPE_LFE, 0, AAC_CHANNEL_LFE }, },//AV_CH_LAYOUT_6POINT1_BACK
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_SIDE }, { TYPE_CPE, 2, AAC_CHANNEL_BACK }, },//AV_CH_LAYOUT_7POINT0
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_FRONT }, { TYPE_CPE, 2, AAC_CHANNEL_SIDE }, },//AV_CH_LAYOUT_7POINT0_FRONT
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_BACK }, { TYPE_SCE, 1, AAC_CHANNEL_BACK }, },//AV_CH_LAYOUT_HEXAGONAL
+    { { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_FRONT }, { TYPE_CPE, 2, AAC_CHANNEL_SIDE }, { TYPE_LFE, 0, AAC_CHANNEL_LFE }, },//AV_CH_LAYOUT_6POINT1_FRONT
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_SIDE }, { TYPE_SCE, 1, AAC_CHANNEL_BACK }, },//AV_CH_LAYOUT_6POINT0
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_SIDE }, { TYPE_LFE, 0, AAC_CHANNEL_LFE }, },//AV_CH_LAYOUT_5POINT1
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_BACK },  { TYPE_LFE, 0, AAC_CHANNEL_LFE }, },//AV_CH_LAYOUT_5POINT1_BACK
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_SCE, 1, AAC_CHANNEL_BACK }, { TYPE_LFE, 0, AAC_CHANNEL_LFE }, },//AV_CH_LAYOUT_4POINT1
+    { { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_FRONT }, { TYPE_CPE, 2, AAC_CHANNEL_SIDE }, },//AV_CH_LAYOUT_6POINT0_FRONT
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_SIDE }, },//AV_CH_LAYOUT_5POINT0
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_BACK }, },//AV_CH_LAYOUT_5POINT0_BACK
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_SCE, 1, AAC_CHANNEL_BACK }, },//AV_CH_LAYOUT_4POINT0
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_LFE, 0, AAC_CHANNEL_LFE }, },//AV_CH_LAYOUT_3POINT1
+    { { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_BACK }, },//AV_CH_LAYOUT_QUAD
+    { { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_SIDE }, },//AV_CH_LAYOUT_2_2
+    { { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_LFE, 0, AAC_CHANNEL_LFE }, },  //AV_CH_LAYOUT_2POINT1
+    { { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_SCE, 0, AAC_CHANNEL_BACK }, }, //AV_CH_LAYOUT_2_1
+    { { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, },//AV_CH_LAYOUT_SURROUND
+    { { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, }, //AV_CH_LAYOUT_STEREO
+    { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, }, //AV_CH_LAYOUT_MONO
+};
+
+/* list of channel layouts supported by the pce encoder /decoder table */
+static const uint64_t pce_channel_layout_list[PCE_LAYOUT_NBR] = {
+    0,
+    AV_CH_LAYOUT_HEXADECAGONAL,
+    AV_CH_LAYOUT_OCTAGONAL | AV_CH_TOP_CENTER | AV_CH_TOP_FRONT_LEFT |
+    AV_CH_TOP_FRONT_RIGHT | AV_CH_TOP_FRONT_CENTER | AV_CH_TOP_BACK_CENTER |
+    AV_CH_TOP_BACK_LEFT | AV_CH_TOP_BACK_RIGHT,
+    AV_CH_LAYOUT_OCTAGONAL | AV_CH_TOP_CENTER | AV_CH_TOP_FRONT_LEFT |
+    AV_CH_TOP_FRONT_RIGHT | AV_CH_TOP_FRONT_CENTER | AV_CH_TOP_BACK_LEFT |
+    AV_CH_TOP_BACK_RIGHT,
+    AV_CH_LAYOUT_OCTAGONAL | AV_CH_TOP_CENTER | AV_CH_TOP_FRONT_LEFT |
+    AV_CH_TOP_FRONT_RIGHT | AV_CH_TOP_FRONT_CENTER | AV_CH_TOP_BACK_CENTER,
+    AV_CH_LAYOUT_OCTAGONAL | AV_CH_TOP_CENTER | AV_CH_TOP_FRONT_LEFT |
+    AV_CH_TOP_FRONT_RIGHT | AV_CH_TOP_FRONT_CENTER,
+    AV_CH_LAYOUT_OCTAGONAL | AV_CH_TOP_CENTER | AV_CH_TOP_FRONT_LEFT | AV_CH_TOP_FRONT_RIGHT,
+    AV_CH_LAYOUT_6POINT0_FRONT | AV_CH_BACK_CENTER | AV_CH_BACK_LEFT |
+    AV_CH_BACK_RIGHT | AV_CH_TOP_CENTER,
+    AV_CH_LAYOUT_OCTAGONAL | AV_CH_TOP_CENTER,
+    AV_CH_LAYOUT_OCTAGONAL,
+    AV_CH_LAYOUT_7POINT1,
+    AV_CH_LAYOUT_7POINT1_WIDE,
+    AV_CH_LAYOUT_7POINT1_WIDE_BACK,
+    AV_CH_LAYOUT_6POINT1,
+    AV_CH_LAYOUT_6POINT1_BACK,
+    AV_CH_LAYOUT_7POINT0,
+    AV_CH_LAYOUT_7POINT0_FRONT,
+    AV_CH_LAYOUT_HEXAGONAL,
+    AV_CH_LAYOUT_6POINT1_FRONT,
+    AV_CH_LAYOUT_6POINT0,
+    AV_CH_LAYOUT_5POINT1,
+    AV_CH_LAYOUT_5POINT1_BACK,
+    AV_CH_LAYOUT_4POINT1,
+    AV_CH_LAYOUT_6POINT0_FRONT,
+    AV_CH_LAYOUT_5POINT0,
+    AV_CH_LAYOUT_5POINT0_BACK,
+    AV_CH_LAYOUT_4POINT0,
+    AV_CH_LAYOUT_3POINT1,
+    AV_CH_LAYOUT_QUAD,
+    AV_CH_LAYOUT_2_2,
+    AV_CH_LAYOUT_2POINT1,
+    AV_CH_LAYOUT_2_1,
+    AV_CH_LAYOUT_SURROUND,
+    AV_CH_LAYOUT_STEREO,
+    AV_CH_LAYOUT_MONO,
+};
+/**
+ * Table to remap channels from libavcodec's default order to AAC order.
+ */
+static const uint8_t aac_chan_maps[AAC_MAX_CHANNELS][AAC_MAX_CHANNELS] = {
+    {0},
+    {0, 1},
+    {2, 0, 1},
+    {2, 0, 1, 3},
+    {2, 0, 1, 3, 4},
+    {2, 0, 1, 4, 5, 3},
+    {2, 0, 1, 4, 5, 6, 3},
+    {2, 0, 1, 6, 7, 4, 5, 3},
+};
+
+static const uint8_t normal_chan_maps[AAC_MAX_CHANNELS][AAC_MAX_CHANNELS] = {
+    {0},
+    {0, 1},
+    {0, 1, 2},
+    {0, 1, 2, 3},
+    {0, 1, 2, 3, 4},
+    {0, 1, 2, 3, 4, 5},
+    {0, 1, 2, 3, 4, 5, 6},
+    {0, 1, 2, 3, 4, 5, 6, 7},
+};
+
+/* Supported layouts without using a PCE */
+static const int64_t aac_normal_chan_layouts[8] = {
+        AV_CH_LAYOUT_MONO,
+        AV_CH_LAYOUT_STEREO,
+        AV_CH_LAYOUT_SURROUND,
+        AV_CH_LAYOUT_4POINT0,
+        AV_CH_LAYOUT_5POINT0_BACK,
+        AV_CH_LAYOUT_5POINT1_BACK,
+        0,
+        AV_CH_LAYOUT_7POINT1,
+};
 #endif /* AVCODEC_AACDECTAB_H */