summaryrefslogtreecommitdiffstats
path: root/plugins/audio_webrtc/audio_webrtc.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/audio_webrtc/audio_webrtc.cxx')
-rw-r--r--plugins/audio_webrtc/audio_webrtc.cxx562
1 files changed, 562 insertions, 0 deletions
diff --git a/plugins/audio_webrtc/audio_webrtc.cxx b/plugins/audio_webrtc/audio_webrtc.cxx
new file mode 100644
index 0000000..167166d
--- /dev/null
+++ b/plugins/audio_webrtc/audio_webrtc.cxx
@@ -0,0 +1,562 @@
+/* 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;
+}
OpenPOWER on IntegriCloud