]> granicus.if.org Git - handbrake/commitdiff
HE-AAC support for the OSX port. Thanks go to amarcus, ritsuka and rodeo.
authorsr55 <sr55.hb@outlook.com>
Mon, 18 Apr 2011 12:10:07 +0000 (12:10 +0000)
committersr55 <sr55.hb@outlook.com>
Mon, 18 Apr 2011 12:10:07 +0000 (12:10 +0000)
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@3937 b64f7644-9d1e-0410-96f1-a4d463321fa5

libhb/common.c
libhb/common.h
libhb/hb.c
libhb/hb.h
libhb/internal.h
libhb/muxmkv.c
libhb/muxmp4.c
libhb/platform/macosx/encca_aac.c
libhb/work.c
macosx/HBAudio.m
test/test.c

index a3887b680f71510dbdcb0e18e3d73e410aeea089..90bd23cb6d26e9d0106ecb2fafab4275bcdeb910 100644 (file)
@@ -118,15 +118,14 @@ ffac3
 24kHz       318 (320)           318 (320)           318 (320)
 48kHz       636 (640)           636 (640)           636 (640)
 
-Core Audio  (core audio api provides range of allowed bitrates)
-24kHz       16-64               32-128              80-320      
-44.1kHz                         64-320              160-768      
-48kHz       32-256              64-320              160-768                 
-
-Core Audio  (minimum limits found in testing)
-24kHz       16                  32                  96
-44.1kHz     32                  64                  160
-48kHz       40                  80                  240
+Core Audio AAC (core audio api provides range of allowed bitrates)
+24kHz       16-64               32-128              80-320
+32kHz       24-96               48-192              128-448
+48kHz       32-256              64-320              160-768
+
+Core Audio HE-AAC (core audio api provides range of allowed bitrates)
+32kHz       12-40               24-80               64-192
+48kHz       16-40               32-80               80-192
 */
 
 void hb_get_audio_bitrate_limits(uint32_t codec, int samplerate, int mixdown, int *low, int *high)
@@ -149,27 +148,27 @@ void hb_get_audio_bitrate_limits(uint32_t codec, int samplerate, int mixdown, in
             break;
 
         case HB_ACODEC_CA_AAC:
-            if (samplerate > 44100)
+            if (samplerate > 32000)
             {
-                *low = channels * 40;
-                *high = 256;
+                *low = channels * 32;
+                if (channels == 1)
+                    *high = 256;
                 if (channels == 2)
                     *high = 320;
                 if (channels == 6)
                 {
+                    *low = 160;
                     *high = 768;
                 }
             }
             else if (samplerate > 24000)
             {
-                *low = channels * 32;
-                *high = 256;
-                if (channels == 2)
-                    *high = 320;
+                *low = channels * 24;
+                *high = channels * 96;
                 if (channels == 6)
                 {
-                    *low = 160;
-                    *high = 768;
+                    *low = 128;
+                    *high = 448;
                 }
             }
             else
@@ -178,11 +177,35 @@ void hb_get_audio_bitrate_limits(uint32_t codec, int samplerate, int mixdown, in
                 *high = channels * 64;
                 if (channels == 6)
                 {
+                    *low = 80;
                     *high = 320;
                 }
             }
             break;
 
+        case HB_ACODEC_CA_HAAC:
+            if (samplerate > 32000)
+            {
+                *low = channels * 16;
+                *high = channels * 40;
+                if (channels == 6)
+                {
+                    *low = 80;
+                    *high = 192;
+                }
+            }
+            else
+            {
+                *low = channels * 12;
+                *high = channels * 40;
+                if (channels == 6)
+                {
+                    *low = 64;
+                    *high = 192;
+                }
+            }
+            break;
+
         case HB_ACODEC_FAAC:
             *low = 32 * channels;
             if (samplerate > 24000)
@@ -280,6 +303,9 @@ int hb_get_default_audio_bitrate( uint32_t codec, int samplerate, int mixdown )
             else
                 bitrate = 640;
             break;
+        case HB_ACODEC_CA_HAAC:
+            bitrate = channels * 32;
+            break;
         default:
             bitrate = channels * 80;
     }
@@ -611,6 +637,9 @@ int hb_calc_bitrate( hb_job_t * job, int size )
             case HB_ACODEC_DCA:
                 samples_per_frame = 1536;
                 break;
+            case HB_ACODEC_CA_HAAC:
+                samples_per_frame = 2048;
+                break;
             default:
                 return 0;
         }
index b945200e74d416dda64797dcbbddafee3d7b7263..1125ec5d304f37ac60263b75f6501b419e72b8a9 100644 (file)
@@ -322,6 +322,7 @@ struct hb_job_s
 #define HB_ACODEC_DCA       0x00004000
 #define HB_ACODEC_FFMPEG    0x00008000
 #define HB_ACODEC_CA_AAC    0x00010000
+#define HB_ACODEC_CA_HAAC   0x00020000
 #define HB_ACODEC_PASS_FLAG 0x40000000
 #define HB_ACODEC_PASS_MASK (HB_ACODEC_AC3 | HB_ACODEC_DCA)
 #define HB_ACODEC_AC3_PASS  (HB_ACODEC_AC3 | HB_ACODEC_PASS_FLAG)
@@ -784,6 +785,7 @@ extern hb_work_object_t hb_enclame;
 extern hb_work_object_t hb_encvorbis;
 extern hb_work_object_t hb_muxer;
 extern hb_work_object_t hb_encca_aac;
+extern hb_work_object_t hb_encca_haac;
 extern hb_work_object_t hb_encac3;
 
 #define FILTER_OK      0
index 8588f7ff487c21a6328421f3840d281d904339e5..411e4bd66b5e94b1b914d7608c532b18287be5c2 100644 (file)
@@ -412,6 +412,7 @@ hb_handle_t * hb_init( int verbose, int update_check )
        hb_register( &hb_muxer );
 #ifdef __APPLE__
        hb_register( &hb_encca_aac );
+       hb_register( &hb_encca_haac );
 #endif
        hb_register( &hb_encac3 );
     
@@ -513,6 +514,7 @@ hb_handle_t * hb_init_dl( int verbose, int update_check )
        hb_register( &hb_muxer );
 #ifdef __APPLE__
        hb_register( &hb_encca_aac );
+       hb_register( &hb_encca_haac );
 #endif
        hb_register( &hb_encac3 );
 
@@ -1940,3 +1942,56 @@ hb_interjob_t * hb_interjob_get( hb_handle_t * h )
 {
     return h->interjob;
 }
+
+/************************************************************************
+ * encca_haac_available()
+ ************************************************************************
+ * Returns whether the Core Audio HE-AAC encoder is available for use
+ * on the system. Under 10.5, if the encoder is available, register it.
+ * The registration is actually only performed on the first call.
+ ************************************************************************/
+int encca_haac_available()
+{
+#ifdef __APPLE__
+    static int encca_haac_available = -1;
+
+    if (encca_haac_available != -1)
+        return encca_haac_available;
+
+    encca_haac_available = 0;
+
+    long minorVersion, majorVersion, quickTimeVersion;
+    Gestalt(gestaltSystemVersionMajor, &majorVersion);
+    Gestalt(gestaltSystemVersionMinor, &minorVersion);
+    Gestalt(gestaltQuickTime, &quickTimeVersion);
+
+    if (majorVersion > 10 || (majorVersion == 10 && minorVersion >= 6))
+    {
+        // OS X 10.6+ - ca_haac is available and ready to use
+        encca_haac_available = 1;
+    }
+    else if (majorVersion == 10 && minorVersion >= 5 && quickTimeVersion >= 0x07630000)
+    {
+        // OS X 10.5, QuickTime 7.6.3+ - register the component
+        ComponentDescription cd;
+        cd.componentType         = kAudioEncoderComponentType;
+        cd.componentSubType      = kAudioFormatMPEG4AAC_HE;
+        cd.componentManufacturer = kAudioUnitManufacturer_Apple;
+        cd.componentFlags        = 0;
+        cd.componentFlagsMask    = 0;
+        ComponentResult (*ComponentRoutine) (ComponentParameters * cp, Handle componentStorage);
+        void *handle = dlopen("/System/Library/Components/AudioCodecs.component/Contents/MacOS/AudioCodecs", RTLD_LAZY|RTLD_LOCAL);
+        if (handle)
+        {
+            ComponentRoutine = dlsym(handle, "ACMP4AACHighEfficiencyEncoderEntry");
+            if (ComponentRoutine)
+                if (RegisterComponent(&cd, ComponentRoutine, 0, NULL, NULL, NULL))
+                    encca_haac_available = 1;
+        }
+    }
+
+    return encca_haac_available;
+#else
+    return 0;
+#endif
+}
index 783da11cd0db5fe039c00ebde14cd5a555b0fe44..0a3af6019b51ef05baad9b433f3da6bb2107bd1b 100644 (file)
@@ -8,6 +8,14 @@ extern "C" {
 #include "project.h"
 #include "common.h"
 
+#ifdef __APPLE__
+#include <CoreServices/CoreServices.h> // for Gestalt
+#include <AudioToolbox/AudioToolbox.h>
+#include <dlfcn.h>
+#endif
+/* Whether the Core Audio HE-AAC encoder is available on the system. */
+int encca_haac_available();
+
 /* hb_init()
    Initializes a libhb session (launches his own thread, detects CPUs,
    etc) */
index 5d71cdadfd9b1c5a1ced12c14b400d604017f1a3..eec560efb4c8c74d378db6f4ab57cbc3e780f004 100644 (file)
@@ -357,6 +357,7 @@ enum
     WORK_ENCLAME,
     WORK_ENCVORBIS,
     WORK_ENC_CA_AAC,
+    WORK_ENC_CA_HAAC,
     WORK_ENCAC3,
     WORK_MUX
 };
index ddb0028d12b40b111920688dcefe4241b3dc9cf3..445447f9c97e0d4bbad460bcad9a3442ebbd7404 100644 (file)
@@ -230,6 +230,7 @@ static int MKVInit( hb_mux_object_t * m )
                 break;
             case HB_ACODEC_FAAC:
             case HB_ACODEC_CA_AAC:
+            case HB_ACODEC_CA_HAAC:
                 track->codecPrivate = audio->priv.config.aac.bytes;
                 track->codecPrivateSize = audio->priv.config.aac.length;
                 track->codecID = MK_ACODEC_AAC;
index b1148819aad2a897d7d3de6721bf5c45b9186698..3df70dc175e3857ccbbb2e32a7ea564f702325ee 100644 (file)
@@ -420,11 +420,13 @@ rate_found2:
             }
         } 
         else if( audio->config.out.codec == HB_ACODEC_FAAC ||
-                 audio->config.out.codec == HB_ACODEC_CA_AAC ) 
+                 audio->config.out.codec == HB_ACODEC_CA_AAC ||
+                 audio->config.out.codec == HB_ACODEC_CA_HAAC ) 
         {
+            int samples_per_frame = ( audio->config.out.codec == HB_ACODEC_CA_HAAC ) ? 2048 : 1024;
             mux_data->track = MP4AddAudioTrack(
                 m->file,
-                audio->config.out.samplerate, 1024, MP4_MPEG4_AUDIO_TYPE );
+                audio->config.out.samplerate, samples_per_frame, MP4_MPEG4_AUDIO_TYPE );
 
             /* Tune track chunk duration */
             MP4TuneTrackDurationPerChunk( m, mux_data->track );
index 21b458960ae3f4d2428007f4b0c40484846ec47d..85c9c2d88f64474515fd9a79d20042dc4bfa35c8 100644 (file)
@@ -6,7 +6,11 @@
 #include <AudioToolbox/AudioToolbox.h>
 #include <CoreAudio/CoreAudio.h>
 
-int     encCoreAudioInit( hb_work_object_t *, hb_job_t * );
+enum AAC_MODE { AAC_MODE_LC, AAC_MODE_HE };
+
+int     encCoreAudioInitLC( hb_work_object_t *, hb_job_t * );
+int     encCoreAudioInitHE( hb_work_object_t *, hb_job_t * );
+int     encCoreAudioInit( hb_work_object_t *, hb_job_t *, enum AAC_MODE mode );
 int     encCoreAudioWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
 void    encCoreAudioClose( hb_work_object_t * );
 
@@ -14,7 +18,16 @@ hb_work_object_t hb_encca_aac =
 {
     WORK_ENC_CA_AAC,
     "AAC encoder (Apple)",
-    encCoreAudioInit,
+    encCoreAudioInitLC,
+    encCoreAudioWork,
+    encCoreAudioClose
+};
+
+hb_work_object_t hb_encca_haac =
+{
+    WORK_ENC_CA_HAAC,
+    "HE-AAC encoder (Apple)",
+    encCoreAudioInitHE,
     encCoreAudioWork,
     encCoreAudioClose
 };
@@ -92,12 +105,27 @@ static long ReadESDSDescExt(void* descExt, UInt8 **buffer, UInt32 *size, int ver
        return noErr;
 }
 
+/***********************************************************************
+ * hb_work_encCoreAudio_init switches
+ ***********************************************************************
+ *
+ **********************************************************************/
+int encCoreAudioInitLC( hb_work_object_t * w, hb_job_t * job )
+{
+    return encCoreAudioInit( w, job, AAC_MODE_LC );
+}
+
+int encCoreAudioInitHE( hb_work_object_t * w, hb_job_t * job )
+{
+    return encCoreAudioInit( w, job, AAC_MODE_HE );
+}
+
 /***********************************************************************
  * hb_work_encCoreAudio_init
  ***********************************************************************
  *
  **********************************************************************/
-int encCoreAudioInit( hb_work_object_t * w, hb_job_t * job )
+int encCoreAudioInit( hb_work_object_t * w, hb_job_t * job, enum AAC_MODE mode )
 {
     hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) );
     hb_audio_t * audio = w->audio;
@@ -122,7 +150,16 @@ int encCoreAudioInit( hb_work_object_t * w, hb_job_t * job )
     input.mBitsPerChannel = 32;
 
     bzero( &output, sizeof( AudioStreamBasicDescription ) );
-    output.mFormatID = kAudioFormatMPEG4AAC;
+    switch ( mode ) 
+    {
+        case AAC_MODE_HE:
+            output.mFormatID = kAudioFormatMPEG4AAC_HE;
+            break;
+        case AAC_MODE_LC:
+        default:
+            output.mFormatID = kAudioFormatMPEG4AAC;
+            break;
+    }
     output.mSampleRate = ( Float64 ) audio->config.out.samplerate;
     output.mChannelsPerFrame = pv->nchannels;
     // let CoreAudio decide the rest...
@@ -133,7 +170,16 @@ int encCoreAudioInit( hb_work_object_t * w, hb_job_t * job )
     {
         // Retry without the samplerate
         bzero( &output, sizeof( AudioStreamBasicDescription ) );
-        output.mFormatID = kAudioFormatMPEG4AAC;
+        switch ( mode )
+        {
+            case AAC_MODE_HE:
+                output.mFormatID = kAudioFormatMPEG4AAC_HE;
+                break;
+            case AAC_MODE_LC:
+            default:
+                output.mFormatID = kAudioFormatMPEG4AAC;
+                break;
+        }
         output.mChannelsPerFrame = pv->nchannels;
 
         err = AudioConverterNew( &input, &output, &pv->converter );
@@ -161,7 +207,7 @@ int encCoreAudioInit( hb_work_object_t * w, hb_job_t * job )
     // set encoder bitrate control mode to constrained variable
     tmp = kAudioCodecBitRateControlMode_VariableConstrained;
     AudioConverterSetProperty( pv->converter, kAudioCodecPropertyBitRateControlMode,
-                              sizeof( tmp ), &tmp );
+                               sizeof( tmp ), &tmp );
 
     // get available bitrates
     AudioValueRange *bitrates;
@@ -180,8 +226,11 @@ int encCoreAudioInit( hb_work_object_t * w, hb_job_t * job )
     if( tmp > bitrates[bitrateCounts-1].mMinimum )
         tmp = bitrates[bitrateCounts-1].mMinimum;
     free( bitrates );
+    if( tmp != audio->config.out.bitrate * 1000 )
+        hb_log( "encca_aac: sanitizing track %d audio bitrate %d to %"PRIu32"", 
+                audio->config.out.track, audio->config.out.bitrate, tmp/1000 );
     AudioConverterSetProperty( pv->converter, kAudioConverterEncodeBitRate,
-                              sizeof( tmp ), &tmp );
+                               sizeof( tmp ), &tmp );
 
     // get real input
     tmpsiz = sizeof( input );
@@ -256,7 +305,7 @@ static OSStatus inInputDataProc( AudioConverterRef converter, UInt32 *npackets,
     {
         *npackets = 0;
         hb_log( "CoreAudio: no data to use in inInputDataProc" );
-        return noErr;
+        return 1;
     }
 
     if( pv->buf != NULL )
index 8c182fa66edda8a21b9c961cadf1782d9e33cb51..29dac4d7ab9f323cce88d260e4463703315773e3 100644 (file)
@@ -127,6 +127,7 @@ hb_work_object_t * hb_codec_encoder( int codec )
         case HB_ACODEC_LAME:   return hb_get_work( WORK_ENCLAME );
         case HB_ACODEC_VORBIS: return hb_get_work( WORK_ENCVORBIS );
         case HB_ACODEC_CA_AAC: return hb_get_work( WORK_ENC_CA_AAC );
+        case HB_ACODEC_CA_HAAC:return hb_get_work( WORK_ENC_CA_HAAC );
         case HB_ACODEC_AC3:    return hb_get_work( WORK_ENCAC3 );
     }
     return NULL;
@@ -369,8 +370,9 @@ void hb_display_job_info( hb_job_t * job )
                     ( audio->config.out.codec == HB_ACODEC_FAAC ) ?  "faac" : 
                     ( ( audio->config.out.codec == HB_ACODEC_LAME ) ?  "lame" : 
                     ( ( audio->config.out.codec == HB_ACODEC_CA_AAC ) ?  "ca_aac" : 
+                    ( ( audio->config.out.codec == HB_ACODEC_CA_HAAC ) ?  "ca_haac" : 
                     ( ( audio->config.out.codec == HB_ACODEC_AC3 ) ?  "ffac3" : 
-                    "vorbis"  ) ) ) );
+                    "vorbis"  ) ) ) ) );
                 hb_log( "     + bitrate: %d kbps, samplerate: %d Hz", audio->config.out.bitrate, audio->config.out.samplerate );            
             }
         }
@@ -556,6 +558,23 @@ static void do_job( hb_job_t * job )
                     audio->config.out.bitrate );
             audio->config.out.bitrate = 640;
         }
+        if( audio->config.out.codec == HB_ACODEC_CA_HAAC )
+        {
+            if( !encca_haac_available() )
+            {
+                // user chose Core Audio HE-AAC but the encoder is unavailable
+                hb_log( "Core Audio HE-AAC unavailable. Using Core Audio AAC for track %d",
+                        audio->config.out.track );
+                audio->config.out.codec = HB_ACODEC_CA_AAC;
+            }
+            else if( audio->config.out.samplerate < 32000 )
+            {
+                // Core Audio HE-AAC doesn't support samplerates < 32 kHz
+                hb_log( "Sample rate %d not supported (ca_haac). Using 32kHz for track %d",
+                        audio->config.out.samplerate, audio->config.out.track );
+                audio->config.out.samplerate = 32000;
+            }
+        }
         /* Adjust output track number, in case we removed one.
          * Output tracks sadly still need to be in sequential order.
          */
index c1c0cd5910d8e21c66aaf61e16c92cc090d4687b..7be16023f071d385140cf191842c543de7b109ad 100644 (file)
@@ -75,6 +75,15 @@ static NSMutableArray *masterBitRateArray = nil;
                                                                          [NSNumber numberWithBool: YES], keyAudioMKV,
                                                                          [NSNumber numberWithBool: NO], keyAudioMustMatchTrack,
                                                                          nil]];
+               if (encca_haac_available()) {
+                       [masterCodecArray addObject: [NSDictionary dictionaryWithObjectsAndKeys:
+                                                                         NSLocalizedString(@"HE-AAC (CoreAudio)", @"HE-AAC (CoreAudio)"), keyAudioCodecName,
+                                                                         [NSNumber numberWithInt: HB_ACODEC_CA_HAAC], keyAudioCodec,
+                                                                         [NSNumber numberWithBool: YES], keyAudioMP4,
+                                                                         [NSNumber numberWithBool: YES], keyAudioMKV,
+                                                                         [NSNumber numberWithBool: NO], keyAudioMustMatchTrack,
+                                                                         nil]];
+               }
                [masterCodecArray addObject: [NSDictionary dictionaryWithObjectsAndKeys:
                                                                          NSLocalizedString(@"AAC (faac)", @"AAC (faac)"), keyAudioCodecName,
                                                                          [NSNumber numberWithInt: HB_ACODEC_FAAC], keyAudioCodec,
index c89267b9feca88e99e8f7da4f9928931f3097db9..7cf65a5cf65972935c62e79012be1aee9982cc75 100644 (file)
@@ -2538,7 +2538,7 @@ static void ShowHelp()
 #ifdef __APPLE_CC__
     fprintf( out,
     "    -E, --aencoder <string> Audio encoder(s)\n"
-    "                                (ca_aac/faac/lame/vorbis/ac3/copy/copy:ac3/copy:dts)\n"
+    "                                (ca_aac/ca_haac/faac/lame/vorbis/ac3/copy/copy:ac3/copy:dts)\n"
     "                            copy, copy:ac3 and copy:dts meaning passthrough.\n"
     "                            copy will passthrough either ac3 or dts.\n"
     "                            Separated by commas for more than one audio track.\n"
@@ -3594,6 +3594,10 @@ static int get_acodec_for_string( char *codec )
     {
         return HB_ACODEC_CA_AAC;
     }
+    else if( !strcasecmp( codec, "ca_haac") )
+    {
+        return HB_ACODEC_CA_HAAC;
+    }
 #endif
     else
     {