[FFmpeg-devel] [PATCH] aacdec: add support for dual mono in Japanese DTV

atsukada at users.sourceforge.net atsukada at users.sourceforge.net
Mon Aug 20 17:54:54 CEST 2012


From: Akihiro Tsukada <atsukada at users.sourceforge.net>

Japanese DTV uses some non standard extensions in AAC audio.
One example is 'dual mono', which combines two independent
audio into one stereo stream, storing them in left and right channels
respectively.  Historically, dual mono audio has been used for
multi-lingual audio, one for local/native language, and another for english,
and usually the "main" (local language) channel should be output without
any user interactions.

The frames of those dual mono audio are allowed to set
ADTS channel_config field to 0, and just contain two SCE's *WITHOUT* PCE,
which is a non standard extension by Japanese DTV standard.
(ref. ARIB STD-B32 PartII 5.2.3)

With this patch, the decoder detects dual mono and
outputs only the selected channel of user's choice,
replicating it to the other channel.
Users can change the output channel by setting AVOption "dual_mono_mode".

AVOption "jp_dtv_mode" is introduced as well,
which enables Japanese DTV specific extensions.
Without this option, fate fails at al17_44.mp4,
as it also has the configuration of two SCE's.

Signed-off-by: Akihiro Tsukada <atsukada at users.sourceforge.net>
---
 libavcodec/aac.h    |   10 +++++++
 libavcodec/aacdec.c |   72 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 82 insertions(+), 0 deletions(-)

diff --git a/libavcodec/aac.h b/libavcodec/aac.h
index 29ba2f1..e989be3 100644
--- a/libavcodec/aac.h
+++ b/libavcodec/aac.h
@@ -261,6 +261,7 @@ typedef struct {
  * main AAC context
  */
 typedef struct {
+    AVClass        *class;        ///< Class for AVOptions
     AVCodecContext *avctx;
     AVFrame frame;
 
@@ -304,6 +305,15 @@ typedef struct {
     float *output_data[MAX_CHANNELS];                 ///< Points to each element's 'ret' buffer (PCM output).
     /** @} */
 
+
+    /**
+     * @name Japanese DTV specific extension
+     * @{
+     */
+    int jp_dtv_mode;    ///< AVOption. enable japanese DTV specific extensions
+    int dmono_mode;     ///< AVOption. select the channel to decode in dual mono.
+    /** @} */
+
     DECLARE_ALIGNED(32, float, temp)[128];
 
     OutputConfiguration oc[2];
diff --git a/libavcodec/aacdec.c b/libavcodec/aacdec.c
index df2a494..1be82ff 100644
--- a/libavcodec/aacdec.c
+++ b/libavcodec/aacdec.c
@@ -99,11 +99,13 @@
 #include "mpeg4audio.h"
 #include "aacadtsdec.h"
 #include "libavutil/intfloat.h"
+#include "libavutil/opt.h"
 
 #include <assert.h>
 #include <errno.h>
 #include <math.h>
 #include <string.h>
+#include <stddef.h>
 
 #if ARCH_ARM
 #   include "arm/aac.h"
@@ -2375,6 +2377,21 @@ static int parse_adts_frame_header(AACContext *ac, GetBitContext *gb)
                 return -7;
         } else {
             ac->oc[1].m4ac.chan_config = 0;
+            /**
+             * dual mono frames in Japanese DTV can have chan_config 0
+             * WITHOUT specifying PCE.
+             *  thus, set dual mono as default.
+             */
+            if (ac->jp_dtv_mode && ac->oc[0].status == OC_NONE) {
+                layout_map_tags = 2;
+                layout_map[0][0] = layout_map[1][0] = TYPE_SCE;
+                layout_map[0][2] = layout_map[1][2] = AAC_CHANNEL_FRONT;
+                layout_map[0][1] = 0;
+                layout_map[1][1] = 1;
+                if (output_configure(ac, layout_map, layout_map_tags,
+                                     0, OC_TRIAL_FRAME))
+                    return -7;
+            }
         }
         ac->oc[1].m4ac.sample_rate     = hdr_info.sample_rate;
         ac->oc[1].m4ac.sampling_index  = hdr_info.sampling_index;
@@ -2399,6 +2416,8 @@ static int aac_decode_frame_int(AVCodecContext *avctx, void *data,
     enum RawDataBlockType elem_type, elem_type_prev = TYPE_END;
     int err, elem_id;
     int samples = 0, multiplier, audio_found = 0, pce_found = 0;
+    int is_dmono, sce_count = 0;
+    float *tmp = NULL;
 
     if (show_bits(gb, 12) == 0xfff) {
         if (parse_adts_frame_header(ac, gb) < 0) {
@@ -2433,6 +2452,7 @@ static int aac_decode_frame_int(AVCodecContext *avctx, void *data,
         case TYPE_SCE:
             err = decode_ics(ac, &che->ch[0], gb, 0, 0);
             audio_found = 1;
+            sce_count++;
             break;
 
         case TYPE_CPE:
@@ -2511,6 +2531,20 @@ static int aac_decode_frame_int(AVCodecContext *avctx, void *data,
     multiplier = (ac->oc[1].m4ac.sbr == 1) ? ac->oc[1].m4ac.ext_sample_rate > ac->oc[1].m4ac.sample_rate : 0;
     samples <<= multiplier;
 
+    /* for dual-mono audio (SCE + SCE) */
+    is_dmono = ac->jp_dtv_mode && sce_count == 2 &&
+               ac->oc[1].channel_layout == (AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT);
+
+    if (is_dmono) {
+        if (ac->dmono_mode == 0) {
+            tmp = ac->output_data[1];
+            ac->output_data[1] = ac->output_data[0];
+        } else if (ac->dmono_mode == 1) {
+            tmp = ac->output_data[0];
+            ac->output_data[0] = ac->output_data[1];
+        }
+    }
+
     if (samples) {
         /* get output buffer */
         ac->frame.nb_samples = samples;
@@ -2533,6 +2567,13 @@ static int aac_decode_frame_int(AVCodecContext *avctx, void *data,
     }
     *got_frame_ptr = !!samples;
 
+    if (is_dmono) {
+        if (ac->dmono_mode == 0)
+            ac->output_data[1] = tmp;
+        else if (ac->dmono_mode == 1)
+            ac->output_data[0] = tmp;
+    }
+
     if (ac->oc[1].status && audio_found) {
         avctx->sample_rate = ac->oc[1].m4ac.sample_rate << multiplier;
         avctx->frame_size = samples;
@@ -2882,6 +2923,36 @@ static av_cold int latm_decode_init(AVCodecContext *avctx)
 }
 
 
+/**
+ * AVOptions for Japanese DTV specific extensions (ADTS only)
+ */
+#define AACDEC_FLAGS AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM
+static const AVOption options[] = {
+    {"jp_dtv_mode", "Enable Japanse DTV specific extensions",
+     offsetof(AACContext, jp_dtv_mode), AV_OPT_TYPE_INT, {0}, 0, 1,
+     AACDEC_FLAGS},
+
+    {"dual_mono_mode", "Select the channel to decode for dual mono",
+     offsetof(AACContext, dmono_mode), AV_OPT_TYPE_INT, {0}, 0, 2,
+     AACDEC_FLAGS, "dual_mono_mode"},
+
+    {"main", "Select Main/Left channel", 0, AV_OPT_TYPE_CONST, {0},
+     INT_MIN, INT_MAX, AACDEC_FLAGS, "dual_mono_mode"},
+    {"sub", "Select Sub/Right channel", 0, AV_OPT_TYPE_CONST, {1},
+     INT_MIN, INT_MAX, AACDEC_FLAGS, "dual_mono_mode"},
+    {"both", "Select both channels", 0, AV_OPT_TYPE_CONST, {2},
+     INT_MIN, INT_MAX, AACDEC_FLAGS, "dual_mono_mode"},
+
+    {NULL},
+};
+
+static const AVClass aac_decoder_class = {
+    .class_name = "AAC decoder",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
 AVCodec ff_aac_decoder = {
     .name            = "aac",
     .type            = AVMEDIA_TYPE_AUDIO,
@@ -2897,6 +2968,7 @@ AVCodec ff_aac_decoder = {
     .capabilities    = CODEC_CAP_CHANNEL_CONF | CODEC_CAP_DR1,
     .channel_layouts = aac_channel_layout,
     .flush = flush,
+    .priv_class      = &aac_decoder_class,
 };
 
 /*
-- 
1.7.7.6



More information about the ffmpeg-devel mailing list