diff options
author | ritsuka <[email protected]> | 2009-05-23 09:46:36 +0000 |
---|---|---|
committer | ritsuka <[email protected]> | 2009-05-23 09:46:36 +0000 |
commit | 9cd4b18874948ed58b5724de086338eb3c036b07 (patch) | |
tree | 4de94af339e96401dc4c77d0741643a0cb0ed82e /libhb/platform | |
parent | 8690c484f325fab8335f8a87a10406726994961d (diff) |
- Add CoreAudio AAC as one of the encoder on Mac OS X.
- Remove hb_init() and hb_init_express() macro. Rename hb_init_real() to hb_init()
- Add two more bitrate combination for audio codecs in common.h
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@2441 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'libhb/platform')
-rw-r--r-- | libhb/platform/macosx/encca_aac.c | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/libhb/platform/macosx/encca_aac.c b/libhb/platform/macosx/encca_aac.c new file mode 100644 index 000000000..69b29ec4b --- /dev/null +++ b/libhb/platform/macosx/encca_aac.c @@ -0,0 +1,341 @@ +/* This file is part of the HandBrake source code. + Homepage: <http://handbrake.fr/>. + It may be used under the terms of the GNU General Public License. */ + +#include "hb.h" +#include <AudioToolbox/AudioToolbox.h> +#include <CoreAudio/CoreAudio.h> + +int encCoreAudioInit( hb_work_object_t *, hb_job_t * ); +int encCoreAudioWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); +void encCoreAudioClose( hb_work_object_t * ); + +hb_work_object_t hb_encca_aac = +{ + WORK_ENC_CA_AAC, + "AAC encoder (Apple)", + encCoreAudioInit, + encCoreAudioWork, + encCoreAudioClose +}; + +struct hb_work_private_s +{ + hb_job_t *job; + + AudioConverterRef converter; + uint8_t *obuf; + uint8_t *buf; + hb_list_t *list; + unsigned long isamples, isamplesiz, omaxpacket, nchannels; + uint64_t pts, ibytes; +}; + +#define MP4ESDescrTag 0x03 +#define MP4DecConfigDescrTag 0x04 +#define MP4DecSpecificDescrTag 0x05 + +// based off of mov_mp4_read_descr_len from mov.c in ffmpeg's libavformat +static int readDescrLen(UInt8 **buffer) +{ + int len = 0; + int count = 4; + while (count--) { + int c = *(*buffer)++; + len = (len << 7) | (c & 0x7f); + if (!(c & 0x80)) + break; + } + return len; +} + +// based off of mov_mp4_read_descr from mov.c in ffmpeg's libavformat +static int readDescr(UInt8 **buffer, int *tag) +{ + *tag = *(*buffer)++; + return readDescrLen(buffer); +} + +// based off of mov_read_esds from mov.c in ffmpeg's libavformat +static long ReadESDSDescExt(void* descExt, UInt8 **buffer, int *size, int versionFlags) +{ + UInt8 *esds = (UInt8 *) descExt; + int tag, len; + *size = 0; + + if (versionFlags) + esds += 4; // version + flags + readDescr(&esds, &tag); + esds += 2; // ID + if (tag == MP4ESDescrTag) + esds++; // priority + + readDescr(&esds, &tag); + if (tag == MP4DecConfigDescrTag) { + esds++; // object type id + esds++; // stream type + esds += 3; // buffer size db + esds += 4; // max bitrate + esds += 4; // average bitrate + + len = readDescr(&esds, &tag); + if (tag == MP4DecSpecificDescrTag) { + *buffer = calloc(1, len + 8); + if (*buffer) { + memcpy(*buffer, esds, len); + *size = len; + } + } + } + + return noErr; +} + +/*********************************************************************** + * hb_work_encCoreAudio_init + *********************************************************************** + * + **********************************************************************/ +int encCoreAudioInit( hb_work_object_t * w, hb_job_t * job ) +{ + hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); + hb_audio_t * audio = w->audio; + AudioStreamBasicDescription input, output; + UInt32 tmp, tmpsiz = sizeof( tmp ); + OSStatus err; + + w->private_data = pv; + pv->job = job; + + // pass the number of channels used into the private work data + pv->nchannels = HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT( audio->config.out.mixdown ); + + bzero( &input, sizeof( AudioStreamBasicDescription ) ); + input.mSampleRate = ( Float64 ) audio->config.out.samplerate; + input.mFormatID = kAudioFormatLinearPCM; + input.mFormatFlags = kLinearPCMFormatFlagIsFloat | kAudioFormatFlagsNativeEndian; + input.mBytesPerPacket = 4 * pv->nchannels; + input.mFramesPerPacket = 1; + input.mBytesPerFrame = input.mBytesPerPacket * input.mFramesPerPacket; + input.mChannelsPerFrame = pv->nchannels; + input.mBitsPerChannel = 32; + + bzero( &output, sizeof( AudioStreamBasicDescription ) ); + output.mSampleRate = ( Float64 ) audio->config.out.samplerate; + output.mFormatID = kAudioFormatMPEG4AAC; + output.mChannelsPerFrame = pv->nchannels; + // let CoreAudio decide the rest... + + // initialise encoder + err = AudioConverterNew( &input, &output, &pv->converter ); + if( err != noErr) + { + hb_log( "Error creating an AudioConverter %x %d", err, output.mBytesPerFrame ); + *job->die = 1; + return 0; + } + + if( audio->config.out.mixdown == HB_AMIXDOWN_6CH && audio->config.in.codec == HB_ACODEC_AC3 ) + { + SInt32 channelMap[6] = { 2, 1, 3, 4, 5, 0 }; + AudioConverterSetProperty( pv->converter, kAudioConverterChannelMap, + sizeof( channelMap ), channelMap ); + } + + // set encoder quality to maximum + tmp = kAudioConverterQuality_Max; + AudioConverterSetProperty( pv->converter, kAudioConverterCodecQuality, + sizeof( tmp ), &tmp ); + + // set encoder bitrate control mode to constrained variable + tmp = kAudioCodecBitRateControlMode_VariableConstrained; + AudioConverterSetProperty( pv->converter, kAudioCodecPropertyBitRateControlMode, + sizeof( tmp ), &tmp ); + + // get available bitrates + AudioValueRange *bitrates; + ssize_t bitrateCounts, n; + err = AudioConverterGetPropertyInfo( pv->converter, kAudioConverterApplicableEncodeBitRates, + &tmpsiz, NULL); + bitrates = malloc( tmpsiz ); + err = AudioConverterGetProperty( pv->converter, kAudioConverterApplicableEncodeBitRates, + &tmpsiz, bitrates); + bitrateCounts = tmpsiz / sizeof( AudioValueRange ); + + // set bitrate + tmp = audio->config.out.bitrate * 1000; + if( tmp < bitrates[0].mMinimum ) + tmp = bitrates[0].mMinimum; + if( tmp > bitrates[bitrateCounts-1].mMinimum ) + tmp = bitrates[bitrateCounts-1].mMinimum; + free( bitrates ); + AudioConverterSetProperty( pv->converter, kAudioConverterEncodeBitRate, + sizeof( tmp ), &tmp ); + + // get real input + tmpsiz = sizeof( input ); + AudioConverterGetProperty( pv->converter, + kAudioConverterCurrentInputStreamDescription, + &tmpsiz, &input ); + // get real output + tmpsiz = sizeof( output ); + AudioConverterGetProperty( pv->converter, + kAudioConverterCurrentOutputStreamDescription, + &tmpsiz, &output ); + + // set sizes + pv->isamplesiz = input.mBytesPerPacket; + pv->isamples = output.mFramesPerPacket; + + // get maximum output size + AudioConverterGetProperty( pv->converter, + kAudioConverterPropertyMaximumOutputPacketSize, + &tmpsiz, &tmp ); + pv->omaxpacket = tmp; + + // get magic cookie (elementary stream descriptor) + tmp = HB_CONFIG_MAX_SIZE; + AudioConverterGetProperty( pv->converter, + kAudioConverterCompressionMagicCookie, + &tmp, w->config->aac.bytes ); + // CoreAudio returns a complete ESDS, but we only need + // the DecoderSpecific info. + UInt8* buffer; + ReadESDSDescExt(w->config->aac.bytes, &buffer, &tmpsiz, 0); + w->config->aac.length = tmpsiz; + memmove( w->config->aac.bytes, buffer, + w->config->aac.length ); + + pv->list = hb_list_init(); + pv->buf = NULL; + + return 0; +} + +/*********************************************************************** + * Close + *********************************************************************** + * + **********************************************************************/ +void encCoreAudioClose( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + + if( pv->converter ) + { + AudioConverterDispose( pv->converter ); + hb_list_empty( &pv->list ); + free( pv->obuf ); + free( pv->buf ); + free( pv ); + w->private_data = NULL; + } +} + +/* Called whenever necessary by AudioConverterFillComplexBuffer */ +static OSStatus inInputDataProc( AudioConverterRef converter, UInt32 *npackets, + AudioBufferList *buffers, + AudioStreamPacketDescription** ignored, + void *userdata ) +{ + hb_work_private_t *pv = userdata; + pv->ibytes = hb_list_bytes( pv->list ); + + if( pv->ibytes == 0 ) { + *npackets = 0; + return noErr; + } + + if( pv->buf != NULL ) + free( pv->buf ); + + uint64_t pts, pos; + pv->ibytes = buffers->mBuffers[0].mDataByteSize = MIN( *npackets * pv->isamplesiz, pv->ibytes ); + buffers->mBuffers[0].mData = pv->buf = malloc( buffers->mBuffers[0].mDataByteSize ); + + hb_list_getbytes( pv->list, buffers->mBuffers[0].mData, + buffers->mBuffers[0].mDataByteSize, &pts, &pos ); + + *npackets = buffers->mBuffers[0].mDataByteSize / pv->isamplesiz; + + /* transform data from [-32768,32767] to [-1.0,1.0] */ + float *fdata = buffers->mBuffers[0].mData; + int i; + + for( i = 0; i < *npackets * pv->nchannels; i++ ) + fdata[i] = fdata[i] / 32768.f; + + return noErr; +} + +/*********************************************************************** + * Encode + *********************************************************************** + * + **********************************************************************/ +static hb_buffer_t * Encode( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + UInt32 npackets = 1; + + /* check if we need more data */ + if( hb_list_bytes( pv->list ) < pv->isamples * pv->isamplesiz ) + return NULL; + + hb_buffer_t * obuf; + AudioStreamPacketDescription odesc = { 0 }; + AudioBufferList obuflist = { .mNumberBuffers = 1, + .mBuffers = { { .mNumberChannels = pv->nchannels } }, + }; + + obuf = hb_buffer_init( pv->omaxpacket ); + obuflist.mBuffers[0].mDataByteSize = obuf->size; + obuflist.mBuffers[0].mData = obuf->data; + + AudioConverterFillComplexBuffer( pv->converter, inInputDataProc, pv, + &npackets, &obuflist, &odesc ); + + if( odesc.mDataByteSize == 0 ) + return NULL; + + obuf->start = pv->pts; + pv->pts += 90000LL * pv->isamples / w->audio->config.out.samplerate; + obuf->stop = pv->pts; + obuf->size = odesc.mDataByteSize; + obuf->frametype = HB_FRAME_AUDIO; + + return obuf; +} + +/*********************************************************************** + * Work + *********************************************************************** + * + **********************************************************************/ +int encCoreAudioWork( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_work_private_t * pv = w->private_data; + hb_buffer_t * buf; + + if( (*buf_in)->size <= 0 ) + { + // EOF on input - send it downstream & say we're done + *buf_out = *buf_in; + *buf_in = NULL; + return HB_WORK_DONE; + } + + hb_list_add( pv->list, *buf_in ); + *buf_in = NULL; + + *buf_out = buf = Encode( w ); + + while( buf ) + { + buf->next = Encode( w ); + buf = buf->next; + } + + return HB_WORK_OK; +} |