plugin_wasapi_consumer_audio.cxx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  1. /*Copyright (C) 2013 Mamadou Diop
  2. * Copyright (C) 2013 Doubango Telecom <http://www.doubango.org>
  3. *
  4. * This file is part of Open Source Doubango Framework.
  5. *
  6. * DOUBANGO is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * DOUBANGO is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with DOUBANGO.
  18. */
  19. /**@file plugin_wasapi_consumer_audio.cxx
  20. * @brief Microsoft Windows Audio Session API (WASAPI) consumer.
  21. * http://msdn.microsoft.com/en-us/library/windows/desktop/dd316551(v=vs.85).aspx
  22. */
  23. #include "plugin_wasapi_utils.h"
  24. #include "tinydav/audio/tdav_consumer_audio.h"
  25. #include "tsk_thread.h"
  26. #include "tsk_memory.h"
  27. #include "tsk_string.h"
  28. #include "tsk_condwait.h"
  29. #include "tsk_debug.h"
  30. #include <windows.h>
  31. #include <audioclient.h>
  32. #if PLUGIN_WASAPI_UNDER_WINDOWS_PHONE
  33. # include <phoneaudioclient.h>
  34. #else
  35. # include <Mmdeviceapi.h>
  36. #endif
  37. #include <initguid.h>
  38. #include <speex/speex_buffer.h>
  39. #if !defined(PLUGIN_WASAPI_CONSUMER_NOTIF_POS_COUNT)
  40. # define PLUGIN_WASAPI_CONSUMER_NOTIF_POS_COUNT 13
  41. #endif
  42. #define WASAPI_MILLIS_TO_100NS(MILLIS) (((LONGLONG)(MILLIS)) * 10000ui64)
  43. #define WASAPI_100NS_TO_MILLIS(NANOS) (((LONGLONG)(NANOS)) / 10000ui64)
  44. struct plugin_wasapi_consumer_audio_s;
  45. class AudioRender sealed
  46. {
  47. public:
  48. AudioRender();
  49. virtual ~AudioRender();
  50. int Prepare(struct plugin_wasapi_consumer_audio_s* wasapi, const tmedia_codec_t* codec);
  51. int UnPrepare();
  52. int Start();
  53. int Stop();
  54. int Pause();
  55. int Consume(const void* buffer, tsk_size_t size, const tsk_object_t* proto_hdr);
  56. private:
  57. tsk_size_t Read(void* data, tsk_size_t size);
  58. static void* TSK_STDCALL AsyncThread(void *pArg);
  59. private:
  60. tsk_mutex_handle_t* m_hMutex;
  61. const struct plugin_wasapi_consumer_audio_s* m_pWrappedConsumer; // Must not take ref() otherwise dtor() will be never called (circular reference)
  62. #if PLUGIN_WASAPI_UNDER_WINDOWS_PHONE
  63. IAudioClient2* m_pDevice;
  64. #else
  65. IAudioClient* m_pDevice;
  66. #endif
  67. IAudioRenderClient* m_pClient;
  68. tsk_condwait_handle_t* m_hCondWait;
  69. tsk_thread_handle_t* m_ppTread[1];
  70. INT32 m_nBytesPerNotif;
  71. INT32 m_nSourceFrameSizeInBytes;
  72. UINT32 m_nMaxFrameCount;
  73. UINT32 m_nPtime;
  74. UINT32 m_nChannels;
  75. struct {
  76. struct {
  77. void* buffer;
  78. tsk_size_t size;
  79. } chunck;
  80. tsk_ssize_t leftBytes;
  81. SpeexBuffer* buffer;
  82. tsk_size_t size;
  83. } m_ring;
  84. bool m_bStarted;
  85. bool m_bPrepared;
  86. bool m_bPaused;
  87. };
  88. typedef struct plugin_wasapi_consumer_audio_s {
  89. TDAV_DECLARE_CONSUMER_AUDIO;
  90. AudioRender* pAudioRender;
  91. }
  92. plugin_wasapi_consumer_audio_t;
  93. /* ============ Media consumer Interface ================= */
  94. static int plugin_wasapi_consumer_audio_set(tmedia_consumer_t* self, const tmedia_param_t* param)
  95. {
  96. return tdav_consumer_audio_set(TDAV_CONSUMER_AUDIO(self), param);
  97. }
  98. static int plugin_wasapi_consumer_audio_prepare(tmedia_consumer_t* self, const tmedia_codec_t* codec)
  99. {
  100. plugin_wasapi_consumer_audio_t* wasapi = (plugin_wasapi_consumer_audio_t*)self;
  101. if(!wasapi || !codec || !wasapi->pAudioRender) {
  102. TSK_DEBUG_ERROR("Invalid parameter");
  103. return -1;
  104. }
  105. TMEDIA_CONSUMER(wasapi)->audio.ptime = TMEDIA_CODEC_PTIME_AUDIO_DECODING(codec);
  106. TMEDIA_CONSUMER(wasapi)->audio.in.channels = TMEDIA_CODEC_CHANNELS_AUDIO_DECODING(codec);
  107. TMEDIA_CONSUMER(wasapi)->audio.in.rate = TMEDIA_CODEC_RATE_DECODING(codec);
  108. TSK_DEBUG_INFO("WASAPI consumer: in.channels=%d, out.channles=%d, in.rate=%d, out.rate=%d, ptime=%d",
  109. TMEDIA_CONSUMER(wasapi)->audio.in.channels,
  110. TMEDIA_CONSUMER(wasapi)->audio.out.channels,
  111. TMEDIA_CONSUMER(wasapi)->audio.in.rate,
  112. TMEDIA_CONSUMER(wasapi)->audio.out.rate,
  113. TMEDIA_CONSUMER(wasapi)->audio.ptime);
  114. return wasapi->pAudioRender->Prepare(wasapi, codec);
  115. }
  116. static int plugin_wasapi_consumer_audio_start(tmedia_consumer_t* self)
  117. {
  118. plugin_wasapi_consumer_audio_t* wasapi = (plugin_wasapi_consumer_audio_t*)self;
  119. TSK_DEBUG_INFO("plugin_wasapi_consumer_audio_start()");
  120. if(!wasapi || !wasapi->pAudioRender) {
  121. TSK_DEBUG_ERROR("Invalid parameter");
  122. return -1;
  123. }
  124. return wasapi->pAudioRender->Start();
  125. }
  126. static int plugin_wasapi_consumer_audio_consume(tmedia_consumer_t* self, const void* buffer, tsk_size_t size, const tsk_object_t* proto_hdr)
  127. {
  128. plugin_wasapi_consumer_audio_t* wasapi = (plugin_wasapi_consumer_audio_t*)self;
  129. if(!wasapi || !wasapi->pAudioRender || !buffer || !size) {
  130. TSK_DEBUG_ERROR("Invalid parameter");
  131. return -1;
  132. }
  133. return wasapi->pAudioRender->Consume(buffer, size, proto_hdr);
  134. }
  135. static int plugin_wasapi_consumer_audio_pause(tmedia_consumer_t* self)
  136. {
  137. plugin_wasapi_consumer_audio_t* wasapi = (plugin_wasapi_consumer_audio_t*)self;
  138. if(!wasapi || !wasapi->pAudioRender) {
  139. TSK_DEBUG_ERROR("Invalid parameter");
  140. return -1;
  141. }
  142. return wasapi->pAudioRender->Pause();
  143. }
  144. static int plugin_wasapi_consumer_audio_stop(tmedia_consumer_t* self)
  145. {
  146. plugin_wasapi_consumer_audio_t* wasapi = (plugin_wasapi_consumer_audio_t*)self;
  147. TSK_DEBUG_INFO("plugin_wasapi_consumer_audio_stop()");
  148. if(!wasapi || !wasapi->pAudioRender) {
  149. TSK_DEBUG_ERROR("Invalid parameter");
  150. return -1;
  151. }
  152. return wasapi->pAudioRender->Stop();
  153. }
  154. AudioRender::AudioRender()
  155. : m_pDevice(NULL)
  156. , m_hMutex(NULL)
  157. , m_pClient(NULL)
  158. , m_hCondWait(NULL)
  159. , m_pWrappedConsumer(NULL)
  160. , m_nBytesPerNotif(0)
  161. , m_nSourceFrameSizeInBytes(0)
  162. , m_nMaxFrameCount(0)
  163. , m_nPtime(0)
  164. , m_nChannels(1)
  165. , m_bStarted(false)
  166. , m_bPrepared(false)
  167. , m_bPaused(false)
  168. {
  169. m_ppTread[0] = NULL;
  170. memset(&m_ring, 0, sizeof(m_ring));
  171. if(!(m_hMutex = tsk_mutex_create())) {
  172. TSK_DEBUG_ERROR("Failed to create mutex");
  173. }
  174. }
  175. AudioRender::~AudioRender()
  176. {
  177. Stop();
  178. UnPrepare();
  179. tsk_mutex_destroy(&m_hMutex);
  180. }
  181. int AudioRender::Prepare(plugin_wasapi_consumer_audio_t* wasapi, const tmedia_codec_t* codec)
  182. {
  183. HRESULT hr = E_FAIL;
  184. int ret = 0;
  185. WAVEFORMATEX wfx = {0};
  186. #if PLUGIN_WASAPI_UNDER_WINDOWS_PHONE
  187. AudioClientProperties properties = {0};
  188. #endif
  189. LPCWSTR pwstrRenderId = NULL;
  190. IMMDeviceEnumerator *pEnumerator = NULL;
  191. IMMDevice *pDevice = NULL;
  192. tsk_mutex_lock(m_hMutex);
  193. if(m_bPrepared) {
  194. TSK_DEBUG_INFO("#WASAPI: Audio consumer already prepared");
  195. goto bail;
  196. }
  197. if(!wasapi || !codec) {
  198. TSK_DEBUG_ERROR("Invalid parameter");
  199. CHECK_HR(hr = E_FAIL);
  200. }
  201. if(m_pDevice || m_pClient) {
  202. TSK_DEBUG_ERROR("consumer already prepared");
  203. CHECK_HR(hr = E_FAIL);
  204. }
  205. #if PLUGIN_WASAPI_UNDER_WINDOWS_PHONE
  206. pwstrRenderId = GetDefaultAudioRenderId(AudioDeviceRole::Communications);
  207. if (NULL == pwstrRenderId) {
  208. PLUGIN_WASAPI_ERROR("GetDefaultAudioRenderId", HRESULT_FROM_WIN32(GetLastError()));
  209. CHECK_HR(hr = E_FAIL);
  210. }
  211. CHECK_HR(hr = ActivateAudioInterface(pwstrRenderId, __uuidof(IAudioClient2), (void**)&m_pDevice));
  212. // Win8 or WP8 only
  213. properties.cbSize = sizeof AudioClientProperties;
  214. properties.eCategory = AudioCategory_Communications;
  215. CHECK_HR(hr = m_pDevice->SetClientProperties(&properties));
  216. #else
  217. CHECK_HR(hr = CoCreateInstance(
  218. CLSID_MMDeviceEnumerator, NULL,
  219. CLSCTX_ALL, IID_IMMDeviceEnumerator,
  220. (void**)&pEnumerator));
  221. CHECK_HR(hr = pEnumerator->GetDefaultAudioEndpoint(
  222. eRender, eCommunications, &pDevice));
  223. CHECK_HR(hr = pDevice->Activate(
  224. IID_IAudioClient, CLSCTX_ALL,
  225. NULL, (void**)&m_pDevice));
  226. #endif
  227. /* Set best format */
  228. {
  229. wfx.wFormatTag = WAVE_FORMAT_PCM;
  230. wfx.nChannels = TMEDIA_CONSUMER(wasapi)->audio.in.channels;
  231. wfx.nSamplesPerSec = TMEDIA_CONSUMER(wasapi)->audio.in.rate;
  232. wfx.wBitsPerSample = TMEDIA_CONSUMER(wasapi)->audio.bits_per_sample;
  233. wfx.nBlockAlign = (wfx.nChannels * wfx.wBitsPerSample/8);
  234. wfx.nAvgBytesPerSec = (wfx.nSamplesPerSec * wfx.nBlockAlign);
  235. PWAVEFORMATEX pwfxClosestMatch = NULL;
  236. hr = m_pDevice->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &wfx, &pwfxClosestMatch);
  237. if(hr != S_OK && hr != S_FALSE) {
  238. PLUGIN_WASAPI_ERROR(HRESULT_FROM_WIN32(GetLastError()));
  239. CHECK_HR(hr = E_FAIL);
  240. }
  241. if(hr == S_FALSE) {
  242. if(!pwfxClosestMatch) {
  243. TSK_DEBUG_ERROR("malloc(%d) failed", sizeof(WAVEFORMATEX));
  244. CHECK_HR(hr = E_OUTOFMEMORY);
  245. }
  246. wfx.nSamplesPerSec = pwfxClosestMatch->nSamplesPerSec;
  247. wfx.nChannels = pwfxClosestMatch->nChannels;
  248. #if 0
  249. wfx.wBitsPerSample = pwfxClosestMatch->wBitsPerSample;
  250. #endif
  251. wfx.nBlockAlign = wfx.nChannels * (wfx.wBitsPerSample / 8);
  252. wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
  253. // Request resampler
  254. TMEDIA_CONSUMER(wasapi)->audio.out.rate = (uint32_t)wfx.nSamplesPerSec;
  255. TMEDIA_CONSUMER(wasapi)->audio.bits_per_sample = (uint8_t)wfx.wBitsPerSample;
  256. TMEDIA_CONSUMER(wasapi)->audio.out.channels = (uint8_t)wfx.nChannels;
  257. TSK_DEBUG_INFO("Audio device format fallback: rate=%d, bps=%d, channels=%d", wfx.nSamplesPerSec, wfx.wBitsPerSample, wfx.nChannels);
  258. }
  259. if(pwfxClosestMatch) {
  260. CoTaskMemFree(pwfxClosestMatch);
  261. }
  262. }
  263. m_nSourceFrameSizeInBytes = (wfx.wBitsPerSample >> 3) * wfx.nChannels;
  264. m_nBytesPerNotif = ((wfx.nAvgBytesPerSec * TMEDIA_CONSUMER(wasapi)->audio.ptime)/1000) * wfx.nChannels;
  265. // Initialize
  266. CHECK_HR(hr = m_pDevice->Initialize(
  267. AUDCLNT_SHAREMODE_SHARED,
  268. 0x00000000,
  269. (PLUGIN_WASAPI_CONSUMER_NOTIF_POS_COUNT * WASAPI_MILLIS_TO_100NS(TMEDIA_CONSUMER(wasapi)->audio.ptime)) ,
  270. 0,
  271. &wfx,
  272. NULL));
  273. REFERENCE_TIME DefaultDevicePeriod, MinimumDevicePeriod;
  274. CHECK_HR(hr = m_pDevice->GetDevicePeriod(&DefaultDevicePeriod, &MinimumDevicePeriod));
  275. CHECK_HR(hr = m_pDevice->GetBufferSize(&m_nMaxFrameCount));
  276. TSK_DEBUG_INFO("#WASAPI (Playback): BufferSize=%u, DefaultDevicePeriod=%lld ms, MinimumDevicePeriod=%lldms", m_nMaxFrameCount, WASAPI_100NS_TO_MILLIS(DefaultDevicePeriod), WASAPI_100NS_TO_MILLIS(MinimumDevicePeriod));
  277. if(!m_hCondWait) {
  278. if(!(m_hCondWait = tsk_condwait_create())) {
  279. PLUGIN_WASAPI_ERROR(HRESULT_FROM_WIN32(GetLastError()));
  280. CHECK_HR(hr = E_FAIL);
  281. }
  282. }
  283. CHECK_HR(hr = m_pDevice->GetService(__uuidof(IAudioRenderClient), (void**)&m_pClient));
  284. int packetperbuffer = (1000 / TMEDIA_CONSUMER(wasapi)->audio.ptime);
  285. m_ring.chunck.size = (wfx.nSamplesPerSec * (wfx.wBitsPerSample >> 3) / packetperbuffer) * wfx.nChannels;
  286. m_ring.size = PLUGIN_WASAPI_CONSUMER_NOTIF_POS_COUNT * m_ring.chunck.size;
  287. if(!(m_ring.chunck.buffer = tsk_realloc(m_ring.chunck.buffer, m_ring.chunck.size))) {
  288. m_ring.size = 0;
  289. TSK_DEBUG_ERROR("Failed to allocate new buffer");
  290. CHECK_HR(hr = E_OUTOFMEMORY);
  291. }
  292. if(!m_ring.buffer) {
  293. m_ring.buffer = speex_buffer_init(m_ring.size);
  294. }
  295. else {
  296. int sret;
  297. if((sret = speex_buffer_resize(m_ring.buffer, m_ring.size)) < 0) {
  298. TSK_DEBUG_ERROR("speex_buffer_resize(%d) failed with error code=%d", m_ring.size, sret);
  299. CHECK_HR(hr = E_OUTOFMEMORY);
  300. }
  301. }
  302. if(!m_ring.buffer) {
  303. TSK_DEBUG_ERROR("Failed to create a new ring buffer with size = %d", m_ring.size);
  304. CHECK_HR(hr = E_OUTOFMEMORY);
  305. }
  306. bail:
  307. ret = SUCCEEDED(hr) ? 0 : -1;
  308. if (pwstrRenderId) {
  309. CoTaskMemFree((LPVOID)pwstrRenderId);
  310. }
  311. if(ret != 0) {
  312. UnPrepare();
  313. }
  314. if((m_bPrepared = (ret == 0))) {
  315. m_pWrappedConsumer = wasapi;
  316. m_nPtime = TMEDIA_CONSUMER(wasapi)->audio.ptime;
  317. m_nChannels = TMEDIA_CONSUMER(wasapi)->audio.out.channels;
  318. }
  319. tsk_mutex_unlock(m_hMutex);
  320. SafeRelease(&pEnumerator);
  321. SafeRelease(&pDevice);
  322. return ret;
  323. }
  324. int AudioRender::UnPrepare()
  325. {
  326. tsk_mutex_lock(m_hMutex);
  327. if(m_hCondWait) {
  328. tsk_condwait_destroy(&m_hCondWait);
  329. }
  330. if(m_pDevice) {
  331. m_pDevice->Release(), m_pDevice = NULL;
  332. }
  333. if(m_pClient) {
  334. m_pClient->Release(), m_pClient = NULL;
  335. }
  336. TSK_FREE(m_ring.chunck.buffer);
  337. if(m_ring.buffer) {
  338. speex_buffer_destroy(m_ring.buffer);
  339. m_ring.buffer = NULL;
  340. }
  341. m_pWrappedConsumer = NULL;
  342. m_bPrepared = false;
  343. tsk_mutex_unlock(m_hMutex);
  344. return 0;
  345. }
  346. int AudioRender::Start()
  347. {
  348. tsk_mutex_lock(m_hMutex);
  349. if(m_bStarted) {
  350. TSK_DEBUG_INFO("#WASAPI: Audio consumer already started");
  351. goto bail;
  352. }
  353. if(!m_bPrepared) {
  354. TSK_DEBUG_ERROR("Audio consumer not prepared");
  355. goto bail;
  356. }
  357. m_bStarted = true;
  358. if(!m_ppTread[0] && tsk_thread_create(m_ppTread, AudioRender::AsyncThread, this) != 0) {
  359. m_bStarted = false;
  360. goto bail;
  361. }
  362. HRESULT hr = m_pDevice->Start();
  363. if(!SUCCEEDED(hr)) {
  364. PLUGIN_WASAPI_ERROR(hr);
  365. Stop();
  366. }
  367. m_bPaused = false;
  368. bail:
  369. tsk_mutex_unlock(m_hMutex);
  370. return (m_bStarted ? 0 : -2);
  371. }
  372. int AudioRender::Stop()
  373. {
  374. m_bStarted = false;
  375. tsk_mutex_lock(m_hMutex);
  376. if (m_hCondWait) {
  377. tsk_condwait_broadcast(m_hCondWait);
  378. }
  379. if (m_ppTread[0]) {
  380. tsk_thread_join(m_ppTread);
  381. }
  382. if(m_pDevice) {
  383. m_pDevice->Stop();
  384. }
  385. // will be prepared again before next start()
  386. UnPrepare();
  387. tsk_mutex_unlock(m_hMutex);
  388. return 0;
  389. }
  390. int AudioRender::Pause()
  391. {
  392. m_bPaused = true;
  393. return 0;
  394. }
  395. int AudioRender::Consume(const void* buffer, tsk_size_t size, const tsk_object_t* proto_hdr)
  396. {
  397. return tdav_consumer_audio_put(TDAV_CONSUMER_AUDIO(m_pWrappedConsumer), buffer, size, proto_hdr);
  398. }
  399. tsk_size_t AudioRender::Read(void* data, tsk_size_t size)
  400. {
  401. tsk_ssize_t retSize = 0;
  402. m_ring.leftBytes += size;
  403. while (m_ring.leftBytes >= (tsk_ssize_t)m_ring.chunck.size) {
  404. m_ring.leftBytes -= m_ring.chunck.size;
  405. retSize = (tsk_ssize_t)tdav_consumer_audio_get(TDAV_CONSUMER_AUDIO(m_pWrappedConsumer), m_ring.chunck.buffer, m_ring.chunck.size);
  406. tdav_consumer_audio_tick(TDAV_CONSUMER_AUDIO(m_pWrappedConsumer));
  407. speex_buffer_write(m_ring.buffer, m_ring.chunck.buffer, retSize);
  408. }
  409. // IMPORTANT: looks like there is a bug in speex: continously trying to read more than avail
  410. // many times can corrupt the buffer. At least on OS X 1.5
  411. int avail = speex_buffer_get_available(m_ring.buffer);
  412. //if(speex_buffer_get_available(m_ring.buffer) >= (tsk_ssize_t)size)
  413. //{
  414. retSize = speex_buffer_read(m_ring.buffer, data, TSK_MIN((int)size,avail));
  415. //}
  416. //else
  417. //{
  418. //memset(data, 0, size);
  419. //}
  420. return retSize;
  421. }
  422. void* TSK_STDCALL AudioRender::AsyncThread(void *pArg)
  423. {
  424. HRESULT hr = S_OK;
  425. INT32 nFramesToWrite;
  426. UINT32 nPadding, nRead;
  427. int waitResult = 0;
  428. AudioRender* This = (AudioRender*)pArg;
  429. TSK_DEBUG_INFO("#WASAPI: __playback_thread -- START");
  430. #define BREAK_WHILE tsk_mutex_unlock(This->m_hMutex); break;
  431. while(This->m_bStarted && SUCCEEDED(hr)) {
  432. waitResult = tsk_condwait_timedwait(This->m_hCondWait, This->m_nPtime);
  433. tsk_mutex_lock(This->m_hMutex);
  434. if(!This->m_bStarted) {
  435. BREAK_WHILE;
  436. }
  437. if(waitResult == 0) {
  438. hr = This->m_pDevice->GetCurrentPadding(&nPadding);
  439. if (SUCCEEDED(hr)) {
  440. BYTE* pRenderBuffer = NULL;
  441. nFramesToWrite = This->m_nMaxFrameCount - nPadding;
  442. if (nFramesToWrite > 0) {
  443. hr = This->m_pClient->GetBuffer(nFramesToWrite, &pRenderBuffer);
  444. if (SUCCEEDED(hr)) {
  445. nRead = This->Read(pRenderBuffer, (nFramesToWrite * This->m_nSourceFrameSizeInBytes));
  446. // Release the buffer
  447. hr = This->m_pClient->ReleaseBuffer((nRead / This->m_nSourceFrameSizeInBytes), (nRead == 0) ? AUDCLNT_BUFFERFLAGS_SILENT: 0);
  448. }
  449. }
  450. }
  451. }
  452. else {
  453. BREAK_WHILE;
  454. }
  455. tsk_mutex_lock(This->m_hMutex);
  456. }// end-of-while
  457. if (!SUCCEEDED(hr)) {
  458. PLUGIN_WASAPI_ERROR(hr);
  459. }
  460. TSK_DEBUG_INFO("WASAPI: __playback_thread(%s) -- STOP", (SUCCEEDED(hr) && waitResult == 0) ? "OK": "NOK");
  461. return NULL;
  462. }
  463. //
  464. // WaveAPI consumer object definition
  465. //
  466. /* constructor */
  467. static tsk_object_t* plugin_wasapi_consumer_audio_ctor(tsk_object_t * self, va_list * app)
  468. {
  469. plugin_wasapi_consumer_audio_t *wasapi = (plugin_wasapi_consumer_audio_t*)self;
  470. if(wasapi) {
  471. WASAPIUtils::Startup();
  472. /* init base */
  473. tdav_consumer_audio_init(TDAV_CONSUMER_AUDIO(wasapi));
  474. /* init self */
  475. wasapi->pAudioRender = new AudioRender();
  476. if(!wasapi->pAudioRender) {
  477. TSK_DEBUG_ERROR("Failed to create renderer");
  478. return tsk_null;
  479. }
  480. }
  481. return self;
  482. }
  483. /* destructor */
  484. static tsk_object_t* plugin_wasapi_consumer_audio_dtor(tsk_object_t * self)
  485. {
  486. plugin_wasapi_consumer_audio_t *wasapi = (plugin_wasapi_consumer_audio_t*)self;
  487. if(wasapi) {
  488. /* stop */
  489. plugin_wasapi_consumer_audio_stop((tmedia_consumer_t*)self);
  490. /* deinit base */
  491. tdav_consumer_audio_deinit(TDAV_CONSUMER_AUDIO(wasapi));
  492. /* deinit self */
  493. if(wasapi->pAudioRender) {
  494. delete wasapi->pAudioRender;
  495. wasapi->pAudioRender = NULL;
  496. }
  497. }
  498. return self;
  499. }
  500. /* object definition */
  501. static const tsk_object_def_t plugin_wasapi_consumer_audio_def_s = {
  502. sizeof(plugin_wasapi_consumer_audio_t),
  503. plugin_wasapi_consumer_audio_ctor,
  504. plugin_wasapi_consumer_audio_dtor,
  505. tdav_consumer_audio_cmp,
  506. };
  507. /* plugin definition*/
  508. static const tmedia_consumer_plugin_def_t plugin_wasapi_consumer_audio_plugin_def_s = {
  509. &plugin_wasapi_consumer_audio_def_s,
  510. tmedia_audio,
  511. "Microsoft Windows Audio Session API (WASAPI) consumer",
  512. plugin_wasapi_consumer_audio_set,
  513. plugin_wasapi_consumer_audio_prepare,
  514. plugin_wasapi_consumer_audio_start,
  515. plugin_wasapi_consumer_audio_consume,
  516. plugin_wasapi_consumer_audio_pause,
  517. plugin_wasapi_consumer_audio_stop
  518. };
  519. const tmedia_consumer_plugin_def_t *plugin_wasapi_consumer_audio_plugin_def_t = &plugin_wasapi_consumer_audio_plugin_def_s;