aboutsummaryrefslogtreecommitdiffstats
path: root/libavcodec/aacdec.c
diff options
context:
space:
mode:
authorAlex Converse <alex.converse@gmail.com>2012-01-31 15:54:21 -0800
committerAlex Converse <alex.converse@gmail.com>2012-02-09 13:33:06 -0800
commit37bed6ff3f7f48e25d1e0c3efdf8cbb90bcf9514 (patch)
tree0fdcb85e113913cb04250f89f7e0a634b386f310 /libavcodec/aacdec.c
parente7d4a2a721ae863488f6a3814f9dcdbc6e8f5166 (diff)
downloadffmpeg-37bed6ff3f7f48e25d1e0c3efdf8cbb90bcf9514.tar.gz
aacdec: Try to sniff a reasonable channel layout for PCE based configurations.
This changes the output order of multichannel PCE based streams.
Diffstat (limited to 'libavcodec/aacdec.c')
-rw-r--r--libavcodec/aacdec.c368
1 files changed, 282 insertions, 86 deletions
diff --git a/libavcodec/aacdec.c b/libavcodec/aacdec.c
index b1de5a571e..bef17ab68d 100644
--- a/libavcodec/aacdec.c
+++ b/libavcodec/aacdec.c
@@ -163,15 +163,14 @@ static ChannelElement *get_che(AACContext *ac, int type, int elem_id)
}
}
-static int count_channels(enum ChannelPosition che_pos[4][MAX_ELEM_ID])
+static int count_channels(uint8_t (*layout)[3], int tags)
{
- int i, type, sum = 0;
- for (i = 0; i < MAX_ELEM_ID; i++) {
- for (type = 0; type < 4; type++) {
- sum += (1 + (type == TYPE_CPE)) *
- (che_pos[type][i] != AAC_CHANNEL_OFF &&
- che_pos[type][i] != AAC_CHANNEL_CC);
- }
+ int i, sum = 0;
+ for (i = 0; i < tags; i++) {
+ int syn_ele = layout[i][0];
+ int pos = layout[i][2];
+ sum += (1 + (syn_ele == TYPE_CPE)) *
+ (pos != AAC_CHANNEL_OFF && pos != AAC_CHANNEL_CC);
}
return sum;
}
@@ -213,30 +212,217 @@ static av_cold int che_configure(AACContext *ac,
return 0;
}
+struct elem_to_channel {
+ uint64_t av_position;
+ uint8_t syn_ele;
+ uint8_t elem_id;
+ uint8_t aac_position;
+};
+
+static int assign_pair(struct elem_to_channel e2c_vec[MAX_ELEM_ID],
+ uint8_t (*layout_map)[3], int offset, int tags, uint64_t left,
+ uint64_t right, int pos)
+{
+ if (layout_map[offset][0] == TYPE_CPE) {
+ e2c_vec[offset] = (struct elem_to_channel) {
+ .av_position = left | right, .syn_ele = TYPE_CPE,
+ .elem_id = layout_map[offset ][1], .aac_position = pos };
+ return 1;
+ } else {
+ e2c_vec[offset] = (struct elem_to_channel) {
+ .av_position = left, .syn_ele = TYPE_SCE,
+ .elem_id = layout_map[offset ][1], .aac_position = pos };
+ e2c_vec[offset + 1] = (struct elem_to_channel) {
+ .av_position = right, .syn_ele = TYPE_SCE,
+ .elem_id = layout_map[offset + 1][1], .aac_position = pos };
+ return 2;
+ }
+}
+
+static int count_paired_channels(uint8_t (*layout_map)[3], int tags, int pos, int *current) {
+ int num_pos_channels = 0;
+ int first_cpe = 0;
+ int sce_parity = 0;
+ int i;
+ for (i = *current; i < tags; i++) {
+ if (layout_map[i][2] != pos)
+ break;
+ if (layout_map[i][0] == TYPE_CPE) {
+ if (sce_parity) {
+ if (pos == AAC_CHANNEL_FRONT || !first_cpe) {
+ sce_parity = 0;
+ } else {
+ return -1;
+ }
+ }
+ num_pos_channels += 2;
+ first_cpe = 1;
+ } else {
+ num_pos_channels++;
+ sce_parity ^= 1;
+ }
+ }
+ if (sce_parity &&
+ ((pos == AAC_CHANNEL_FRONT && first_cpe) || pos == AAC_CHANNEL_SIDE))
+ return -1;
+ *current = i;
+ return num_pos_channels;
+}
+
+static uint64_t sniff_channel_order(uint8_t (*layout_map)[3], int tags)
+{
+ int i, n, total_non_cc_elements;
+ struct elem_to_channel e2c_vec[MAX_ELEM_ID] = {{ 0 }};
+ int num_front_channels, num_side_channels, num_back_channels;
+ uint64_t layout;
+
+ i = 0;
+ num_front_channels =
+ count_paired_channels(layout_map, tags, AAC_CHANNEL_FRONT, &i);
+ if (num_front_channels < 0)
+ return 0;
+ num_side_channels =
+ count_paired_channels(layout_map, tags, AAC_CHANNEL_SIDE, &i);
+ if (num_side_channels < 0)
+ return 0;
+ num_back_channels =
+ count_paired_channels(layout_map, tags, AAC_CHANNEL_BACK, &i);
+ if (num_back_channels < 0)
+ return 0;
+
+ i = 0;
+ if (num_front_channels & 1) {
+ e2c_vec[i] = (struct elem_to_channel) {
+ .av_position = AV_CH_FRONT_CENTER, .syn_ele = TYPE_SCE,
+ .elem_id = layout_map[i][1], .aac_position = AAC_CHANNEL_FRONT };
+ i++;
+ num_front_channels--;
+ }
+ if (num_front_channels >= 4) {
+ i += assign_pair(e2c_vec, layout_map, i, tags,
+ AV_CH_FRONT_LEFT_OF_CENTER,
+ AV_CH_FRONT_RIGHT_OF_CENTER,
+ AAC_CHANNEL_FRONT);
+ num_front_channels -= 2;
+ }
+ if (num_front_channels >= 2) {
+ i += assign_pair(e2c_vec, layout_map, i, tags,
+ AV_CH_FRONT_LEFT,
+ AV_CH_FRONT_RIGHT,
+ AAC_CHANNEL_FRONT);
+ num_front_channels -= 2;
+ }
+ while (num_front_channels >= 2) {
+ i += assign_pair(e2c_vec, layout_map, i, tags,
+ UINT64_MAX,
+ UINT64_MAX,
+ AAC_CHANNEL_FRONT);
+ num_front_channels -= 2;
+ }
+
+ if (num_side_channels >= 2) {
+ i += assign_pair(e2c_vec, layout_map, i, tags,
+ AV_CH_SIDE_LEFT,
+ AV_CH_SIDE_RIGHT,
+ AAC_CHANNEL_FRONT);
+ num_side_channels -= 2;
+ }
+ while (num_side_channels >= 2) {
+ i += assign_pair(e2c_vec, layout_map, i, tags,
+ UINT64_MAX,
+ UINT64_MAX,
+ AAC_CHANNEL_SIDE);
+ num_side_channels -= 2;
+ }
+
+ while (num_back_channels >= 4) {
+ i += assign_pair(e2c_vec, layout_map, i, tags,
+ UINT64_MAX,
+ UINT64_MAX,
+ AAC_CHANNEL_BACK);
+ num_back_channels -= 2;
+ }
+ if (num_back_channels >= 2) {
+ i += assign_pair(e2c_vec, layout_map, i, tags,
+ AV_CH_BACK_LEFT,
+ AV_CH_BACK_RIGHT,
+ AAC_CHANNEL_BACK);
+ num_back_channels -= 2;
+ }
+ if (num_back_channels) {
+ e2c_vec[i] = (struct elem_to_channel) {
+ .av_position = AV_CH_BACK_CENTER, .syn_ele = TYPE_SCE,
+ .elem_id = layout_map[i][1], .aac_position = AAC_CHANNEL_BACK };
+ i++;
+ num_back_channels--;
+ }
+
+ if (i < tags && layout_map[i][2] == AAC_CHANNEL_LFE) {
+ e2c_vec[i] = (struct elem_to_channel) {
+ .av_position = AV_CH_LOW_FREQUENCY, .syn_ele = TYPE_LFE,
+ .elem_id = layout_map[i][1], .aac_position = AAC_CHANNEL_LFE };
+ i++;
+ }
+ while (i < tags && layout_map[i][2] == AAC_CHANNEL_LFE) {
+ e2c_vec[i] = (struct elem_to_channel) {
+ .av_position = UINT64_MAX, .syn_ele = TYPE_LFE,
+ .elem_id = layout_map[i][1], .aac_position = AAC_CHANNEL_LFE };
+ i++;
+ }
+
+ // Must choose a stable sort
+ total_non_cc_elements = n = i;
+ do {
+ int next_n = 0;
+ for (i = 1; i < n; i++) {
+ if (e2c_vec[i-1].av_position > e2c_vec[i].av_position) {
+ FFSWAP(struct elem_to_channel, e2c_vec[i-1], e2c_vec[i]);
+ next_n = i;
+ }
+ }
+ n = next_n;
+ } while (n > 0);
+
+ layout = 0;
+ for (i = 0; i < total_non_cc_elements; i++) {
+ layout_map[i][0] = e2c_vec[i].syn_ele;
+ layout_map[i][1] = e2c_vec[i].elem_id;
+ layout_map[i][2] = e2c_vec[i].aac_position;
+ if (e2c_vec[i].av_position != UINT64_MAX) {
+ layout |= e2c_vec[i].av_position;
+ }
+ }
+
+ return layout;
+}
+
/**
* Configure output channel order based on the current program configuration element.
*
* @param che_pos current channel position configuration
- * @param new_che_pos New channel position configuration - we only do something if it differs from the current one.
*
* @return Returns error status. 0 - OK, !0 - error
*/
static av_cold int output_configure(AACContext *ac,
- enum ChannelPosition new_che_pos[4][MAX_ELEM_ID],
+ uint8_t layout_map[MAX_ELEM_ID*4][3], int tags,
int channel_config, enum OCStatus oc_type)
{
AVCodecContext *avctx = ac->avctx;
- int i, type, channels = 0, ret;
+ int i, channels = 0, ret;
- if (new_che_pos)
- memcpy(ac->che_pos, new_che_pos, 4 * MAX_ELEM_ID * sizeof(new_che_pos[0][0]));
+ if (ac->layout_map != layout_map) {
+ memcpy(ac->layout_map, layout_map, tags * sizeof(layout_map[0]));
+ ac->layout_map_tags = tags;
+ }
if (channel_config) {
for (i = 0; i < tags_per_config[channel_config]; i++) {
- int id = aac_channel_layout_map[channel_config - 1][i][1];
- type = aac_channel_layout_map[channel_config - 1][i][0];
- if ((ret = che_configure(ac, ac->che_pos[type][id],
- type, id, &channels)))
+ int type = aac_channel_layout_map[channel_config - 1][i][0];
+ int id = aac_channel_layout_map[channel_config - 1][i][1];
+ int positon = aac_channel_layout_map[channel_config - 1][i][2];
+ if ((ret = che_configure(ac, positon,
+ type, id,
+ &channels)))
return ret;
}
@@ -247,20 +433,24 @@ static av_cold int output_configure(AACContext *ac,
/* Allocate or free elements depending on if they are in the
* current program configuration.
*
- * Set up default 1:1 output mapping.
+ * Try to sniff a reasonable channel order, otherwise output the
+ * channels in the order the PCE declared them.
*/
- for (i = 0; i < MAX_ELEM_ID; i++) {
- for (type = 0; type < 4; type++) {
- if ((ret = che_configure(ac, ac->che_pos[type][i],
- type, i, &channels)))
- return ret;
- }
+ uint64_t layout = sniff_channel_order(layout_map, tags);
+ for (i = 0; i < tags; i++) {
+ int type = layout_map[i][0];
+ int id = layout_map[i][1];
+ int position = layout_map[i][2];
+ if ((ret = che_configure(ac, position,
+ type, id,
+ &channels)))
+ return ret;
}
memcpy(ac->tag_che_map, ac->che, 4 * MAX_ELEM_ID * sizeof(ac->che[0][0]));
- avctx->channel_layout = 0;
+ avctx->channel_layout = layout;
}
avctx->channels = channels;
@@ -277,30 +467,45 @@ static av_cold int output_configure(AACContext *ac,
* @param sce_map mono (Single Channel Element) map
* @param type speaker type/position for these channels
*/
-static void decode_channel_map(enum ChannelPosition *cpe_map,
- enum ChannelPosition *sce_map,
+static void decode_channel_map(uint8_t layout_map[][3],
enum ChannelPosition type,
GetBitContext *gb, int n)
{
while (n--) {
- enum ChannelPosition *map = cpe_map && get_bits1(gb) ? cpe_map : sce_map; // stereo or mono map
- map[get_bits(gb, 4)] = type;
+ enum RawDataBlockType syn_ele;
+ switch (type) {
+ case AAC_CHANNEL_FRONT:
+ case AAC_CHANNEL_BACK:
+ case AAC_CHANNEL_SIDE:
+ syn_ele = get_bits1(gb);
+ break;
+ case AAC_CHANNEL_CC:
+ skip_bits1(gb);
+ syn_ele = TYPE_CCE;
+ break;
+ case AAC_CHANNEL_LFE:
+ syn_ele = TYPE_LFE;
+ break;
+ }
+ layout_map[0][0] = syn_ele;
+ layout_map[0][1] = get_bits(gb, 4);
+ layout_map[0][2] = type;
+ layout_map++;
}
}
/**
* Decode program configuration element; reference: table 4.2.
*
- * @param new_che_pos New channel position configuration - we only do something if it differs from the current one.
- *
* @return Returns error status. 0 - OK, !0 - error
*/
static int decode_pce(AVCodecContext *avctx, MPEG4AudioConfig *m4ac,
- enum ChannelPosition new_che_pos[4][MAX_ELEM_ID],
+ uint8_t (*layout_map)[3],
GetBitContext *gb)
{
int num_front, num_side, num_back, num_lfe, num_assoc_data, num_cc, sampling_index;
int comment_len;
+ int tags;
skip_bits(gb, 2); // object_type
@@ -323,14 +528,19 @@ static int decode_pce(AVCodecContext *avctx, MPEG4AudioConfig *m4ac,
if (get_bits1(gb))
skip_bits(gb, 3); // mixdown_coeff_index and pseudo_surround
- decode_channel_map(new_che_pos[TYPE_CPE], new_che_pos[TYPE_SCE], AAC_CHANNEL_FRONT, gb, num_front);
- decode_channel_map(new_che_pos[TYPE_CPE], new_che_pos[TYPE_SCE], AAC_CHANNEL_SIDE, gb, num_side );
- decode_channel_map(new_che_pos[TYPE_CPE], new_che_pos[TYPE_SCE], AAC_CHANNEL_BACK, gb, num_back );
- decode_channel_map(NULL, new_che_pos[TYPE_LFE], AAC_CHANNEL_LFE, gb, num_lfe );
+ decode_channel_map(layout_map , AAC_CHANNEL_FRONT, gb, num_front);
+ tags = num_front;
+ decode_channel_map(layout_map + tags, AAC_CHANNEL_SIDE, gb, num_side);
+ tags += num_side;
+ decode_channel_map(layout_map + tags, AAC_CHANNEL_BACK, gb, num_back);
+ tags += num_back;
+ decode_channel_map(layout_map + tags, AAC_CHANNEL_LFE, gb, num_lfe);
+ tags += num_lfe;
skip_bits_long(gb, 4 * num_assoc_data);
- decode_channel_map(new_che_pos[TYPE_CCE], new_che_pos[TYPE_CCE], AAC_CHANNEL_CC, gb, num_cc );
+ decode_channel_map(layout_map + tags, AAC_CHANNEL_CC, gb, num_cc);
+ tags += num_cc;
align_get_bits(gb);
@@ -341,19 +551,18 @@ static int decode_pce(AVCodecContext *avctx, MPEG4AudioConfig *m4ac,
return -1;
}
skip_bits_long(gb, comment_len);
- return 0;
+ return tags;
}
/**
* Set up channel positions based on a default channel configuration
* as specified in table 1.17.
*
- * @param new_che_pos New channel position configuration - we only do something if it differs from the current one.
- *
* @return Returns error status. 0 - OK, !0 - error
*/
static av_cold int set_default_channel_config(AVCodecContext *avctx,
- enum ChannelPosition new_che_pos[4][MAX_ELEM_ID],
+ uint8_t (*layout_map)[3],
+ int *tags,
int channel_config)
{
if (channel_config < 1 || channel_config > 7) {
@@ -361,32 +570,8 @@ static av_cold int set_default_channel_config(AVCodecContext *avctx,
channel_config);
return -1;
}
-
- /* default channel configurations:
- *
- * 1ch : front center (mono)
- * 2ch : L + R (stereo)
- * 3ch : front center + L + R
- * 4ch : front center + L + R + back center
- * 5ch : front center + L + R + back stereo
- * 6ch : front center + L + R + back stereo + LFE
- * 7ch : front center + L + R + outer front left + outer front right + back stereo + LFE
- */
-
- if (channel_config != 2)
- new_che_pos[TYPE_SCE][0] = AAC_CHANNEL_FRONT; // front center (or mono)
- if (channel_config > 1)
- new_che_pos[TYPE_CPE][0] = AAC_CHANNEL_FRONT; // L + R (or stereo)
- if (channel_config == 4)
- new_che_pos[TYPE_SCE][1] = AAC_CHANNEL_BACK; // back center
- if (channel_config > 4)
- new_che_pos[TYPE_CPE][(channel_config == 7) + 1]
- = AAC_CHANNEL_BACK; // back stereo
- if (channel_config > 5)
- new_che_pos[TYPE_LFE][0] = AAC_CHANNEL_LFE; // LFE
- if (channel_config == 7)
- new_che_pos[TYPE_CPE][1] = AAC_CHANNEL_FRONT; // outer front left + outer front right
-
+ *tags = tags_per_config[channel_config];
+ memcpy(layout_map, aac_channel_layout_map[channel_config-1], *tags * sizeof(*layout_map));
return 0;
}
@@ -403,8 +588,9 @@ static int decode_ga_specific_config(AACContext *ac, AVCodecContext *avctx,
MPEG4AudioConfig *m4ac,
int channel_config)
{
- enum ChannelPosition new_che_pos[4][MAX_ELEM_ID];
int extension_flag, ret;
+ uint8_t layout_map[MAX_ELEM_ID*4][3];
+ int tags = 0;
if (get_bits1(gb)) { // frameLengthFlag
av_log_missing_feature(avctx, "960/120 MDCT window is", 1);
@@ -419,22 +605,23 @@ static int decode_ga_specific_config(AACContext *ac, AVCodecContext *avctx,
m4ac->object_type == AOT_ER_AAC_SCALABLE)
skip_bits(gb, 3); // layerNr
- memset(new_che_pos, 0, 4 * MAX_ELEM_ID * sizeof(new_che_pos[0][0]));
if (channel_config == 0) {
skip_bits(gb, 4); // element_instance_tag
- if ((ret = decode_pce(avctx, m4ac, new_che_pos, gb)))
- return ret;
+ tags = decode_pce(avctx, m4ac, layout_map, gb);
+ if (tags < 0)
+ return tags;
} else {
- if ((ret = set_default_channel_config(avctx, new_che_pos, channel_config)))
+ if ((ret = set_default_channel_config(avctx, layout_map, &tags, channel_config)))
return ret;
}
- if (count_channels(new_che_pos) > 1) {
+ if (count_channels(layout_map, tags) > 1) {
m4ac->ps = 0;
} else if (m4ac->sbr == 1 && m4ac->ps == -1)
m4ac->ps = 1;
- if (ac && (ret = output_configure(ac, new_che_pos, channel_config, OC_GLOBAL_HDR)))
+ if (ac && (ret = output_configure(ac, layout_map, tags,
+ channel_config, OC_GLOBAL_HDR)))
return ret;
if (extension_flag) {
@@ -588,7 +775,8 @@ static av_cold int aac_decode_init(AVCodecContext *avctx)
return -1;
} else {
int sr, i;
- enum ChannelPosition new_che_pos[4][MAX_ELEM_ID];
+ uint8_t layout_map[MAX_ELEM_ID*4][3];
+ int layout_map_tags;
sr = sample_rate_idx(avctx->sample_rate);
ac->m4ac.sampling_index = sr;
@@ -605,9 +793,11 @@ static av_cold int aac_decode_init(AVCodecContext *avctx)
ac->m4ac.chan_config = i;
if (ac->m4ac.chan_config) {
- int ret = set_default_channel_config(avctx, new_che_pos, ac->m4ac.chan_config);
+ int ret = set_default_channel_config(avctx, layout_map,
+ &layout_map_tags, ac->m4ac.chan_config);
if (!ret)
- output_configure(ac, new_che_pos, ac->m4ac.chan_config, OC_GLOBAL_HDR);
+ output_configure(ac, layout_map, layout_map_tags,
+ ac->m4ac.chan_config, OC_GLOBAL_HDR);
else if (avctx->err_recognition & AV_EF_EXPLODE)
return AVERROR_INVALIDDATA;
}
@@ -1711,7 +1901,8 @@ static int decode_extension_payload(AACContext *ac, GetBitContext *gb, int cnt,
} else if (ac->m4ac.ps == -1 && ac->output_configured < OC_LOCKED && ac->avctx->channels == 1) {
ac->m4ac.sbr = 1;
ac->m4ac.ps = 1;
- output_configure(ac, NULL, ac->m4ac.chan_config, ac->output_configured);
+ output_configure(ac, ac->layout_map, ac->layout_map_tags,
+ ac->m4ac.chan_config, ac->output_configured);
} else {
ac->m4ac.sbr = 1;
}
@@ -2085,16 +2276,18 @@ static int parse_adts_frame_header(AACContext *ac, GetBitContext *gb)
{
int size;
AACADTSHeaderInfo hdr_info;
+ uint8_t layout_map[MAX_ELEM_ID*4][3];
+ int layout_map_tags;
size = avpriv_aac_parse_header(gb, &hdr_info);
if (size > 0) {
if (hdr_info.chan_config) {
- enum ChannelPosition new_che_pos[4][MAX_ELEM_ID];
- memset(new_che_pos, 0, 4 * MAX_ELEM_ID * sizeof(new_che_pos[0][0]));
ac->m4ac.chan_config = hdr_info.chan_config;
- if (set_default_channel_config(ac->avctx, new_che_pos, hdr_info.chan_config))
+ if (set_default_channel_config(ac->avctx, layout_map,
+ &layout_map_tags, hdr_info.chan_config))
return -7;
- if (output_configure(ac, new_che_pos, hdr_info.chan_config,
+ if (output_configure(ac, layout_map, layout_map_tags,
+ hdr_info.chan_config,
FFMAX(ac->output_configured, OC_TRIAL_FRAME)))
return -7;
} else if (ac->output_configured != OC_LOCKED) {
@@ -2181,15 +2374,18 @@ static int aac_decode_frame_int(AVCodecContext *avctx, void *data,
break;
case TYPE_PCE: {
- enum ChannelPosition new_che_pos[4][MAX_ELEM_ID];
- memset(new_che_pos, 0, 4 * MAX_ELEM_ID * sizeof(new_che_pos[0][0]));
- if ((err = decode_pce(avctx, &ac->m4ac, new_che_pos, gb)))
+ uint8_t layout_map[MAX_ELEM_ID*4][3];
+ int tags;
+ tags = decode_pce(avctx, &ac->m4ac, layout_map, gb);
+ if (tags < 0) {
+ err = tags;
break;
+ }
if (ac->output_configured > OC_TRIAL_PCE)
av_log(avctx, AV_LOG_ERROR,
"Not evaluating a further program_config_element as this construct is dubious at best.\n");
else
- err = output_configure(ac, new_che_pos, 0, OC_TRIAL_PCE);
+ err = output_configure(ac, layout_map, tags, 0, OC_TRIAL_PCE);
break;
}