/*********************************************************************** * hb_work_encCoreAudio_init *********************************************************************** * **********************************************************************/ 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; 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_mixdown_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)); 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 // initialise encoder err = AudioConverterNew(&input, &output, &pv->converter); if (err != noErr) { // Retry without the samplerate bzero(&output, sizeof(AudioStreamBasicDescription)); 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); if (err != noErr) { hb_log("Error creating an AudioConverter err=%"PRId64" output.mBytesPerFrame=%"PRIu64"", (int64_t)err, (uint64_t)output.mBytesPerFrame); *job->done_error = HB_ERROR_UNKNOWN; *job->die = 1; return -1; } } // set encoder quality to maximum tmp = kAudioConverterQuality_Max; AudioConverterSetProperty(pv->converter, kAudioConverterCodecQuality, sizeof(tmp), &tmp); if (audio->config.out.bitrate > 0) { // 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; 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); if (tmp != audio->config.out.bitrate * 1000) { hb_log("encCoreAudioInit: 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); } else if (audio->config.out.quality >= 0) { if (mode != AAC_MODE_LC) { hb_error("encCoreAudioInit: internal error, VBR set but not applicable"); return 1; } // set encoder bitrate control mode to variable tmp = kAudioCodecBitRateControlMode_Variable; AudioConverterSetProperty(pv->converter, kAudioCodecPropertyBitRateControlMode, sizeof(tmp), &tmp); // set quality tmp = audio->config.out.quality; AudioConverterSetProperty(pv->converter, kAudioCodecPropertySoundQualityForVBR, sizeof(tmp), &tmp); } else { hb_error("encCoreAudioInit: internal error, bitrate/quality not set"); return 1; } // 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; pv->osamplerate = output.mSampleRate; audio->config.out.samples_per_frame = pv->isamples; // channel remapping pv->remap = hb_audio_remap_init(AV_SAMPLE_FMT_FLT, &hb_aac_chan_map, audio->config.in.channel_map); if (pv->remap == NULL) { hb_error("encCoreAudioInit: hb_audio_remap_init() failed"); } uint64_t layout = hb_ff_mixdown_xlat(audio->config.out.mixdown, NULL); hb_audio_remap_set_channel_layout(pv->remap, layout); // 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->extradata.bytes); // CoreAudio returns a complete ESDS, but we only need // the DecoderSpecific info. UInt8* buffer = NULL; ReadESDSDescExt(w->config->extradata.bytes, &buffer, &tmpsiz, 0); w->config->extradata.length = tmpsiz; memmove(w->config->extradata.bytes, buffer, w->config->extradata.length); free(buffer); pv->list = hb_list_init(); pv->buf = NULL; return 0; }
/*********************************************************************** * hb_work_encCoreAudio_init *********************************************************************** * **********************************************************************/ 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; 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 ) ); 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... // initialise encoder err = AudioConverterNew( &input, &output, &pv->converter ); if( err != noErr) { // Retry without the samplerate bzero( &output, sizeof( AudioStreamBasicDescription ) ); 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 ); if( err != noErr) { hb_log( "Error creating an AudioConverter err=%"PRId64" %"PRIu64, (int64_t)err, (uint64_t)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; 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 ); 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 ); // 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; pv->osamplerate = output.mSampleRate; // 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 = NULL; 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; }