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; }
/* destructor */ static tsk_object_t* tdav_consumer_dsound_dtor(tsk_object_t * self) { tdav_consumer_dsound_t *dsound = self; if(dsound){ tsk_size_t i; /* stop */ if(dsound->started){ tdav_consumer_dsound_stop(self); } /* deinit base */ tdav_consumer_audio_deinit(TDAV_CONSUMER_AUDIO(dsound)); /* deinit self */ // Delete secondary buffer if(dsound->primaryBuffer){ IDirectSoundBuffer_Release(dsound->primaryBuffer); } if(dsound->secondaryBuffer){ IDirectSoundBuffer_Release(dsound->secondaryBuffer); } if(dsound->device){ IDirectSound_Release(dsound->device); } for(i = 0; i<sizeof(dsound->notifEvents)/sizeof(HANDLE); i++){ if(dsound->notifEvents[i]){ CloseHandle(dsound->notifEvents[i]); } } } return self; }
/* destructor */ static tsk_object_t* tdav_consumer_audioqueue_dtor(tsk_object_t * self) { tdav_consumer_audioqueue_t *consumer = self; if(consumer){ // Stop the consumer if not done if(consumer->started){ tdav_consumer_audioqueue_stop(self); } // Free all buffers and dispose the queue if (consumer->queue) { tsk_size_t i; for(i=0; i<CoreAudioPlayBuffers; i++){ AudioQueueFreeBuffer(consumer->queue, consumer->buffers[i]); } AudioQueueDispose(consumer->queue, true); } /* deinit base */ tdav_consumer_audio_deinit(TDAV_CONSUMER_AUDIO(consumer)); } return self; }
/* destructor */ static tsk_object_t* tdav_consumer_audiounit_dtor(tsk_object_t * self) { tdav_consumer_audiounit_t *consumer = self; if(consumer){ /* deinit self */ // Stop the consumer if not done if(consumer->started){ tdav_consumer_audiounit_stop(self); } // destroy handle if(consumer->audioUnitHandle){ tdav_audiounit_handle_destroy(&consumer->audioUnitHandle); } TSK_FREE(consumer->ring.chunck.buffer); if(consumer->ring.buffer){ speex_buffer_destroy(consumer->ring.buffer); } if(consumer->ring.mutex){ tsk_mutex_destroy(&consumer->ring.mutex); } /* deinit base */ tdav_consumer_audio_deinit(TDAV_CONSUMER_AUDIO(consumer)); } return self; }
static void* TSK_STDCALL _tdav_consumer_oss_playback_thread(void *param) { tdav_consumer_oss_t* p_oss = (tdav_consumer_oss_t*)param; int err; void* p_buffer = ((p_oss->n_bits_per_sample == 8) ? (void*)p_oss->p_buff16_ptr: (void*)p_oss->p_buff_ptr); tsk_size_t n_buffer_in_bytes = (p_oss->n_bits_per_sample == 8) ? p_oss->n_buff16_size_in_bytes : p_oss->n_buff_size_in_bytes; tsk_size_t n_buffer_in_samples = p_oss->n_buff_size_in_samples; const void* _p_buffer; tsk_size_t _n_buffer_in_bytes; OSS_DEBUG_INFO("__playback_thread -- START"); tsk_thread_set_priority_2(TSK_THREAD_PRIORITY_TIME_CRITICAL); while (p_oss->b_started) { tsk_safeobj_lock(p_oss); err = tdav_consumer_audio_get(TDAV_CONSUMER_AUDIO(p_oss), p_buffer, n_buffer_in_bytes); // requires 16bits, thread-safe if (err >= 0) { _p_buffer = p_buffer; _n_buffer_in_bytes = n_buffer_in_bytes; if (err < n_buffer_in_bytes) { memset(((uint8_t*)p_buffer) + err, 0, (n_buffer_in_bytes - err)); } if (p_oss->n_bits_per_sample == 8) { __oss_from_16bits_to_8bits(p_buffer, p_oss->p_buff_ptr, n_buffer_in_samples); _p_buffer = p_oss->p_buff_ptr; _n_buffer_in_bytes >>= 1; } if ((err = write(p_oss->fd, _p_buffer, _n_buffer_in_bytes)) != _n_buffer_in_bytes) { OSS_DEBUG_ERROR ("Failed to read data from audio interface failed (%d -> %s)", err , strerror(errno)); tsk_safeobj_unlock(p_oss); goto bail; } }
static void __handle_output_buffer(void *userdata, AudioQueueRef queue, AudioQueueBufferRef buffer) { tdav_consumer_audioqueue_t* consumer = (tdav_consumer_audioqueue_t*)userdata; if (!consumer->started) { return; } if(!tdav_consumer_audio_get(TDAV_CONSUMER_AUDIO(consumer), buffer->mAudioData, consumer->buffer_size)){ // Put silence memset(buffer->mAudioData, 0, consumer->buffer_size); } // Re-enqueue the buffer AudioQueueEnqueueBuffer(consumer->queue, buffer, 0, NULL); // alert the jitter buffer tdav_consumer_audio_tick(TDAV_CONSUMER_AUDIO(consumer)); }
/* constructor */ static tsk_object_t* tdav_consumer_audioqueue_ctor(tsk_object_t * self, va_list * app) { tdav_consumer_audioqueue_t *consumer = self; if(consumer){ /* init base */ tdav_consumer_audio_init(TDAV_CONSUMER_AUDIO(consumer)); } return self; }
int tdav_consumer_dsound_consume(tmedia_consumer_t* self, void** buffer, tsk_size_t size, const tsk_object_t* proto_hdr) { tdav_consumer_dsound_t* dsound = (tdav_consumer_dsound_t*)self; if(!dsound || !buffer || !*buffer || !size){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } /* buffer is already decoded */ return tdav_consumer_audio_put(TDAV_CONSUMER_AUDIO(dsound), buffer, size, proto_hdr); }
int tdav_consumer_audioqueue_consume(tmedia_consumer_t* self, const void* buffer, tsk_size_t size, const tsk_object_t* proto_hdr) { tdav_consumer_audioqueue_t* consumer = (tdav_consumer_audioqueue_t*)self; if(!consumer || !buffer || !size){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } // buffer is already decoded return tdav_consumer_audio_put(TDAV_CONSUMER_AUDIO(consumer), buffer, size, proto_hdr); }
/* ============ Media Consumer Interface ================= */ int tdav_consumer_audiounit_set(tmedia_consumer_t* self, const tmedia_param_t* param) { tdav_consumer_audiounit_t* consumer = (tdav_consumer_audiounit_t*)self; if (param->plugin_type == tmedia_ppt_consumer) { if (param->value_type == tmedia_pvt_int32) { if (tsk_striequals(param->key, "interrupt")) { int32_t interrupt = *((uint8_t*)param->value) ? 1 : 0; return tdav_audiounit_handle_interrupt(consumer->audioUnitHandle, interrupt); } else if (tsk_striequals(param->key, "pause") || tsk_striequals(param->key, "hold")) { int32_t pause = *((uint8_t*)param->value) ? 1 : 0; return pause ? tdav_consumer_audiounit_pause(self) : tdav_consumer_audiounit_resume(self); } } } return tdav_consumer_audio_set(TDAV_CONSUMER_AUDIO(self), param); }
/* destructor */ static tsk_object_t* tdav_consumer_audiounit_dtor(tsk_object_t * self) { tdav_consumer_audiounit_t *consumer = self; if(consumer) { /* deinit self */ tdav_consumer_audiounit_deinit(TMEDIA_CONSUMER(self)); TSK_FREE(consumer->ring.chunck.buffer); if(consumer->ring.buffer) { speex_buffer_destroy(consumer->ring.buffer); } if(consumer->ring.mutex) { tsk_mutex_destroy(&consumer->ring.mutex); } /* deinit base */ tdav_consumer_audio_deinit(TDAV_CONSUMER_AUDIO(consumer)); TSK_DEBUG_INFO("*** AudioUnit Consumer destroyed ***"); } return self; }
static void __handle_output_buffer(void *userdata, AudioQueueRef queue, AudioQueueBufferRef buffer) { OSStatus ret; void *data; tsk_size_t out_size = 0; tdav_consumer_coreaudio_t* consumer = (tdav_consumer_coreaudio_t*)userdata; if (!consumer->started) { return; } if((data = tdav_consumer_audio_get(TDAV_CONSUMER_AUDIO(consumer), &out_size))){ // If we can get audio to play, then copy in the buffer memcpy(buffer->mAudioData, data, TSK_MIN(consumer->buffer_size, out_size)); TSK_FREE(data); } else{ // Put silence if there is no audio to play memset(buffer->mAudioData, 0, consumer->buffer_size); } // Re-enqueue the buffer ret = AudioQueueEnqueueBuffer(consumer->queue, buffer, 0, NULL); }
static int tdav_consumer_audiounit_consume(tmedia_consumer_t* self, const void* buffer, tsk_size_t size, const tsk_object_t* proto_hdr) { tdav_consumer_audiounit_t* consumer = (tdav_consumer_audiounit_t*)self; if(!consumer || !buffer || !size){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } #if DISABLE_JITTER_BUFFER { if(consumer->ring.buffer){ tsk_mutex_lock(consumer->ring.mutex); speex_buffer_write(consumer->ring.buffer, (void*)buffer, size); tsk_mutex_unlock(consumer->ring.mutex); return 0; } return -2; } #else { return tdav_consumer_audio_put(TDAV_CONSUMER_AUDIO(consumer), buffer, size, proto_hdr); } #endif }
/* ============ Media Consumer Interface ================= */ int tdav_consumer_audiounit_set(tmedia_consumer_t* self, const tmedia_param_t* param) { return tdav_consumer_audio_set(TDAV_CONSUMER_AUDIO(self), param); }
/** * Generic function to compare two consumers. * @param consumer1 The first consumer to compare. * @param consumer2 The second consumer to compare. * @retval Returns an integral value indicating the relationship between the two consumers: * <0 : @a consumer1 less than @a consumer2.<br> * 0 : @a consumer1 identical to @a consumer2.<br> * >0 : @a consumer1 greater than @a consumer2.<br> */ int tdav_consumer_audio_cmp(const tsk_object_t* consumer1, const tsk_object_t* consumer2) { return (TDAV_CONSUMER_AUDIO(consumer1) - TDAV_CONSUMER_AUDIO(consumer2)); }
int tdav_consumer_coreaudio_prepare(tmedia_consumer_t* self, const tmedia_codec_t* codec) { OSStatus ret; tsk_size_t i; tdav_consumer_coreaudio_t* consumer = (tdav_consumer_coreaudio_t*)self; if(!consumer || !codec && codec->plugin){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } TDAV_CONSUMER_AUDIO(consumer)->channels = codec->plugin->audio.channels; TDAV_CONSUMER_AUDIO(consumer)->rate = codec->plugin->rate; /* codec should have ptime */ // Set audio category UInt32 category = kAudioSessionCategory_PlayAndRecord; AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(category), &category); // Create the audio stream description AudioStreamBasicDescription *description = &(consumer->description); description->mSampleRate = TDAV_CONSUMER_AUDIO(consumer)->rate; description->mFormatID = kAudioFormatLinearPCM; description->mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; description->mChannelsPerFrame = TDAV_CONSUMER_AUDIO(consumer)->channels; description->mFramesPerPacket = 1; description->mBitsPerChannel = TDAV_CONSUMER_AUDIO(consumer)->bits_per_sample; description->mBytesPerPacket = description->mBitsPerChannel / 8 * description->mChannelsPerFrame; description->mBytesPerFrame = description->mBytesPerPacket; description->mReserved = 0; int packetperbuffer = 1000 / TDAV_CONSUMER_AUDIO(consumer)->ptime; consumer->buffer_size = description->mSampleRate * description->mBytesPerFrame / packetperbuffer; // Create the playback audio queue ret = AudioQueueNewOutput(&(consumer->description), __handle_output_buffer, consumer, NULL, NULL, 0, &(consumer->queue)); for(i = 0; i < CoreAudioPlayBuffers; i++) { // Create the buffer for the queue ret = AudioQueueAllocateBuffer(consumer->queue, consumer->buffer_size, &(consumer->buffers[i])); if (ret) { break; } // Clear the data memset(consumer->buffers[i]->mAudioData, 0, consumer->buffer_size); consumer->buffers[i]->mAudioDataByteSize = consumer->buffer_size; // Enqueue the buffer ret = AudioQueueEnqueueBuffer(consumer->queue, consumer->buffers[i], 0, NULL); if (ret) { break; } } return ret; }
/* ============ Media Consumer Interface ================= */ int tdav_consumer_dsound_prepare(tmedia_consumer_t* self, const tmedia_codec_t* codec) { HRESULT hr; HWND hWnd; WAVEFORMATEX wfx = {0}; DSBUFFERDESC dsbd = {0}; tdav_consumer_dsound_t* dsound = (tdav_consumer_dsound_t*)self; if(!dsound){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if(dsound->device || dsound->primaryBuffer || dsound->secondaryBuffer){ TSK_DEBUG_ERROR("Consumer already prepared"); return -2; } TDAV_CONSUMER_AUDIO(dsound)->channels = codec->plugin->audio.channels; TDAV_CONSUMER_AUDIO(dsound)->rate = codec->plugin->rate; /* Create sound device */ if((hr = DirectSoundCreate(NULL, &dsound->device, NULL) != DS_OK)){ tdav_win32_print_error("DirectSoundCreate", hr); return -3; } /* Set CooperativeLevel */ if((hWnd = GetConsoleWindow()) || (hWnd = GetDesktopWindow()) || (hWnd = GetForegroundWindow())){ if((hr = IDirectSound_SetCooperativeLevel(dsound->device, hWnd, DSSCL_PRIORITY)) != DS_OK){ tdav_win32_print_error("IDirectSound_SetCooperativeLevel", hr); } } /* Creates the primary buffer and apply format */ wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nChannels = TDAV_CONSUMER_AUDIO(dsound)->channels; wfx.nSamplesPerSec = TDAV_CONSUMER_AUDIO(dsound)->rate; wfx.wBitsPerSample = TDAV_CONSUMER_AUDIO(dsound)->bits_per_sample; wfx.nBlockAlign = (wfx.nChannels * wfx.wBitsPerSample/8); wfx.nAvgBytesPerSec = (wfx.nSamplesPerSec * wfx.nBlockAlign); /* Average bytes (count) for each notification */ dsound->bytes_per_notif = ((wfx.nAvgBytesPerSec * TDAV_CONSUMER_AUDIO(dsound)->ptime)/1000); dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; dsbd.dwBufferBytes = 0; dsbd.lpwfxFormat = NULL; if((hr = IDirectSound_CreateSoundBuffer(dsound->device, &dsbd, &dsound->primaryBuffer, NULL)) != DS_OK){ tdav_win32_print_error("IDirectSound_CreateSoundBuffer", hr); return -4; } if((hr = IDirectSoundBuffer_SetFormat(dsound->primaryBuffer, &wfx)) != DS_OK){ tdav_win32_print_error("IDirectSoundBuffer_SetFormat", hr); return -5; } /* Creates the secondary buffer and apply format */ dsbd.dwFlags = (DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS); dsbd.dwBufferBytes = (TDAV_DSOUNS_CONSUMER_NOTIF_POS_COUNT * dsound->bytes_per_notif); dsbd.lpwfxFormat = &wfx; if((hr = IDirectSound_CreateSoundBuffer(dsound->device, &dsbd, &dsound->secondaryBuffer, NULL)) != DS_OK){ tdav_win32_print_error("IDirectSound_CreateSoundBuffer", hr); return -6; } return 0; }
static void *__playback_thread(void *param) { tdav_consumer_dsound_t* dsound = (tdav_consumer_dsound_t*)param; HRESULT hr; LPVOID lpvAudio1, lpvAudio2; DWORD dwBytesAudio1, dwBytesAudio2; void* data; int index; TSK_DEBUG_INFO("__playback_thread -- START"); SetPriorityClass(GetCurrentThread(), REALTIME_PRIORITY_CLASS); for(;;){ DWORD dwEvent = WaitForMultipleObjects(sizeof(dsound->notifEvents)/sizeof(HANDLE), dsound->notifEvents, FALSE, INFINITE); if(!dsound->started){ break; } else { tsk_size_t out_size = 0; data = tdav_consumer_audio_get(TDAV_CONSUMER_AUDIO(dsound), &out_size); index = (dwEvent == (TDAV_DSOUNS_CONSUMER_NOTIF_POS_COUNT-1)) ? 0 : (dwEvent + 1); // lock if((hr = IDirectSoundBuffer_Lock(dsound->secondaryBuffer, (index * dsound->bytes_per_notif), dsound->bytes_per_notif, &lpvAudio1, &dwBytesAudio1, &lpvAudio2, &dwBytesAudio2, 0)) != DS_OK){ tdav_win32_print_error("IDirectSoundBuffer_Lock", hr); goto next; } if(data){ // copy data to dsound buffers memcpy(lpvAudio1, data, TSK_MIN(dwBytesAudio1, out_size)); if(lpvAudio2){ memcpy(lpvAudio2, ((LPBYTE*)data) + dwBytesAudio1, dwBytesAudio2); } } else{ // Put silence memset(lpvAudio1, 0, dwBytesAudio1); if(lpvAudio2){ memset(lpvAudio2, 0, dwBytesAudio2); } } // unlock if((hr = IDirectSoundBuffer_Unlock(dsound->secondaryBuffer, lpvAudio1, dwBytesAudio1, lpvAudio2, dwBytesAudio2)) != DS_OK){ tdav_win32_print_error("IDirectSoundBuffer_UnLock", hr); goto next; } next: TSK_FREE(data); } } TSK_DEBUG_INFO("__playback_thread -- STOP"); return tsk_null; }