[FFmpeg-devel] [PATCH] aacenc: set default coding tools by profile

Rostislav Pehlivanov atomnuker at gmail.com
Wed Sep 2 03:45:34 CEST 2015


This commit will enable or disable coding tools by choosing
a profile.
Each option can be overriden individually (e.g. disable PNS
with AAC-Main profile).

The drawback is that "-help encoder=aac" will no longer
list the actual defaults, but instead print "-1" for the
default values of options. This is because we need to detect
if an option has been set individually (e.g. -aac_is 1) in
order to know which options override the default profile
options.
Hints have been added to the long description of each
option's state to mark which is the current default.

This is a hacky solution, so I'm asking people for their
opinions whether it is acceptable. This is the only way
I thought of, so any alternative suggestions are welcome.

The whole point of this commit is to add more restrictive
profiles like MPEG-2 Part 7 which disable PNS.

Signed-off-by: Rostislav Pehlivanov <atomnuker at gmail.com>
---
 libavcodec/aacenc.c       | 112 ++++++++++++++++++++++++++++++++--------------
 libavcodec/aacenc.h       |  15 +++++++
 libavcodec/aacenc_utils.h |  34 ++++++++++++++
 3 files changed, 127 insertions(+), 34 deletions(-)

diff --git a/libavcodec/aacenc.c b/libavcodec/aacenc.c
index 444ca0e..37cfe42 100644
--- a/libavcodec/aacenc.c
+++ b/libavcodec/aacenc.c
@@ -47,6 +47,42 @@
 #include "psymodel.h"
 
 /**
+ * List of currently supported profiles, anything not listed isn't supported.
+ */
+struct AACProfileOptions aacenc_profiles[] = {
+    {FF_PROFILE_AAC_LOW,
+        {  /* Default profile, these are the settings that get set by default */
+            .stereo_mode = 0,
+            .aac_coder = AAC_CODER_TWOLOOP,
+            .pns = 1,
+            .tns = 0,
+            .pred = OPT_NEEDS_MAIN,
+            .intensity_stereo = 1,
+        },
+    },
+    {FF_PROFILE_MPEG2_AAC_LOW,
+        {  /* Strict MPEG 2 Part 7 compliance profile */
+            .stereo_mode = 0,
+            .aac_coder = AAC_CODER_TWOLOOP,
+            .pns = OPT_BANNED,
+            .tns = 0,
+            .pred = OPT_BANNED,
+            .intensity_stereo = 1,
+        },
+    },
+    {FF_PROFILE_AAC_MAIN,
+        {  /* Main profile, all advanced encoding abilities enabled */
+            .stereo_mode = 0,
+            .aac_coder = AAC_CODER_TWOLOOP,
+            .pns = 1,
+            .tns = 0,
+            .pred = OPT_REQUIRED,
+            .intensity_stereo = 0,
+        },
+    },
+};
+
+/**
  * Make AAC audio config object.
  * @see 1.6.2.1 "Syntax - AudioSpecificConfig"
  */
@@ -616,15 +652,13 @@ static int aac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
                     tns_mode = 1;
             }
             s->cur_channel = start_ch;
-            if (s->options.stereo_mode && cpe->common_window) {
-                if (s->options.stereo_mode > 0) {
-                    IndividualChannelStream *ics = &cpe->ch[0].ics;
-                    for (w = 0; w < ics->num_windows; w += ics->group_len[w])
-                        for (g = 0;  g < ics->num_swb; g++)
-                            cpe->ms_mask[w*16+g] = 1;
-                } else if (s->coder->search_for_ms) {
-                    s->coder->search_for_ms(s, cpe);
-                }
+            if (s->options.stereo_mode == 2 && cpe->common_window) {
+                IndividualChannelStream *ics = &cpe->ch[0].ics;
+                for (w = 0; w < ics->num_windows; w += ics->group_len[w])
+                    for (g = 0;  g < ics->num_swb; g++)
+                        cpe->ms_mask[w*16+g] = 1;
+            } else if (s->options.stereo_mode == 1 && s->coder->search_for_ms) {
+                s->coder->search_for_ms(s, cpe);
             }
             if (s->options.intensity_stereo && s->coder->search_for_is) {
                 s->coder->search_for_is(s, avctx, cpe);
@@ -764,13 +798,16 @@ alloc_fail:
 
 static av_cold int aac_encode_init(AVCodecContext *avctx)
 {
+    AACEncOptions *p_opt = NULL;
     AACEncContext *s = avctx->priv_data;
-    int i, ret = 0;
+    int i, j, ret = 0;
     const uint8_t *sizes[2];
     uint8_t grouping[AAC_MAX_CHANNELS];
     int lengths[2];
 
     avctx->frame_size = 1024;
+    avctx->profile = avctx->profile == FF_PROFILE_UNKNOWN ? FF_PROFILE_AAC_LOW :
+                     avctx->profile;
 
     for (i = 0; i < 16; i++)
         if (avctx->sample_rate == avpriv_mpeg4audio_sample_rates[i])
@@ -784,17 +821,24 @@ static av_cold int aac_encode_init(AVCodecContext *avctx)
              "Unsupported number of channels: %d\n", s->channels);
     WARN_IF(1024.0 * avctx->bit_rate / avctx->sample_rate > 6144 * s->channels,
              "Too many bits per frame requested, clamping to max\n");
-    if (avctx->profile == FF_PROFILE_AAC_MAIN) {
-        s->options.pred = 1;
-    } else if ((avctx->profile == FF_PROFILE_AAC_LOW ||
-                avctx->profile == FF_PROFILE_UNKNOWN) && s->options.pred) {
-        s->profile = 0; /* Main */
-        WARN_IF(1, "Prediction requested, changing profile to AAC-Main\n");
-    } else if (avctx->profile == FF_PROFILE_AAC_LOW ||
-               avctx->profile == FF_PROFILE_UNKNOWN) {
-        s->profile = 1; /* Low */
+    for (j = 0; j < FF_ARRAY_ELEMS(aacenc_profiles); j++)
+        if (avctx->profile == aacenc_profiles[j].profile)
+            break;
+    ERROR_IF(j == FF_ARRAY_ELEMS(aacenc_profiles),
+             "Unsupported encoding profile: %d\n", avctx->profile);
+    p_opt = &aacenc_profiles[j].opts;
+    OPT_SET(&s->options, p_opt, stereo_mode);
+    OPT_SET(&s->options, p_opt, aac_coder);
+    OPT_SET(&s->options, p_opt, pns);
+    OPT_SET(&s->options, p_opt, tns);
+    OPT_SET(&s->options, p_opt, pred);
+    OPT_SET(&s->options, p_opt, intensity_stereo);
+
+    /* Special cases */
+    if (avctx->profile == FF_PROFILE_MPEG2_AAC_LOW) {
+        s->profile =FF_PROFILE_AAC_LOW;
     } else {
-        ERROR_IF(1, "Unsupported profile %d\n", avctx->profile);
+        s->profile = avctx->profile;
     }
 
     if (s->options.aac_coder != AAC_CODER_TWOLOOP) {
@@ -850,26 +894,26 @@ fail:
 
 #define AACENC_FLAGS AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM
 static const AVOption aacenc_options[] = {
-    {"stereo_mode", "Stereo coding method", offsetof(AACEncContext, options.stereo_mode), AV_OPT_TYPE_INT, {.i64 = 0}, -1, 1, AACENC_FLAGS, "stereo_mode"},
-        {"auto",     "Selected by the Encoder", 0, AV_OPT_TYPE_CONST, {.i64 = -1 }, INT_MIN, INT_MAX, AACENC_FLAGS, "stereo_mode"},
-        {"ms_off",   "Disable Mid/Side coding", 0, AV_OPT_TYPE_CONST, {.i64 =  0 }, INT_MIN, INT_MAX, AACENC_FLAGS, "stereo_mode"},
-        {"ms_force", "Force Mid/Side for the whole frame if possible", 0, AV_OPT_TYPE_CONST, {.i64 =  1 }, INT_MIN, INT_MAX, AACENC_FLAGS, "stereo_mode"},
-    {"aac_coder", "Coding algorithm", offsetof(AACEncContext, options.aac_coder), AV_OPT_TYPE_INT, {.i64 = AAC_CODER_TWOLOOP}, 0, AAC_CODER_NB-1, AACENC_FLAGS, "aac_coder"},
+    {"stereo_mode", "Stereo coding method", offsetof(AACEncContext, options.stereo_mode), AV_OPT_TYPE_INT, {.i64 = OPT_DEFAULT}, -1, 2, AACENC_FLAGS, "stereo_mode"},
+        {"ms_off",   "Disable Mid/Side coding (default)", 0, AV_OPT_TYPE_CONST, {.i64 =  0 }, INT_MIN, INT_MAX, AACENC_FLAGS, "stereo_mode"},
+        {"auto",     "Selected by the Encoder", 0, AV_OPT_TYPE_CONST, {.i64 =  1 }, INT_MIN, INT_MAX, AACENC_FLAGS, "stereo_mode"},
+        {"ms_force", "Force Mid/Side for the whole frame if possible", 0, AV_OPT_TYPE_CONST, {.i64 =  2 }, INT_MIN, INT_MAX, AACENC_FLAGS, "stereo_mode"},
+    {"aac_coder", "Coding algorithm", offsetof(AACEncContext, options.aac_coder), AV_OPT_TYPE_INT, {.i64 = OPT_DEFAULT}, -1, AAC_CODER_NB-1, AACENC_FLAGS, "aac_coder"},
         {"faac",     "FAAC-inspired method",      0, AV_OPT_TYPE_CONST, {.i64 = AAC_CODER_FAAC},    INT_MIN, INT_MAX, AACENC_FLAGS, "aac_coder"},
         {"anmr",     "ANMR method",               0, AV_OPT_TYPE_CONST, {.i64 = AAC_CODER_ANMR},    INT_MIN, INT_MAX, AACENC_FLAGS, "aac_coder"},
-        {"twoloop",  "Two loop searching method", 0, AV_OPT_TYPE_CONST, {.i64 = AAC_CODER_TWOLOOP}, INT_MIN, INT_MAX, AACENC_FLAGS, "aac_coder"},
+        {"twoloop",  "Two loop searching method (default)", 0, AV_OPT_TYPE_CONST, {.i64 = AAC_CODER_TWOLOOP}, INT_MIN, INT_MAX, AACENC_FLAGS, "aac_coder"},
         {"fast",     "Constant quantizer",        0, AV_OPT_TYPE_CONST, {.i64 = AAC_CODER_FAST},    INT_MIN, INT_MAX, AACENC_FLAGS, "aac_coder"},
-    {"aac_pns", "Perceptual Noise Substitution", offsetof(AACEncContext, options.pns), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, AACENC_FLAGS, "aac_pns"},
+    {"aac_pns", "Perceptual Noise Substitution", offsetof(AACEncContext, options.pns), AV_OPT_TYPE_INT, {.i64 = OPT_DEFAULT}, -1, 1, AACENC_FLAGS, "aac_pns"},
         {"disable",  "Disable perceptual noise substitution", 0, AV_OPT_TYPE_CONST, {.i64 =  0 }, INT_MIN, INT_MAX, AACENC_FLAGS, "aac_pns"},
-        {"enable",   "Enable perceptual noise substitution",  0, AV_OPT_TYPE_CONST, {.i64 =  1 }, INT_MIN, INT_MAX, AACENC_FLAGS, "aac_pns"},
-    {"aac_is", "Intensity stereo coding", offsetof(AACEncContext, options.intensity_stereo), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, AACENC_FLAGS, "intensity_stereo"},
+        {"enable",   "Enable perceptual noise substitution (default)",  0, AV_OPT_TYPE_CONST, {.i64 =  1 }, INT_MIN, INT_MAX, AACENC_FLAGS, "aac_pns"},
+    {"aac_is", "Intensity stereo coding", offsetof(AACEncContext, options.intensity_stereo), AV_OPT_TYPE_INT, {.i64 = OPT_DEFAULT}, -1, 1, AACENC_FLAGS, "intensity_stereo"},
         {"disable",  "Disable intensity stereo coding", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, INT_MIN, INT_MAX, AACENC_FLAGS, "intensity_stereo"},
-        {"enable",   "Enable intensity stereo coding", 0, AV_OPT_TYPE_CONST, {.i64 = 1}, INT_MIN, INT_MAX, AACENC_FLAGS, "intensity_stereo"},
-    {"aac_tns", "Temporal noise shaping", offsetof(AACEncContext, options.tns), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, AACENC_FLAGS, "aac_tns"},
-        {"disable",  "Disable temporal noise shaping", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, INT_MIN, INT_MAX, AACENC_FLAGS, "aac_tns"},
+        {"enable",   "Enable intensity stereo coding (default)", 0, AV_OPT_TYPE_CONST, {.i64 = 1}, INT_MIN, INT_MAX, AACENC_FLAGS, "intensity_stereo"},
+    {"aac_tns", "Temporal noise shaping", offsetof(AACEncContext, options.tns), AV_OPT_TYPE_INT, {.i64 = OPT_DEFAULT}, -1, 1, AACENC_FLAGS, "aac_tns"},
+        {"disable",  "Disable temporal noise shaping (default)", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, INT_MIN, INT_MAX, AACENC_FLAGS, "aac_tns"},
         {"enable",   "Enable temporal noise shaping", 0, AV_OPT_TYPE_CONST, {.i64 = 1}, INT_MIN, INT_MAX, AACENC_FLAGS, "aac_tns"},
-    {"aac_pred", "AAC-Main prediction", offsetof(AACEncContext, options.pred), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, AACENC_FLAGS, "aac_pred"},
-        {"disable",  "Disable AAC-Main prediction", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, INT_MIN, INT_MAX, AACENC_FLAGS, "aac_pred"},
+    {"aac_pred", "AAC-Main prediction", offsetof(AACEncContext, options.pred), AV_OPT_TYPE_INT, {.i64 = OPT_DEFAULT}, -1, 1, AACENC_FLAGS, "aac_pred"},
+        {"disable",  "Disable AAC-Main prediction (default)", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, INT_MIN, INT_MAX, AACENC_FLAGS, "aac_pred"},
         {"enable",   "Enable AAC-Main prediction", 0, AV_OPT_TYPE_CONST, {.i64 = 1}, INT_MIN, INT_MAX, AACENC_FLAGS, "aac_pred"},
     {NULL}
 };
diff --git a/libavcodec/aacenc.h b/libavcodec/aacenc.h
index 2b7a62a..8e767cd 100644
--- a/libavcodec/aacenc.h
+++ b/libavcodec/aacenc.h
@@ -41,6 +41,14 @@ typedef enum AACCoder {
     AAC_CODER_NB,
 }AACCoder;
 
+enum AACOpts {
+    OPT_DEFAULT     = -1,
+    OPT_BANNED      = -256,
+    OPT_NEEDS_MAIN  = -384,
+    OPT_NEEDS_LTP   = -512,
+    OPT_REQUIRED    = -768,
+};
+
 typedef struct AACEncOptions {
     int stereo_mode;
     int aac_coder;
@@ -50,6 +58,13 @@ typedef struct AACEncOptions {
     int intensity_stereo;
 } AACEncOptions;
 
+struct AACProfileOptions {
+    int profile;
+    struct AACEncOptions opts;
+};
+
+extern struct AACProfileOptions aacenc_profiles[];
+
 struct AACEncContext;
 
 typedef struct AACCoefficientsEncoder {
diff --git a/libavcodec/aacenc_utils.h b/libavcodec/aacenc_utils.h
index 327fbad..ac665d5 100644
--- a/libavcodec/aacenc_utils.h
+++ b/libavcodec/aacenc_utils.h
@@ -139,5 +139,39 @@ static inline int quant_array_idx(const float val, const float *arr, const int n
         av_log(avctx, AV_LOG_WARNING, __VA_ARGS__); \
     }
 
+#define OPT_SET(e_opt, p_opt, name) \
+    ERROR_IF ((e_opt)->name == 1 && (p_opt)->name == OPT_BANNED, \
+              "Profile %i does not allow %s\n", avctx->profile, #name); \
+    ERROR_IF ((e_opt)->name == 0 && (p_opt)->name == OPT_REQUIRED, \
+             "Option %s is a requirement for this profile (%i)\n", \
+              #name, avctx->profile); \
+    if ((e_opt)->name == 1 && (p_opt)->name == OPT_NEEDS_MAIN && \
+        avctx->profile == FF_PROFILE_AAC_LOW) { \
+        WARN_IF(1, "Profile %i does not allow for %s, setting profile to " \
+                "\"aac_main\"(%i)\n", avctx->profile, #name, FF_PROFILE_AAC_MAIN); \
+        avctx->profile = FF_PROFILE_AAC_MAIN; \
+        p_opt = &aacenc_profiles[1].opts; \
+    } \
+    if ((e_opt)->name == 1 && (p_opt)->name == OPT_NEEDS_LTP && \
+        avctx->profile == FF_PROFILE_AAC_LOW) { \
+        WARN_IF(1, "Profile %i does not allow for %s, setting profile to " \
+                "\"aac_ltp\"(%i)\n", avctx->profile, #name, FF_PROFILE_AAC_LTP); \
+        avctx->profile = FF_PROFILE_AAC_LTP; \
+        p_opt = &aacenc_profiles[2].opts; \
+    } \
+    if ((e_opt)->name == OPT_DEFAULT) { \
+        if ((p_opt)->name == OPT_BANNED) { \
+            (e_opt)->name = 0; \
+        } else if ((p_opt)->name == OPT_NEEDS_LTP) { \
+            (e_opt)->name = 0; \
+        } else if ((p_opt)->name == OPT_NEEDS_MAIN) { \
+            (e_opt)->name = 0; \
+        } else if ((p_opt)->name == OPT_REQUIRED) { \
+            (e_opt)->name = 1; \
+        } else { \
+            (e_opt)->name = (p_opt)->name; \
+        } \
+    } \
+    WARN_IF(0, "Option %s set to %i\n", #name, (e_opt)->name);
 
 #endif /* AVCODEC_AACENC_UTILS_H */
-- 
2.5.1



More information about the ffmpeg-devel mailing list