audio_webrtc.cxx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. /* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org>
  2. *
  3. * This file is part of Open Source Doubango Framework.
  4. *
  5. * DOUBANGO is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * DOUBANGO is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with DOUBANGO.
  17. */
  18. #include "audio_webrtc.h"
  19. #include "audio_webrtc_consumer.h"
  20. #include "audio_webrtc_producer.h"
  21. #include "audio_webrtc_transport.h"
  22. #include <webrtc/audio_device_config.h>
  23. #include <webrtc/audio_device_impl.h>
  24. #include "tinymedia/tmedia_consumer.h"
  25. #include "tinymedia/tmedia_producer.h"
  26. #include "tsk_list.h"
  27. #include "tsk_safeobj.h"
  28. #include "tsk_debug.h"
  29. using namespace webrtc;
  30. #define kAudioDeviceModuleId 444
  31. #if DOUBANGO_AUDIO_WEBRTC_UNDER_ANDROID
  32. // https://groups.google.com/group/android-ndk/browse_thread/thread/a1667f28162cf69b/8ef3a171df7f8dfe
  33. extern "C"
  34. {
  35. void *__dso_handle = NULL;
  36. }
  37. #endif
  38. typedef enum PLUGIN_INDEX_E {
  39. PLUGIN_INDEX_AUDIO_CONSUMER,
  40. PLUGIN_INDEX_AUDIO_PRODUCER,
  41. PLUGIN_INDEX_COUNT
  42. }
  43. PLUGIN_INDEX_T;
  44. int __plugin_get_def_count()
  45. {
  46. return PLUGIN_INDEX_COUNT;
  47. }
  48. tsk_plugin_def_type_t __plugin_get_def_type_at(int index)
  49. {
  50. switch(index) {
  51. case PLUGIN_INDEX_AUDIO_CONSUMER:
  52. return tsk_plugin_def_type_consumer;
  53. case PLUGIN_INDEX_AUDIO_PRODUCER:
  54. return tsk_plugin_def_type_producer;
  55. default: {
  56. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("No plugin at index %d", index);
  57. return tsk_plugin_def_type_none;
  58. }
  59. }
  60. }
  61. tsk_plugin_def_media_type_t __plugin_get_def_media_type_at(int index)
  62. {
  63. switch(index) {
  64. case PLUGIN_INDEX_AUDIO_CONSUMER:
  65. case PLUGIN_INDEX_AUDIO_PRODUCER: {
  66. return tsk_plugin_def_media_type_audio;
  67. }
  68. default: {
  69. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("No plugin at index %d", index);
  70. return tsk_plugin_def_media_type_none;
  71. }
  72. }
  73. }
  74. tsk_plugin_def_ptr_const_t __plugin_get_def_at(int index)
  75. {
  76. switch(index) {
  77. case PLUGIN_INDEX_AUDIO_CONSUMER: {
  78. return audio_consumer_webrtc_plugin_def_t;
  79. }
  80. case PLUGIN_INDEX_AUDIO_PRODUCER: {
  81. return audio_producer_webrtc_plugin_def_t;
  82. }
  83. default: {
  84. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("No plugin at index %d", index);
  85. return tsk_null;
  86. }
  87. }
  88. }
  89. //
  90. // WebRTC AudioInstance
  91. //
  92. typedef struct audio_webrtc_instance_s {
  93. TSK_DECLARE_OBJECT;
  94. uint64_t sessionId;
  95. bool isStarted;
  96. bool isConsumerPrepared;
  97. bool isConsumerStarted;
  98. bool isProducerPrepared;
  99. bool isProducerStarted;
  100. bool isSpeakerAvailable;
  101. bool isPlayoutAvailable;
  102. bool isRecordingAvailable;
  103. AudioDeviceModule* device;
  104. AudioTransportImpl* transport;
  105. TSK_DECLARE_SAFEOBJ;
  106. }
  107. audio_webrtc_instance_t;
  108. typedef tsk_list_t audio_webrtc_instances_L_t;
  109. static audio_webrtc_instances_L_t* __audioInstances = tsk_null;
  110. static tsk_object_t* audio_webrtc_instance_ctor(tsk_object_t * self, va_list * app)
  111. {
  112. audio_webrtc_instance_t* audioInstance = (audio_webrtc_instance_t*)self;
  113. if(audioInstance) {
  114. tsk_safeobj_init(audioInstance);
  115. }
  116. return self;
  117. }
  118. static tsk_object_t* audio_webrtc_instance_dtor(tsk_object_t * self)
  119. {
  120. DOUBANGO_AUDIO_WEBRTC_DEBUG_INFO("Audio Instance destroyed");
  121. audio_webrtc_instance_t* audioInstance = (audio_webrtc_instance_t*)self;
  122. if(audioInstance) {
  123. tsk_safeobj_lock(audioInstance);
  124. if(audioInstance->device) {
  125. audioInstance->device->RegisterAudioCallback(tsk_null);
  126. audioInstance->device->Terminate();
  127. audioInstance->device->Release();//FIXME: must be deleted?
  128. audioInstance->device = tsk_null;
  129. }
  130. if(audioInstance->transport) {
  131. delete audioInstance->transport;
  132. audioInstance->transport = tsk_null;
  133. }
  134. tsk_safeobj_unlock(audioInstance);
  135. tsk_safeobj_deinit(audioInstance);
  136. }
  137. return self;
  138. }
  139. static int audio_webrtc_instance_cmp(const tsk_object_t *_ai1, const tsk_object_t *_ai2)
  140. {
  141. return ((int)_ai1 - (int)_ai2);
  142. }
  143. static const tsk_object_def_t audio_webrtc_instance_def_s = {
  144. sizeof(audio_webrtc_instance_t),
  145. audio_webrtc_instance_ctor,
  146. audio_webrtc_instance_dtor,
  147. audio_webrtc_instance_cmp,
  148. };
  149. const tsk_object_def_t *audio_webrtc_instance_def_t = &audio_webrtc_instance_def_s;
  150. audio_webrtc_instance_handle_t* audio_webrtc_instance_create(uint64_t sessionId)
  151. {
  152. audio_webrtc_instance_t* audioInstance = tsk_null;
  153. // create list used to hold instances
  154. if(!__audioInstances && !(__audioInstances = tsk_list_create())) {
  155. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("Failed to create new list");
  156. return tsk_null;
  157. }
  158. //= lock the list
  159. tsk_list_lock(__audioInstances);
  160. // find the instance from the list
  161. const tsk_list_item_t* item;
  162. tsk_list_foreach(item, __audioInstances) {
  163. if(((audio_webrtc_instance_t*)item->data)->sessionId == sessionId) {
  164. audioInstance = (audio_webrtc_instance_t*)tsk_object_ref(item->data);
  165. break;
  166. }
  167. }
  168. if(!audioInstance) {
  169. audio_webrtc_instance_t* _audioInstance;
  170. if(!(_audioInstance = (audio_webrtc_instance_t*)tsk_object_new(&audio_webrtc_instance_def_s))) {
  171. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("Failed to create new audio instance");
  172. goto done;
  173. }
  174. if(!(_audioInstance->device = AudioDeviceModuleImpl::Create(kAudioDeviceModuleId))) {
  175. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("Failed to create audio device");
  176. TSK_OBJECT_SAFE_FREE(_audioInstance);
  177. goto done;
  178. }
  179. _audioInstance->device->AddRef();
  180. if(!(_audioInstance->transport = new AudioTransportImpl(_audioInstance->device))) {
  181. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("Failed to create audio transport");
  182. TSK_OBJECT_SAFE_FREE(_audioInstance);
  183. goto done;
  184. }
  185. if((_audioInstance->device->RegisterAudioCallback(_audioInstance->transport))) {
  186. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("AudioDeviceModule::RegisterAudioCallback() failed");
  187. TSK_OBJECT_SAFE_FREE(_audioInstance);
  188. goto done;
  189. }
  190. if((_audioInstance->device->Init())) {
  191. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("AudioDeviceModule::Init() failed");
  192. TSK_OBJECT_SAFE_FREE(_audioInstance);
  193. goto done;
  194. }
  195. _audioInstance->sessionId = sessionId;
  196. audioInstance = _audioInstance;
  197. tsk_list_push_back_data(__audioInstances, (void**)&_audioInstance);
  198. }
  199. done:
  200. //= unlock the list
  201. tsk_list_unlock(__audioInstances);
  202. return audioInstance;
  203. }
  204. int audio_webrtc_instance_prepare_consumer(audio_webrtc_instance_handle_t* _self, tmedia_consumer_t** _consumer)
  205. {
  206. audio_webrtc_instance_t* self = (audio_webrtc_instance_t*)_self;
  207. if(!self || !self->device || !self->transport || !_consumer || !*_consumer) {
  208. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("invalid parameter");
  209. return -1;
  210. }
  211. if(self->isConsumerPrepared) {
  212. DOUBANGO_AUDIO_WEBRTC_DEBUG_WARN("Consumer already prepared");
  213. return 0;
  214. }
  215. int ret;
  216. bool _bool;
  217. tsk_safeobj_lock(self);
  218. self->transport->SetConsumer((const struct audio_consumer_webrtc_s*)*_consumer);
  219. if((ret = self->device->SetPlayoutDevice(DOUBANGO_AUDIO_WEBRTC_DEVICE_DEFAULT))) {
  220. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("AudioDeviceModule->SetPlayoutDevice(%d) failed", DOUBANGO_AUDIO_WEBRTC_DEVICE_DEFAULT);
  221. }
  222. if((ret = self->device->SpeakerIsAvailable(&_bool))) {
  223. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("SpeakerIsAvailable() failed with error code=%d", ret);
  224. }
  225. else {
  226. if(!_bool) {
  227. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("SpeakerIsAvailable() returned false");
  228. }
  229. self->isSpeakerAvailable = _bool;
  230. }
  231. if((ret = self->device->InitSpeaker())) {
  232. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("InitSpeaker() failed with error code=%d", ret);
  233. }
  234. if((ret = self->device->PlayoutIsAvailable(&_bool))) {
  235. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("PlayoutIsAvailable() failed with error code =%d", ret);
  236. }
  237. else {
  238. if(!_bool) {
  239. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("PlayoutIsAvailable() returned false");
  240. }
  241. self->isPlayoutAvailable = _bool;
  242. }
  243. if((ret = self->device->SetStereoPlayout(((*_consumer)->audio.in.channels == 2)))) {
  244. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("SetStereoPlayout(%d==2) failed with error code=%d", (*_consumer)->audio.in.channels, ret);
  245. }
  246. //if((ret = self->device->SetPlayoutBuffer(AudioDeviceModule::kFixedBufferSize, (*_consumer)->audio.ptime))){
  247. // DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("SetPlayoutBuffer(%d ms) failed with error code=%d", (*_consumer)->audio.ptime, ret);
  248. //}
  249. // always request 10ms buffers. In all cases WebRTC don't support anything else
  250. if((ret = self->device->SetPlayoutBuffer(AudioDeviceModule::kFixedBufferSize, 10))) {
  251. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("SetPlayoutBuffer(%d ms) failed with error code=%d", 10, ret);
  252. }
  253. uint32_t playoutSampleRate = (*_consumer)->audio.out.rate ? (*_consumer)->audio.out.rate : (*_consumer)->audio.in.rate;
  254. if((ret = self->device->SetPlayoutSampleRate(playoutSampleRate))) {
  255. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("SetPlayoutSampleRate(%d) failed with error code=%d", playoutSampleRate, ret);
  256. }
  257. if((ret = self->device->InitPlayout())) {
  258. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("AudioDeviceModule::InitPlayout() failed with error code = %d", ret);
  259. goto done;
  260. }
  261. // init output parameters
  262. if((ret = self->device->StereoPlayout(&_bool))) {
  263. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("StereoPlayout() failed with error code=%d", ret);
  264. }
  265. else {
  266. (*_consumer)->audio.out.channels = (_bool ? 2 : 1);
  267. }
  268. if((ret = self->device->PlayoutSampleRate(&playoutSampleRate))) {
  269. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("PlayoutSampleRate() failed with error code=%d", ret);
  270. }
  271. else {
  272. (*_consumer)->audio.out.rate = playoutSampleRate;
  273. }
  274. done:
  275. tsk_safeobj_unlock(self);
  276. self->isConsumerPrepared = (ret == 0);
  277. return ret;
  278. }
  279. int audio_webrtc_instance_prepare_producer(audio_webrtc_instance_handle_t* _self, tmedia_producer_t** _producer)
  280. {
  281. audio_webrtc_instance_t* self = (audio_webrtc_instance_t*)_self;
  282. if(!self || !self->device || !self->transport || !_producer || !*_producer) {
  283. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("invalid parameter");
  284. return -1;
  285. }
  286. if(self->isProducerPrepared) {
  287. DOUBANGO_AUDIO_WEBRTC_DEBUG_WARN("Producer already prepared");
  288. return 0;
  289. }
  290. int ret;
  291. bool _bool;
  292. tsk_safeobj_lock(self);
  293. self->transport->SetProducer((const struct audio_producer_webrtc_s*)*_producer);
  294. if((ret = self->device->SetRecordingDevice(DOUBANGO_AUDIO_WEBRTC_DEVICE_DEFAULT))) {
  295. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("AudioDeviceModule->SetRecordingDevice(%d) failed", DOUBANGO_AUDIO_WEBRTC_DEVICE_DEFAULT);
  296. }
  297. if((ret = self->device->RecordingIsAvailable(&_bool))) {
  298. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("RecordingIsAvailable() failed with error code =%d", ret);
  299. }
  300. else {
  301. if(!_bool) {
  302. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("RecordingIsAvailable() returned false");
  303. }
  304. self->isRecordingAvailable = _bool;
  305. }
  306. if((ret = self->device->MicrophoneIsAvailable(&_bool))) {
  307. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("MicrophoneIsAvailable() failed with error code =%d", ret);
  308. }
  309. else {
  310. if(!_bool) {
  311. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("MicrophoneIsAvailable() returned false");
  312. }
  313. else {
  314. if((ret = self->device->InitMicrophone())) {
  315. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("InitMicrophone() failed with error code =%d", ret);
  316. }
  317. }
  318. }
  319. if((ret = self->device->SetStereoRecording(((*_producer)->audio.channels == 2)))) {
  320. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("SetStereoRecording(%d==2) failed with error code=%d", (*_producer)->audio.channels, ret);
  321. }
  322. uint32_t recordingSampleRate = (*_producer)->audio.rate;
  323. if((ret = self->device->SetRecordingSampleRate(recordingSampleRate))) {
  324. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("SetRecordingSampleRate(%d) failed with error code=%d", recordingSampleRate, ret);
  325. }
  326. if((ret = self->device->InitRecording())) {
  327. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("AudioDeviceModule::InitRecording() failed with error code = %d", ret);
  328. goto done;
  329. }
  330. // init output parameters
  331. if((ret = self->device->StereoRecording(&_bool))) {
  332. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("StereoRecording() failed with error code=%d", ret);
  333. }
  334. else {
  335. (*_producer)->audio.channels = (_bool ? 2 : 1);
  336. }
  337. if((ret = self->device->RecordingSampleRate(&recordingSampleRate))) {
  338. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("RecordingSampleRate() failed with error code=%d", ret);
  339. }
  340. else {
  341. (*_producer)->audio.rate = recordingSampleRate;
  342. }
  343. done:
  344. tsk_safeobj_unlock(self);
  345. self->isProducerPrepared = (ret == 0);
  346. return ret;
  347. }
  348. int audio_webrtc_instance_start_consumer(audio_webrtc_instance_handle_t* _self)
  349. {
  350. audio_webrtc_instance_t* self = (audio_webrtc_instance_t*)_self;
  351. if(!self || !self->device || !self->transport) {
  352. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("invalid parameter");
  353. return -1;
  354. }
  355. tsk_safeobj_lock(self);
  356. if(!self->isConsumerPrepared) {
  357. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("Consumer not prepared");
  358. goto done;
  359. }
  360. if(self->isConsumerStarted) {
  361. DOUBANGO_AUDIO_WEBRTC_DEBUG_WARN("Consumer already started");
  362. goto done;
  363. }
  364. if(self->isPlayoutAvailable) {
  365. int ret;
  366. if((ret = self->device->StartPlayout())) {
  367. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("StartPlayout() failed with error code = %d", ret);
  368. }
  369. self->isConsumerStarted = self->device->Playing();
  370. DOUBANGO_AUDIO_WEBRTC_DEBUG_INFO("isPlaying=%s", (self->isConsumerPrepared ? "true" : "false"));
  371. }
  372. done:
  373. tsk_safeobj_unlock(self);
  374. return (self->isConsumerStarted ? 0 : -1);
  375. }
  376. int audio_webrtc_instance_start_producer(audio_webrtc_instance_handle_t* _self)
  377. {
  378. audio_webrtc_instance_t* self = (audio_webrtc_instance_t*)_self;
  379. if(!self || !self->device || !self->transport) {
  380. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("invalid parameter");
  381. return -1;
  382. }
  383. tsk_safeobj_lock(self);
  384. if(!self->isProducerPrepared) {
  385. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("Producer not prepared");
  386. goto done;
  387. }
  388. if(self->isProducerStarted) {
  389. DOUBANGO_AUDIO_WEBRTC_DEBUG_WARN("Consumer already started");
  390. goto done;
  391. }
  392. if(self->isRecordingAvailable) {
  393. int ret;
  394. if((ret = self->device->StartRecording())) {
  395. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("StartRecording() failed with error code = %d", ret);
  396. }
  397. self->isProducerStarted = self->device->Recording();
  398. DOUBANGO_AUDIO_WEBRTC_DEBUG_INFO("isRecording=%s", (self->isProducerStarted ? "true" : "false"));
  399. }
  400. done:
  401. tsk_safeobj_unlock(self);
  402. return (self->isProducerStarted ? 0 : -1);
  403. return 0;
  404. }
  405. int audio_webrtc_instance_stop_consumer(audio_webrtc_instance_handle_t* _self)
  406. {
  407. audio_webrtc_instance_t* self = (audio_webrtc_instance_t*)_self;
  408. if(!self || !self->device || !self->transport) {
  409. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("invalid parameter");
  410. return -1;
  411. }
  412. tsk_safeobj_lock(self);
  413. if(!self->isConsumerStarted) {
  414. goto done;
  415. }
  416. int ret;
  417. if((ret = self->device->StopPlayout())) {
  418. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("StopPlayout() failed with error code = %d", ret);
  419. }
  420. else {
  421. self->isConsumerStarted = self->device->Playing();
  422. }
  423. done:
  424. tsk_safeobj_unlock(self);
  425. return (self->isConsumerStarted ? -1 : 0);
  426. }
  427. int audio_webrtc_instance_stop_producer(audio_webrtc_instance_handle_t* _self)
  428. {
  429. audio_webrtc_instance_t* self = (audio_webrtc_instance_t*)_self;
  430. if(!self || !self->device || !self->transport) {
  431. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("invalid parameter");
  432. return -1;
  433. }
  434. tsk_safeobj_lock(self);
  435. if(!self->isProducerStarted) {
  436. goto done;
  437. }
  438. int ret;
  439. if((ret = self->device->StopRecording())) {
  440. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("StopRecording() failed with error code = %d", ret);
  441. }
  442. else {
  443. self->isProducerStarted = self->device->Recording();
  444. }
  445. done:
  446. tsk_safeobj_unlock(self);
  447. return (self->isProducerStarted ? -1 : 0);
  448. }
  449. int audio_webrtc_instance_destroy(audio_webrtc_instance_handle_t** _self)
  450. {
  451. if(!_self || !*_self) {
  452. DOUBANGO_AUDIO_WEBRTC_DEBUG_ERROR("Invalid parameter");
  453. return -1;
  454. }
  455. tsk_list_lock(__audioInstances);
  456. if(tsk_object_get_refcount(*_self)==1) {
  457. tsk_list_remove_item_by_data(__audioInstances, *_self);
  458. }
  459. else {
  460. tsk_object_unref(*_self);
  461. }
  462. tsk_list_unlock(__audioInstances);
  463. *_self = tsk_null;
  464. return 0;
  465. }