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)
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
*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)
else
bitrate = 640;
break;
+ case HB_ACODEC_CA_HAAC:
+ bitrate = channels * 32;
+ break;
default:
bitrate = channels * 80;
}
case HB_ACODEC_DCA:
samples_per_frame = 1536;
break;
+ case HB_ACODEC_CA_HAAC:
+ samples_per_frame = 2048;
+ break;
default:
return 0;
}
#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)
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
hb_register( &hb_muxer );
#ifdef __APPLE__
hb_register( &hb_encca_aac );
+ hb_register( &hb_encca_haac );
#endif
hb_register( &hb_encac3 );
hb_register( &hb_muxer );
#ifdef __APPLE__
hb_register( &hb_encca_aac );
+ hb_register( &hb_encca_haac );
#endif
hb_register( &hb_encac3 );
{
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
+}
#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) */
WORK_ENCLAME,
WORK_ENCVORBIS,
WORK_ENC_CA_AAC,
+ WORK_ENC_CA_HAAC,
WORK_ENCAC3,
WORK_MUX
};
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;
}
}
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 );
#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 * );
{
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
};
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;
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...
{
// 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 );
// 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;
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 );
{
*npackets = 0;
hb_log( "CoreAudio: no data to use in inInputDataProc" );
- return noErr;
+ return 1;
}
if( pv->buf != NULL )
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;
( 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 );
}
}
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.
*/
[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,
#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"
{
return HB_ACODEC_CA_AAC;
}
+ else if( !strcasecmp( codec, "ca_haac") )
+ {
+ return HB_ACODEC_CA_HAAC;
+ }
#endif
else
{