lavc/audiotoolboxenc: fix a number of config issues
authorRodger Combs <rodger.combs@gmail.com>
Wed, 23 Mar 2016 21:46:39 +0000 (16:46 -0500)
committerRodger Combs <rodger.combs@gmail.com>
Sat, 2 Apr 2016 08:03:13 +0000 (03:03 -0500)
- size variables were used in a confusing way
- incorrect size var use led to channel layouts not being set properly
- channel layouts were incorrectly mapped for >2-channel AAC
- bitrates not accepted by the encoder were discarded instead of being clamped
- some minor style/indentation fixes

libavcodec/audiotoolboxenc.c

index 4797b2a..22352da 100644 (file)
@@ -146,6 +146,86 @@ static int get_ilbc_mode(AVCodecContext *avctx)
         return 30;
 }
 
         return 30;
 }
 
+static av_cold int get_channel_label(int channel)
+{
+    uint64_t map = 1 << channel;
+    if (map <= AV_CH_LOW_FREQUENCY)
+        return channel + 1;
+    else if (map <= AV_CH_BACK_RIGHT)
+        return channel + 29;
+    else if (map <= AV_CH_BACK_CENTER)
+        return channel - 1;
+    else if (map <= AV_CH_SIDE_RIGHT)
+        return channel - 4;
+    else if (map <= AV_CH_TOP_BACK_RIGHT)
+        return channel + 1;
+    else if (map <= AV_CH_STEREO_RIGHT)
+        return -1;
+    else if (map <= AV_CH_WIDE_RIGHT)
+        return channel + 4;
+    else if (map <= AV_CH_SURROUND_DIRECT_RIGHT)
+        return channel - 23;
+    else if (map == AV_CH_LOW_FREQUENCY_2)
+        return kAudioChannelLabel_LFE2;
+    else
+        return -1;
+}
+
+static int remap_layout(AudioChannelLayout *layout, uint64_t in_layout, int count)
+{
+    int i;
+    int c = 0;
+    layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
+    layout->mNumberChannelDescriptions = count;
+    for (i = 0; i < count; i++) {
+        int label;
+        while (!(in_layout & (1 << c)) && c < 64)
+            c++;
+        if (c == 64)
+            return AVERROR(EINVAL); // This should never happen
+        label = get_channel_label(c);
+        layout->mChannelDescriptions[i].mChannelLabel = label;
+        if (label < 0)
+            return AVERROR(EINVAL);
+        c++;
+    }
+    return 0;
+}
+
+static int get_aac_tag(uint64_t in_layout)
+{
+    switch (in_layout) {
+    case AV_CH_LAYOUT_MONO:
+        return kAudioChannelLayoutTag_Mono;
+    case AV_CH_LAYOUT_STEREO:
+        return kAudioChannelLayoutTag_Stereo;
+    case AV_CH_LAYOUT_QUAD:
+        return kAudioChannelLayoutTag_AAC_Quadraphonic;
+    case AV_CH_LAYOUT_OCTAGONAL:
+        return kAudioChannelLayoutTag_AAC_Octagonal;
+    case AV_CH_LAYOUT_SURROUND:
+        return kAudioChannelLayoutTag_AAC_3_0;
+    case AV_CH_LAYOUT_4POINT0:
+        return kAudioChannelLayoutTag_AAC_4_0;
+    case AV_CH_LAYOUT_5POINT0:
+        return kAudioChannelLayoutTag_AAC_5_0;
+    case AV_CH_LAYOUT_5POINT1:
+        return kAudioChannelLayoutTag_AAC_5_1;
+    case AV_CH_LAYOUT_6POINT0:
+        return kAudioChannelLayoutTag_AAC_6_0;
+    case AV_CH_LAYOUT_6POINT1:
+        return kAudioChannelLayoutTag_AAC_6_1;
+    case AV_CH_LAYOUT_7POINT0:
+        return kAudioChannelLayoutTag_AAC_7_0;
+    case AV_CH_LAYOUT_7POINT1_WIDE_BACK:
+        return kAudioChannelLayoutTag_AAC_7_1;
+    case AV_CH_LAYOUT_7POINT1:
+        return kAudioChannelLayoutTag_MPEG_7_1_C;
+    default:
+        return 0;
+    }
+}
+
 static av_cold int ffat_init_encoder(AVCodecContext *avctx)
 {
     ATDecodeContext *at = avctx->priv_data;
 static av_cold int ffat_init_encoder(AVCodecContext *avctx)
 {
     ATDecodeContext *at = avctx->priv_data;
@@ -170,11 +250,12 @@ static av_cold int ffat_init_encoder(AVCodecContext *avctx)
         .mFormatID = ffat_get_format_id(avctx->codec_id, avctx->profile),
         .mChannelsPerFrame = in_format.mChannelsPerFrame,
     };
         .mFormatID = ffat_get_format_id(avctx->codec_id, avctx->profile),
         .mChannelsPerFrame = in_format.mChannelsPerFrame,
     };
-    AudioChannelLayout channel_layout = {
-        .mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelBitmap,
-        .mChannelBitmap = avctx->channel_layout,
-    };
-    UInt32 size = sizeof(channel_layout);
+    UInt32 layout_size = sizeof(AudioChannelLayout) +
+                         sizeof(AudioChannelDescription) * avctx->channels;
+    AudioChannelLayout *channel_layout = av_malloc(layout_size);
+
+    if (!channel_layout)
+        return AVERROR(ENOMEM);
 
     if (avctx->codec_id == AV_CODEC_ID_ILBC) {
         int mode = get_ilbc_mode(avctx);
 
     if (avctx->codec_id == AV_CODEC_ID_ILBC) {
         int mode = get_ilbc_mode(avctx);
@@ -186,22 +267,45 @@ static av_cold int ffat_init_encoder(AVCodecContext *avctx)
 
     if (status != 0) {
         av_log(avctx, AV_LOG_ERROR, "AudioToolbox init error: %i\n", (int)status);
 
     if (status != 0) {
         av_log(avctx, AV_LOG_ERROR, "AudioToolbox init error: %i\n", (int)status);
+        av_free(channel_layout);
         return AVERROR_UNKNOWN;
     }
 
         return AVERROR_UNKNOWN;
     }
 
-    size = sizeof(UInt32);
+    if (!avctx->channel_layout)
+        avctx->channel_layout = av_get_default_channel_layout(avctx->channels);
+
+    if ((status = remap_layout(channel_layout, avctx->channel_layout, avctx->channels)) < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Invalid channel layout\n");
+        av_free(channel_layout);
+        return status;
+    }
 
 
-    AudioConverterSetProperty(at->converter, kAudioConverterInputChannelLayout,
-                              size, &channel_layout);
-    AudioConverterSetProperty(at->converter, kAudioConverterOutputChannelLayout,
-                              size, &channel_layout);
+    if (AudioConverterSetProperty(at->converter, kAudioConverterInputChannelLayout,
+                                  layout_size, channel_layout)) {
+        av_log(avctx, AV_LOG_ERROR, "Unsupported input channel layout\n");
+        av_free(channel_layout);
+        return AVERROR(EINVAL);
+    }
+    if (avctx->codec_id == AV_CODEC_ID_AAC) {
+        int tag = get_aac_tag(avctx->channel_layout);
+        if (tag) {
+            channel_layout->mChannelLayoutTag = tag;
+            channel_layout->mNumberChannelDescriptions = 0;
+        }
+    }
+    if (AudioConverterSetProperty(at->converter, kAudioConverterOutputChannelLayout,
+                                  layout_size, channel_layout)) {
+        av_log(avctx, AV_LOG_ERROR, "Unsupported output channel layout\n");
+        av_free(channel_layout);
+        return AVERROR(EINVAL);
+    }
+    av_free(channel_layout);
 
 
-    if (avctx->bits_per_raw_sample) {
-        size = sizeof(avctx->bits_per_raw_sample);
+    if (avctx->bits_per_raw_sample)
         AudioConverterSetProperty(at->converter,
                                   kAudioConverterPropertyBitDepthHint,
         AudioConverterSetProperty(at->converter,
                                   kAudioConverterPropertyBitDepthHint,
-                                  size, &avctx->bits_per_raw_sample);
-    }
+                                  sizeof(avctx->bits_per_raw_sample),
+                                  &avctx->bits_per_raw_sample);
 
     if (at->mode == -1)
         at->mode = (avctx->flags & AV_CODEC_FLAG_QSCALE) ?
 
     if (at->mode == -1)
         at->mode = (avctx->flags & AV_CODEC_FLAG_QSCALE) ?
@@ -209,7 +313,7 @@ static av_cold int ffat_init_encoder(AVCodecContext *avctx)
                    kAudioCodecBitRateControlMode_Constant;
 
     AudioConverterSetProperty(at->converter, kAudioCodecPropertyBitRateControlMode,
                    kAudioCodecBitRateControlMode_Constant;
 
     AudioConverterSetProperty(at->converter, kAudioCodecPropertyBitRateControlMode,
-                              size, &at->mode);
+                              sizeof(at->mode), &at->mode);
 
     if (at->mode == kAudioCodecBitRateControlMode_Variable) {
         int q = avctx->global_quality / FF_QP2LAMBDA;
 
     if (at->mode == kAudioCodecBitRateControlMode_Variable) {
         int q = avctx->global_quality / FF_QP2LAMBDA;
@@ -220,16 +324,50 @@ static av_cold int ffat_init_encoder(AVCodecContext *avctx)
         }
         q = 127 - q * 9;
         AudioConverterSetProperty(at->converter, kAudioCodecPropertySoundQualityForVBR,
         }
         q = 127 - q * 9;
         AudioConverterSetProperty(at->converter, kAudioCodecPropertySoundQualityForVBR,
-                                  size, &q);
+                                  sizeof(q), &q);
     } else if (avctx->bit_rate > 0) {
         UInt32 rate = avctx->bit_rate;
     } else if (avctx->bit_rate > 0) {
         UInt32 rate = avctx->bit_rate;
+        UInt32 size;
+        status = AudioConverterGetPropertyInfo(at->converter,
+                                               kAudioConverterApplicableEncodeBitRates,
+                                               &size, NULL);
+        if (!status && size) {
+            UInt32 new_rate = rate;
+            int count;
+            int i;
+            AudioValueRange *ranges = av_malloc(size);
+            if (!ranges)
+                return AVERROR(ENOMEM);
+            AudioConverterGetProperty(at->converter,
+                                      kAudioConverterApplicableEncodeBitRates,
+                                      &size, ranges);
+            count = size / sizeof(AudioValueRange);
+            for (i = 0; i < count; i++) {
+                AudioValueRange *range = &ranges[i];
+                if (rate >= range->mMinimum && rate <= range->mMaximum) {
+                    new_rate = rate;
+                    break;
+                } else if (rate > range->mMaximum) {
+                    new_rate = range->mMaximum;
+                } else {
+                    new_rate = range->mMinimum;
+                    break;
+                }
+            }
+            if (new_rate != rate) {
+                av_log(avctx, AV_LOG_WARNING,
+                       "Bitrate %u not allowed; changing to %u\n", rate, new_rate);
+                rate = new_rate;
+            }
+            av_free(ranges);
+        }
         AudioConverterSetProperty(at->converter, kAudioConverterEncodeBitRate,
         AudioConverterSetProperty(at->converter, kAudioConverterEncodeBitRate,
-                                  size, &rate);
+                                  sizeof(rate), &rate);
     }
 
     at->quality = 96 - at->quality * 32;
     AudioConverterSetProperty(at->converter, kAudioConverterCodecQuality,
     }
 
     at->quality = 96 - at->quality * 32;
     AudioConverterSetProperty(at->converter, kAudioConverterCodecQuality,
-                              size, &at->quality);
+                              sizeof(at->quality), &at->quality);
 
     if (!AudioConverterGetPropertyInfo(at->converter, kAudioConverterCompressionMagicCookie,
                                        &avctx->extradata_size, NULL) &&
 
     if (!AudioConverterGetPropertyInfo(at->converter, kAudioConverterCompressionMagicCookie,
                                        &avctx->extradata_size, NULL) &&
@@ -289,10 +427,10 @@ static av_cold int ffat_init_encoder(AVCodecContext *avctx)
 
 #if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
     if (at->mode == kAudioCodecBitRateControlMode_Variable && avctx->rc_max_rate) {
 
 #if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
     if (at->mode == kAudioCodecBitRateControlMode_Variable && avctx->rc_max_rate) {
-        int max_size = avctx->rc_max_rate * avctx->frame_size / avctx->sample_rate;
+        UInt32 max_size = avctx->rc_max_rate * avctx->frame_size / avctx->sample_rate;
         if (max_size)
         if (max_size)
-        AudioConverterSetProperty(at->converter, kAudioCodecPropertyPacketSizeLimitForVBR,
-                                  size, &max_size);
+            AudioConverterSetProperty(at->converter, kAudioCodecPropertyPacketSizeLimitForVBR,
+                                      sizeof(max_size), &max_size);
     }
 #endif
 
     }
 #endif
 
@@ -455,7 +593,23 @@ static const AVOption options[] = {
         .profiles       = PROFILES, \
     };
 
         .profiles       = PROFILES, \
     };
 
-FFAT_ENC(aac,          AV_CODEC_ID_AAC,          aac_profiles)
+static const uint64_t aac_at_channel_layouts[] = {
+    AV_CH_LAYOUT_MONO,
+    AV_CH_LAYOUT_STEREO,
+    AV_CH_LAYOUT_SURROUND,
+    AV_CH_LAYOUT_4POINT0,
+    AV_CH_LAYOUT_5POINT0,
+    AV_CH_LAYOUT_5POINT1,
+    AV_CH_LAYOUT_6POINT0,
+    AV_CH_LAYOUT_6POINT1,
+    AV_CH_LAYOUT_7POINT0,
+    AV_CH_LAYOUT_7POINT1_WIDE_BACK,
+    AV_CH_LAYOUT_QUAD,
+    AV_CH_LAYOUT_OCTAGONAL,
+    0,
+};
+
+FFAT_ENC(aac,          AV_CODEC_ID_AAC,          aac_profiles, , .channel_layouts = aac_at_channel_layouts)
 //FFAT_ENC(adpcm_ima_qt, AV_CODEC_ID_ADPCM_IMA_QT, NULL)
 FFAT_ENC(alac,         AV_CODEC_ID_ALAC,         NULL, | AV_CODEC_CAP_VARIABLE_FRAME_SIZE | AV_CODEC_CAP_LOSSLESS)
 FFAT_ENC(ilbc,         AV_CODEC_ID_ILBC,         NULL)
 //FFAT_ENC(adpcm_ima_qt, AV_CODEC_ID_ADPCM_IMA_QT, NULL)
 FFAT_ENC(alac,         AV_CODEC_ID_ALAC,         NULL, | AV_CODEC_CAP_VARIABLE_FRAME_SIZE | AV_CODEC_CAP_LOSSLESS)
 FFAT_ENC(ilbc,         AV_CODEC_ID_ILBC,         NULL)