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; }