123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- /* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org>
- *
- * This file is part of Open Source Doubango Framework.
- *
- * DOUBANGO is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * DOUBANGO is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with DOUBANGO.
- */
- #include "audio_webrtc.h"
- #include "audio_webrtc_consumer.h"
- #include "audio_webrtc_producer.h"
- #include "audio_webrtc_transport.h"
- #include <webrtc/audio_device_config.h>
- #include <webrtc/audio_device_impl.h>
- #include "tinymedia/tmedia_consumer.h"
- #include "tinymedia/tmedia_producer.h"
- #include "tsk_list.h"
- #include "tsk_safeobj.h"
- #include "tsk_debug.h"
- using namespace webrtc;
- #define kAudioDeviceModuleId 444
- #if DOUBANGO_AUDIO_WEBRTC_UNDER_ANDROID
- // https://groups.google.com/group/android-ndk/browse_thread/thread/a1667f28162cf69b/8ef3a171df7f8dfe
- extern "C"
- {
- void *__dso_handle = NULL;
- }
- #endif
- typedef enum PLUGIN_INDEX_E {
- PLUGIN_INDEX_AUDIO_CONSUMER,
- PLUGIN_INDEX_AUDIO_PRODUCER,
- PLUGIN_INDEX_COUNT
- }
- PLUGIN_INDEX_T;
- int __plugin_get_def_count()
- {
- return PLUGIN_INDEX_COUNT;
- }
- tsk_plugin_def_type_t __plugin_get_def_type_at(int index)
- {
- switch(index) {
- case PLUGIN_INDEX_AUDIO_CONSUMER:
- return tsk_plugin_def_type_consumer;
- case PLUGIN_INDEX_AUDIO_PRODUCER:
- return tsk_plugin_def_type_producer;
- default: {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("No plugin at index %d", index);
- return tsk_plugin_def_type_none;
- }
- }
- }
- tsk_plugin_def_media_type_t __plugin_get_def_media_type_at(int index)
- {
- switch(index) {
- case PLUGIN_INDEX_AUDIO_CONSUMER:
- case PLUGIN_INDEX_AUDIO_PRODUCER: {
- return tsk_plugin_def_media_type_audio;
- }
- default: {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("No plugin at index %d", index);
- return tsk_plugin_def_media_type_none;
- }
- }
- }
- tsk_plugin_def_ptr_const_t __plugin_get_def_at(int index)
- {
- switch(index) {
- case PLUGIN_INDEX_AUDIO_CONSUMER: {
- return audio_consumer_webrtc_plugin_def_t;
- }
- case PLUGIN_INDEX_AUDIO_PRODUCER: {
- return audio_producer_webrtc_plugin_def_t;
- }
- default: {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("No plugin at index %d", index);
- return tsk_null;
- }
- }
- }
- //
- // WebRTC AudioInstance
- //
- typedef struct audio_webrtc_instance_s {
- TSK_DECLARE_OBJECT;
- uint64_t sessionId;
- bool isStarted;
- bool isConsumerPrepared;
- bool isConsumerStarted;
- bool isProducerPrepared;
- bool isProducerStarted;
- bool isSpeakerAvailable;
- bool isPlayoutAvailable;
- bool isRecordingAvailable;
- AudioDeviceModule* device;
- AudioTransportImpl* transport;
- TSK_DECLARE_SAFEOBJ;
- }
- audio_webrtc_instance_t;
- typedef tsk_list_t audio_webrtc_instances_L_t;
- static audio_webrtc_instances_L_t* __audioInstances = tsk_null;
- static tsk_object_t* audio_webrtc_instance_ctor(tsk_object_t * self, va_list * app)
- {
- audio_webrtc_instance_t* audioInstance = (audio_webrtc_instance_t*)self;
- if(audioInstance) {
- tsk_safeobj_init(audioInstance);
- }
- return self;
- }
- static tsk_object_t* audio_webrtc_instance_dtor(tsk_object_t * self)
- {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_INFO("Audio Instance destroyed");
- audio_webrtc_instance_t* audioInstance = (audio_webrtc_instance_t*)self;
- if(audioInstance) {
- tsk_safeobj_lock(audioInstance);
- if(audioInstance->device) {
- audioInstance->device->RegisterAudioCallback(tsk_null);
- audioInstance->device->Terminate();
- audioInstance->device->Release();//FIXME: must be deleted?
- audioInstance->device = tsk_null;
- }
- if(audioInstance->transport) {
- delete audioInstance->transport;
- audioInstance->transport = tsk_null;
- }
- tsk_safeobj_unlock(audioInstance);
- tsk_safeobj_deinit(audioInstance);
- }
- return self;
- }
- static int audio_webrtc_instance_cmp(const tsk_object_t *_ai1, const tsk_object_t *_ai2)
- {
- return ((int)_ai1 - (int)_ai2);
- }
- static const tsk_object_def_t audio_webrtc_instance_def_s = {
- sizeof(audio_webrtc_instance_t),
- audio_webrtc_instance_ctor,
- audio_webrtc_instance_dtor,
- audio_webrtc_instance_cmp,
- };
- const tsk_object_def_t *audio_webrtc_instance_def_t = &audio_webrtc_instance_def_s;
- audio_webrtc_instance_handle_t* audio_webrtc_instance_create(uint64_t sessionId)
- {
- audio_webrtc_instance_t* audioInstance = tsk_null;
- // create list used to hold instances
- if(!__audioInstances && !(__audioInstances = tsk_list_create())) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("Failed to create new list");
- return tsk_null;
- }
- //= lock the list
- tsk_list_lock(__audioInstances);
- // find the instance from the list
- const tsk_list_item_t* item;
- tsk_list_foreach(item, __audioInstances) {
- if(((audio_webrtc_instance_t*)item->data)->sessionId == sessionId) {
- audioInstance = (audio_webrtc_instance_t*)tsk_object_ref(item->data);
- break;
- }
- }
- if(!audioInstance) {
- audio_webrtc_instance_t* _audioInstance;
- if(!(_audioInstance = (audio_webrtc_instance_t*)tsk_object_new(&audio_webrtc_instance_def_s))) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("Failed to create new audio instance");
- goto done;
- }
- if(!(_audioInstance->device = AudioDeviceModuleImpl::Create(kAudioDeviceModuleId))) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("Failed to create audio device");
- TSK_OBJECT_SAFE_FREE(_audioInstance);
- goto done;
- }
- _audioInstance->device->AddRef();
- if(!(_audioInstance->transport = new AudioTransportImpl(_audioInstance->device))) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("Failed to create audio transport");
- TSK_OBJECT_SAFE_FREE(_audioInstance);
- goto done;
- }
- if((_audioInstance->device->RegisterAudioCallback(_audioInstance->transport))) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("AudioDeviceModule::RegisterAudioCallback() failed");
- TSK_OBJECT_SAFE_FREE(_audioInstance);
- goto done;
- }
- if((_audioInstance->device->Init())) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("AudioDeviceModule::Init() failed");
- TSK_OBJECT_SAFE_FREE(_audioInstance);
- goto done;
- }
- _audioInstance->sessionId = sessionId;
- audioInstance = _audioInstance;
- tsk_list_push_back_data(__audioInstances, (void**)&_audioInstance);
- }
- done:
- //= unlock the list
- tsk_list_unlock(__audioInstances);
- return audioInstance;
- }
- int audio_webrtc_instance_prepare_consumer(audio_webrtc_instance_handle_t* _self, tmedia_consumer_t** _consumer)
- {
- audio_webrtc_instance_t* self = (audio_webrtc_instance_t*)_self;
- if(!self || !self->device || !self->transport || !_consumer || !*_consumer) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("invalid parameter");
- return -1;
- }
- if(self->isConsumerPrepared) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_WARN("Consumer already prepared");
- return 0;
- }
- int ret;
- bool _bool;
- tsk_safeobj_lock(self);
- self->transport->SetConsumer((const struct audio_consumer_webrtc_s*)*_consumer);
- if((ret = self->device->SetPlayoutDevice(DOUBANGO_AUDIO_WEBRTC_DEVICE_DEFAULT))) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("AudioDeviceModule->SetPlayoutDevice(%d) failed", DOUBANGO_AUDIO_WEBRTC_DEVICE_DEFAULT);
- }
- if((ret = self->device->SpeakerIsAvailable(&_bool))) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("SpeakerIsAvailable() failed with error code=%d", ret);
- }
- else {
- if(!_bool) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("SpeakerIsAvailable() returned false");
- }
- self->isSpeakerAvailable = _bool;
- }
- if((ret = self->device->InitSpeaker())) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("InitSpeaker() failed with error code=%d", ret);
- }
- if((ret = self->device->PlayoutIsAvailable(&_bool))) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("PlayoutIsAvailable() failed with error code =%d", ret);
- }
- else {
- if(!_bool) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("PlayoutIsAvailable() returned false");
- }
- self->isPlayoutAvailable = _bool;
- }
- if((ret = self->device->SetStereoPlayout(((*_consumer)->audio.in.channels == 2)))) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("SetStereoPlayout(%d==2) failed with error code=%d", (*_consumer)->audio.in.channels, ret);
- }
- //if((ret = self->device->SetPlayoutBuffer(AudioDeviceModule::kFixedBufferSize, (*_consumer)->audio.ptime))){
- // DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("SetPlayoutBuffer(%d ms) failed with error code=%d", (*_consumer)->audio.ptime, ret);
- //}
- // always request 10ms buffers. In all cases WebRTC don't support anything else
- if((ret = self->device->SetPlayoutBuffer(AudioDeviceModule::kFixedBufferSize, 10))) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("SetPlayoutBuffer(%d ms) failed with error code=%d", 10, ret);
- }
- uint32_t playoutSampleRate = (*_consumer)->audio.out.rate ? (*_consumer)->audio.out.rate : (*_consumer)->audio.in.rate;
- if((ret = self->device->SetPlayoutSampleRate(playoutSampleRate))) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("SetPlayoutSampleRate(%d) failed with error code=%d", playoutSampleRate, ret);
- }
- if((ret = self->device->InitPlayout())) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("AudioDeviceModule::InitPlayout() failed with error code = %d", ret);
- goto done;
- }
- // init output parameters
- if((ret = self->device->StereoPlayout(&_bool))) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("StereoPlayout() failed with error code=%d", ret);
- }
- else {
- (*_consumer)->audio.out.channels = (_bool ? 2 : 1);
- }
- if((ret = self->device->PlayoutSampleRate(&playoutSampleRate))) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("PlayoutSampleRate() failed with error code=%d", ret);
- }
- else {
- (*_consumer)->audio.out.rate = playoutSampleRate;
- }
- done:
- tsk_safeobj_unlock(self);
- self->isConsumerPrepared = (ret == 0);
- return ret;
- }
- int audio_webrtc_instance_prepare_producer(audio_webrtc_instance_handle_t* _self, tmedia_producer_t** _producer)
- {
- audio_webrtc_instance_t* self = (audio_webrtc_instance_t*)_self;
- if(!self || !self->device || !self->transport || !_producer || !*_producer) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("invalid parameter");
- return -1;
- }
- if(self->isProducerPrepared) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_WARN("Producer already prepared");
- return 0;
- }
- int ret;
- bool _bool;
- tsk_safeobj_lock(self);
- self->transport->SetProducer((const struct audio_producer_webrtc_s*)*_producer);
- if((ret = self->device->SetRecordingDevice(DOUBANGO_AUDIO_WEBRTC_DEVICE_DEFAULT))) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("AudioDeviceModule->SetRecordingDevice(%d) failed", DOUBANGO_AUDIO_WEBRTC_DEVICE_DEFAULT);
- }
- if((ret = self->device->RecordingIsAvailable(&_bool))) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("RecordingIsAvailable() failed with error code =%d", ret);
- }
- else {
- if(!_bool) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("RecordingIsAvailable() returned false");
- }
- self->isRecordingAvailable = _bool;
- }
- if((ret = self->device->MicrophoneIsAvailable(&_bool))) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("MicrophoneIsAvailable() failed with error code =%d", ret);
- }
- else {
- if(!_bool) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("MicrophoneIsAvailable() returned false");
- }
- else {
- if((ret = self->device->InitMicrophone())) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("InitMicrophone() failed with error code =%d", ret);
- }
- }
- }
- if((ret = self->device->SetStereoRecording(((*_producer)->audio.channels == 2)))) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("SetStereoRecording(%d==2) failed with error code=%d", (*_producer)->audio.channels, ret);
- }
- uint32_t recordingSampleRate = (*_producer)->audio.rate;
- if((ret = self->device->SetRecordingSampleRate(recordingSampleRate))) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("SetRecordingSampleRate(%d) failed with error code=%d", recordingSampleRate, ret);
- }
- if((ret = self->device->InitRecording())) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("AudioDeviceModule::InitRecording() failed with error code = %d", ret);
- goto done;
- }
- // init output parameters
- if((ret = self->device->StereoRecording(&_bool))) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("StereoRecording() failed with error code=%d", ret);
- }
- else {
- (*_producer)->audio.channels = (_bool ? 2 : 1);
- }
- if((ret = self->device->RecordingSampleRate(&recordingSampleRate))) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("RecordingSampleRate() failed with error code=%d", ret);
- }
- else {
- (*_producer)->audio.rate = recordingSampleRate;
- }
- done:
- tsk_safeobj_unlock(self);
- self->isProducerPrepared = (ret == 0);
- return ret;
- }
- int audio_webrtc_instance_start_consumer(audio_webrtc_instance_handle_t* _self)
- {
- audio_webrtc_instance_t* self = (audio_webrtc_instance_t*)_self;
- if(!self || !self->device || !self->transport) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("invalid parameter");
- return -1;
- }
- tsk_safeobj_lock(self);
- if(!self->isConsumerPrepared) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("Consumer not prepared");
- goto done;
- }
- if(self->isConsumerStarted) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_WARN("Consumer already started");
- goto done;
- }
- if(self->isPlayoutAvailable) {
- int ret;
- if((ret = self->device->StartPlayout())) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("StartPlayout() failed with error code = %d", ret);
- }
- self->isConsumerStarted = self->device->Playing();
- DOUBANGO_AUDIO_WEBRTC_DEBUG_INFO("isPlaying=%s", (self->isConsumerPrepared ? "true" : "false"));
- }
- done:
- tsk_safeobj_unlock(self);
- return (self->isConsumerStarted ? 0 : -1);
- }
- int audio_webrtc_instance_start_producer(audio_webrtc_instance_handle_t* _self)
- {
- audio_webrtc_instance_t* self = (audio_webrtc_instance_t*)_self;
- if(!self || !self->device || !self->transport) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("invalid parameter");
- return -1;
- }
- tsk_safeobj_lock(self);
- if(!self->isProducerPrepared) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("Producer not prepared");
- goto done;
- }
- if(self->isProducerStarted) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_WARN("Consumer already started");
- goto done;
- }
- if(self->isRecordingAvailable) {
- int ret;
- if((ret = self->device->StartRecording())) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("StartRecording() failed with error code = %d", ret);
- }
- self->isProducerStarted = self->device->Recording();
- DOUBANGO_AUDIO_WEBRTC_DEBUG_INFO("isRecording=%s", (self->isProducerStarted ? "true" : "false"));
- }
- done:
- tsk_safeobj_unlock(self);
- return (self->isProducerStarted ? 0 : -1);
- return 0;
- }
- int audio_webrtc_instance_stop_consumer(audio_webrtc_instance_handle_t* _self)
- {
- audio_webrtc_instance_t* self = (audio_webrtc_instance_t*)_self;
- if(!self || !self->device || !self->transport) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("invalid parameter");
- return -1;
- }
- tsk_safeobj_lock(self);
- if(!self->isConsumerStarted) {
- goto done;
- }
- int ret;
- if((ret = self->device->StopPlayout())) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("StopPlayout() failed with error code = %d", ret);
- }
- else {
- self->isConsumerStarted = self->device->Playing();
- }
- done:
- tsk_safeobj_unlock(self);
- return (self->isConsumerStarted ? -1 : 0);
- }
- int audio_webrtc_instance_stop_producer(audio_webrtc_instance_handle_t* _self)
- {
- audio_webrtc_instance_t* self = (audio_webrtc_instance_t*)_self;
- if(!self || !self->device || !self->transport) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("invalid parameter");
- return -1;
- }
- tsk_safeobj_lock(self);
- if(!self->isProducerStarted) {
- goto done;
- }
- int ret;
- if((ret = self->device->StopRecording())) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("StopRecording() failed with error code = %d", ret);
- }
- else {
- self->isProducerStarted = self->device->Recording();
- }
- done:
- tsk_safeobj_unlock(self);
- return (self->isProducerStarted ? -1 : 0);
- }
- int audio_webrtc_instance_destroy(audio_webrtc_instance_handle_t** _self)
- {
- if(!_self || !*_self) {
- DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("Invalid parameter");
- return -1;
- }
- tsk_list_lock(__audioInstances);
- if(tsk_object_get_refcount(*_self)==1) {
- tsk_list_remove_item_by_data(__audioInstances, *_self);
- }
- else {
- tsk_object_unref(*_self);
- }
- tsk_list_unlock(__audioInstances);
- *_self = tsk_null;
- return 0;
- }
|