static tsk_size_t tdav_consumer_audiounit_get(tdav_consumer_audiounit_t* self, void* data, tsk_size_t size)
{
	tsk_ssize_t retSize = 0;
	
#if DISABLE_JITTER_BUFFER
	retSize = speex_buffer_read(self->ring.buffer, data, size);
	if(retSize < size){
		memset(((uint8_t*)data)+retSize, 0, (size - retSize));
	}
#else
	self->ring.leftBytes += size;
	while (self->ring.leftBytes >= self->ring.chunck.size) {
		self->ring.leftBytes -= self->ring.chunck.size;
		retSize =  (tsk_ssize_t)tdav_consumer_audio_get(TDAV_CONSUMER_AUDIO(self), self->ring.chunck.buffer, self->ring.chunck.size);
		tdav_consumer_audio_tick(TDAV_CONSUMER_AUDIO(self));
		speex_buffer_write(self->ring.buffer, self->ring.chunck.buffer, retSize);
	}
	// IMPORTANT: looks like there is a bug in speex: continously trying to read more than avail
	// many times can corrupt the buffer. At least on OS X 1.5
	if(speex_buffer_get_available(self->ring.buffer) >= size){
		retSize = speex_buffer_read(self->ring.buffer, data, size);
	}
	else{
		memset(data, 0, size);
	}
#endif

	return retSize;
}
static void *__sender_thread(void *param)
{
	TSK_DEBUG_INFO("__sender_thread::ENTER");
	
	tdav_producer_audiounit_t* producer = (tdav_producer_audiounit_t*)param;
	uint32_t ptime = TMEDIA_PRODUCER(producer)->audio.ptime;
	tsk_ssize_t avail;
	
	// interval to sleep when using nonosleep() instead of conditional variable
	struct timespec interval;
	interval.tv_sec = (long)(ptime/1000); 
	interval.tv_nsec = (long)(ptime%1000) * 1000000; 
	
	// change thread priority
//#if TARGET_OS_IPHONE
	__sender_thread_set_realtime(TMEDIA_PRODUCER(producer)->audio.ptime);
//#endif
	
	// starts looping
	for (;;) {
		// wait for "ptime" milliseconds
		if(ptime <= kMaxPtimeBeforeUsingCondVars){
			nanosleep(&interval, 0);
		}
		else {
			tsk_condwait_timedwait(producer->senderCondWait, (uint64_t)ptime);
		}
		// check state
		if(!producer->started){
			break;
		}
		// read data and send them
		if(TMEDIA_PRODUCER(producer)->enc_cb.callback) {
			tsk_mutex_lock(producer->ring.mutex);
			avail = speex_buffer_get_available(producer->ring.buffer);
			while (producer->started && avail >= producer->ring.chunck.size) {
				avail -= speex_buffer_read(producer->ring.buffer, producer->ring.chunck.buffer, producer->ring.chunck.size);
				TMEDIA_PRODUCER(producer)->enc_cb.callback(TMEDIA_PRODUCER(producer)->enc_cb.callback_data, 
														   producer->ring.chunck.buffer, producer->ring.chunck.size);
			}
			tsk_mutex_unlock(producer->ring.mutex);
		}
		else;
	}
	TSK_DEBUG_INFO("__sender_thread::EXIT");
	return tsk_null;
}
static OSStatus __handle_input_buffer(void *inRefCon, 
                                  AudioUnitRenderActionFlags *ioActionFlags, 
                                  const AudioTimeStamp *inTimeStamp, 
                                  UInt32 inBusNumber, 
                                  UInt32 inNumberFrames, 
                                  AudioBufferList *ioData) {
	OSStatus status = noErr;
	tdav_producer_audiounit_t* producer = (tdav_producer_audiounit_t*)inRefCon;
	
	// holder
	AudioBuffer buffer;
	buffer.mData =  tsk_null;
	buffer.mDataByteSize = 0;
	buffer.mNumberChannels = TMEDIA_PRODUCER(producer)->audio.channels;
	
	// list of holders
	AudioBufferList buffers;
	buffers.mNumberBuffers = 1;
	buffers.mBuffers[0] = buffer;
	
	// render to get frames from the system
	status = AudioUnitRender(tdav_audiounit_handle_get_instance(producer->audioUnitHandle), 
							 ioActionFlags, 
							 inTimeStamp, 
							 inBusNumber, 
							 inNumberFrames, 
							 &buffers);
	if(status == 0){
        // must not be done on async thread: doing it gives bad audio quality when audio+video call is done with CPU consuming codec (e.g. speex or g729)
		speex_buffer_write(producer->ring.buffer, buffers.mBuffers[0].mData, buffers.mBuffers[0].mDataByteSize);
        int avail = speex_buffer_get_available(producer->ring.buffer);
        while (producer->started && avail >= producer->ring.chunck.size) {
            avail -= speex_buffer_read(producer->ring.buffer, (void*)producer->ring.chunck.buffer, (int)producer->ring.chunck.size);
            TMEDIA_PRODUCER(producer)->enc_cb.callback(TMEDIA_PRODUCER(producer)->enc_cb.callback_data,
                                                       producer->ring.chunck.buffer, producer->ring.chunck.size);
        }
	}
	
    return status;
}