static int tdav_consumer_audiounit_prepare(tmedia_consumer_t* self, const tmedia_codec_t* codec) { static UInt32 flagOne = 1; AudioStreamBasicDescription audioFormat; #define kOutputBus 0 tdav_consumer_audiounit_t* consumer = (tdav_consumer_audiounit_t*)self; OSStatus status = noErr; if(!consumer || !codec || !codec->plugin){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if(!consumer->audioUnitHandle){ if(!(consumer->audioUnitHandle = tdav_audiounit_handle_create(TMEDIA_CONSUMER(consumer)->session_id))){ TSK_DEBUG_ERROR("Failed to get audio unit instance for session with id=%lld", TMEDIA_CONSUMER(consumer)->session_id); return -3; } } // enable status = AudioUnitSetProperty(tdav_audiounit_handle_get_instance(consumer->audioUnitHandle), kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flagOne, sizeof(flagOne)); if(status){ TSK_DEBUG_ERROR("AudioUnitSetProperty(kAudioOutputUnitProperty_EnableIO) failed with status=%d", (int32_t)status); return -4; } else { #if !TARGET_OS_IPHONE // strange: TARGET_OS_MAC is equal to '1' on Smulator UInt32 param; // disable input param = 0; status = AudioUnitSetProperty(tdav_audiounit_handle_get_instance(consumer->audioUnitHandle), kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, ¶m, sizeof(UInt32)); if(status != noErr){ TSK_DEBUG_ERROR("AudioUnitSetProperty(kAudioOutputUnitProperty_EnableIO) failed with status=%ld", (signed long)status); return -4; } // set default audio device param = sizeof(AudioDeviceID); AudioDeviceID outputDeviceID; status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, ¶m, &outputDeviceID); if(status != noErr){ TSK_DEBUG_ERROR("AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice) failed with status=%ld", (signed long)status); return -4; } // set the current device to the default input unit status = AudioUnitSetProperty(tdav_audiounit_handle_get_instance(consumer->audioUnitHandle), kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &outputDeviceID, sizeof(AudioDeviceID)); if(status != noErr){ TSK_DEBUG_ERROR("AudioUnitSetProperty(kAudioOutputUnitProperty_CurrentDevice) failed with status=%ld", (signed long)status); return -4; } #endif TMEDIA_CONSUMER(consumer)->audio.ptime = TMEDIA_CODEC_PTIME_AUDIO_DECODING(codec); TMEDIA_CONSUMER(consumer)->audio.in.channels = TMEDIA_CODEC_CHANNELS_AUDIO_DECODING(codec); TMEDIA_CONSUMER(consumer)->audio.in.rate = TMEDIA_CODEC_RATE_DECODING(codec); TSK_DEBUG_INFO("AudioUnit consumer: in.channels=%d, out.channles=%d, in.rate=%d, out.rate=%d, ptime=%d", TMEDIA_CONSUMER(consumer)->audio.in.channels, TMEDIA_CONSUMER(consumer)->audio.out.channels, TMEDIA_CONSUMER(consumer)->audio.in.rate, TMEDIA_CONSUMER(consumer)->audio.out.rate, TMEDIA_CONSUMER(consumer)->audio.ptime); audioFormat.mSampleRate = TMEDIA_CONSUMER(consumer)->audio.out.rate ? TMEDIA_CONSUMER(consumer)->audio.out.rate : TMEDIA_CONSUMER(consumer)->audio.in.rate; audioFormat.mFormatID = kAudioFormatLinearPCM; audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; audioFormat.mChannelsPerFrame = TMEDIA_CONSUMER(consumer)->audio.in.channels; audioFormat.mFramesPerPacket = 1; audioFormat.mBitsPerChannel = TMEDIA_CONSUMER(consumer)->audio.bits_per_sample; audioFormat.mBytesPerPacket = audioFormat.mBitsPerChannel / 8 * audioFormat.mChannelsPerFrame; audioFormat.mBytesPerFrame = audioFormat.mBytesPerPacket; audioFormat.mReserved = 0; status = AudioUnitSetProperty(tdav_audiounit_handle_get_instance(consumer->audioUnitHandle), kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &audioFormat, sizeof(audioFormat)); if(status){ TSK_DEBUG_ERROR("AudioUnitSetProperty(kAudioUnitProperty_StreamFormat) failed with status=%ld", (signed long)status); return -5; } else { // configure if(tdav_audiounit_handle_configure(consumer->audioUnitHandle, tsk_true, TMEDIA_CONSUMER(consumer)->audio.ptime, &audioFormat)){ TSK_DEBUG_ERROR("tdav_audiounit_handle_set_rate(%d) failed", TMEDIA_CONSUMER(consumer)->audio.out.rate); return -4; } // set callback function AURenderCallbackStruct callback; callback.inputProc = __handle_output_buffer; callback.inputProcRefCon = consumer; status = AudioUnitSetProperty(tdav_audiounit_handle_get_instance(consumer->audioUnitHandle), kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback)); if(status){ TSK_DEBUG_ERROR("AudioUnitSetProperty(kAudioOutputUnitProperty_SetInputCallback) failed with status=%ld", (signed long)status); return -6; } } } // allocate the chunck buffer and create the ring consumer->ring.chunck.size = (TMEDIA_CONSUMER(consumer)->audio.ptime * audioFormat.mSampleRate * audioFormat.mBytesPerFrame) / 1000; consumer->ring.size = kRingPacketCount * consumer->ring.chunck.size; if(!(consumer->ring.chunck.buffer = tsk_realloc(consumer->ring.chunck.buffer, consumer->ring.chunck.size))){ TSK_DEBUG_ERROR("Failed to allocate new buffer"); return -7; } if(!consumer->ring.buffer){ consumer->ring.buffer = speex_buffer_init(consumer->ring.size); } else { int ret; if((ret = speex_buffer_resize(consumer->ring.buffer, consumer->ring.size)) < 0){ TSK_DEBUG_ERROR("speex_buffer_resize(%d) failed with error code=%d", consumer->ring.size, ret); return ret; } } if(!consumer->ring.buffer){ TSK_DEBUG_ERROR("Failed to create a new ring buffer with size = %d", consumer->ring.size); return -8; } if(!consumer->ring.mutex && !(consumer->ring.mutex = tsk_mutex_create_2(tsk_false))){ TSK_DEBUG_ERROR("Failed to create mutex"); return -9; } // set maximum frames per slice as buffer size //UInt32 numFrames = (UInt32)consumer->ring.chunck.size; //status = AudioUnitSetProperty(tdav_audiounit_handle_get_instance(consumer->audioUnitHandle), // kAudioUnitProperty_MaximumFramesPerSlice, // kAudioUnitScope_Global, // 0, // &numFrames, // sizeof(numFrames)); //if(status){ // TSK_DEBUG_ERROR("AudioUnitSetProperty(kAudioUnitProperty_MaximumFramesPerSlice, %u) failed with status=%d", (unsigned)numFrames, (int32_t)status); // return -6; //} TSK_DEBUG_INFO("AudioUnit consumer prepared"); return tdav_audiounit_handle_signal_consumer_prepared(consumer->audioUnitHandle); }
static int tdav_producer_audiounit_prepare(tmedia_producer_t* self, const tmedia_codec_t* codec) { static UInt32 flagOne = 1; UInt32 param; // static UInt32 flagZero = 0; #define kInputBus 1 tdav_producer_audiounit_t* producer = (tdav_producer_audiounit_t*)self; OSStatus status = noErr; AudioStreamBasicDescription audioFormat; AudioStreamBasicDescription deviceFormat; if(!producer || !codec || !codec->plugin){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if(!producer->audioUnitHandle){ if(!(producer->audioUnitHandle = tdav_audiounit_handle_create(TMEDIA_PRODUCER(producer)->session_id))){ TSK_DEBUG_ERROR("Failed to get audio unit instance for session with id=%lld", TMEDIA_PRODUCER(producer)->session_id); return -3; } } // enable status = AudioUnitSetProperty(tdav_audiounit_handle_get_instance(producer->audioUnitHandle), kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flagOne, sizeof(flagOne)); if(status != noErr){ TSK_DEBUG_ERROR("AudioUnitSetProperty(kAudioOutputUnitProperty_EnableIO) failed with status=%ld", (signed long)status); return -4; } else { #if !TARGET_OS_IPHONE // strange: TARGET_OS_MAC is equal to '1' on Smulator // disable output param = 0; status = AudioUnitSetProperty(tdav_audiounit_handle_get_instance(producer->audioUnitHandle), kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, ¶m, sizeof(UInt32)); if(status != noErr){ TSK_DEBUG_ERROR("AudioUnitSetProperty(kAudioOutputUnitProperty_EnableIO) failed with status=%ld", (signed long)status); return -4; } // set default audio device param = sizeof(AudioDeviceID); AudioDeviceID inputDeviceID; status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, ¶m, &inputDeviceID); if(status != noErr){ TSK_DEBUG_ERROR("AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice) failed with status=%ld", (signed long)status); return -4; } // set the current device to the default input unit status = AudioUnitSetProperty(tdav_audiounit_handle_get_instance(producer->audioUnitHandle), kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Output, 0, &inputDeviceID, sizeof(AudioDeviceID)); if(status != noErr){ TSK_DEBUG_ERROR("AudioUnitSetProperty(kAudioOutputUnitProperty_CurrentDevice) failed with status=%ld", (signed long)status); return -4; } #endif /* TARGET_OS_MAC */ /* codec should have ptime */ TMEDIA_PRODUCER(producer)->audio.channels = codec->plugin->audio.channels; TMEDIA_PRODUCER(producer)->audio.rate = codec->plugin->rate; TMEDIA_PRODUCER(producer)->audio.ptime = codec->plugin->audio.ptime; // get device format param = sizeof(AudioStreamBasicDescription); status = AudioUnitGetProperty(tdav_audiounit_handle_get_instance(producer->audioUnitHandle), kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kInputBus, &deviceFormat, ¶m); if(status == noErr && deviceFormat.mSampleRate){ #if TARGET_OS_IPHONE // iOS support 8Khz, 16kHz and 32kHz => do not override the sampleRate #elif TARGET_OS_MAC // For example, iSight supports only 48kHz TMEDIA_PRODUCER(producer)->audio.rate = deviceFormat.mSampleRate; #endif } // set format audioFormat.mSampleRate = TMEDIA_PRODUCER(producer)->audio.rate; audioFormat.mFormatID = kAudioFormatLinearPCM; audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved; audioFormat.mChannelsPerFrame = TMEDIA_PRODUCER(producer)->audio.channels; audioFormat.mFramesPerPacket = 1; audioFormat.mBitsPerChannel = TMEDIA_PRODUCER(producer)->audio.bits_per_sample; audioFormat.mBytesPerPacket = audioFormat.mBitsPerChannel / 8 * audioFormat.mChannelsPerFrame; audioFormat.mBytesPerFrame = audioFormat.mBytesPerPacket; audioFormat.mReserved = 0; if(audioFormat.mFormatID == kAudioFormatLinearPCM && audioFormat.mChannelsPerFrame == 1){ audioFormat.mFormatFlags &= ~kLinearPCMFormatFlagIsNonInterleaved; } status = AudioUnitSetProperty(tdav_audiounit_handle_get_instance(producer->audioUnitHandle), kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &audioFormat, sizeof(audioFormat)); if(status){ TSK_DEBUG_ERROR("AudioUnitSetProperty(kAudioUnitProperty_StreamFormat) failed with status=%ld", (signed long)status); return -5; } else { // configure if(tdav_audiounit_handle_configure(producer->audioUnitHandle, tsk_false, TMEDIA_PRODUCER(producer)->audio.ptime, &audioFormat)){ TSK_DEBUG_ERROR("tdav_audiounit_handle_set_rate(%d) failed", TMEDIA_PRODUCER(producer)->audio.rate); return -4; } // set callback function AURenderCallbackStruct callback; callback.inputProc = __handle_input_buffer; callback.inputProcRefCon = producer; status = AudioUnitSetProperty(tdav_audiounit_handle_get_instance(producer->audioUnitHandle), kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Output, kInputBus, &callback, sizeof(callback)); if(status){ TSK_DEBUG_ERROR("AudioUnitSetProperty(kAudioOutputUnitProperty_SetInputCallback) failed with status=%ld", (signed long)status); return -6; } else { // disbale buffer allocation as we will provide ours //status = AudioUnitSetProperty(tdav_audiounit_handle_get_instance(producer->audioUnitHandle), // kAudioUnitProperty_ShouldAllocateBuffer, // kAudioUnitScope_Output, // kInputBus, // &flagZero, // sizeof(flagZero)); int packetperbuffer = (1000 / codec->plugin->audio.ptime); producer->ring.chunck.size = audioFormat.mSampleRate * audioFormat.mBytesPerFrame / packetperbuffer; // allocate our chunck buffer if(!(producer->ring.chunck.buffer = tsk_realloc(producer->ring.chunck.buffer, producer->ring.chunck.size))){ TSK_DEBUG_ERROR("Failed to allocate new buffer"); return -7; } // create mutex for ring buffer if(!producer->ring.mutex && !(producer->ring.mutex = tsk_mutex_create_2(tsk_false))){ TSK_DEBUG_ERROR("Failed to create new mutex"); return -8; } // create ringbuffer producer->ring.size = kRingPacketCount * producer->ring.chunck.size; if(!producer->ring.buffer){ producer->ring.buffer = speex_buffer_init(producer->ring.size); } else { int ret; if((ret = speex_buffer_resize(producer->ring.buffer, producer->ring.size)) < 0){ TSK_DEBUG_ERROR("speex_buffer_resize(%d) failed with error code=%d", producer->ring.size, ret); return ret; } } if(!producer->ring.buffer){ TSK_DEBUG_ERROR("Failed to create a new ring buffer with size = %d", producer->ring.size); return -9; } } } } TSK_DEBUG_INFO("AudioUnit producer prepared"); return tdav_audiounit_handle_signal_producer_prepared(producer->audioUnitHandle); }