1 /* $Id$ */
2 /*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 *
5 * This program 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 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program 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 this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19 #include <pjmedia-audiodev/audiodev_imp.h>
20 #include <pj/assert.h>
21 #include <pj/log.h>
22 #include <pj/os.h>
23
24 #if PJMEDIA_AUDIO_DEV_HAS_WASAPI
25
26 #include <Avrt.h>
27 #include <windows.h>
28 #include <audioclient.h>
29 #include <Processthreadsapi.h>
30
31 #if defined(PJ_WIN32_UWP) && PJ_WIN32_UWP
32 #define USE_ASYNC_ACTIVATE 1;
33 #endif
34
35 #if defined(USE_ASYNC_ACTIVATE)
36 #include <Mmdeviceapi.h>
37 #include <wrl\implements.h>
38 #include <ppltasks.h>
39 using namespace Windows::Media::Devices;
40 using namespace Microsoft::WRL;
41 using namespace concurrency;
42 #else
43 #include <phoneaudioclient.h>
44 #using <Windows.winmd>
45 using namespace Windows::Phone::Media::Devices;
46 #endif
47
48 #define THIS_FILE "wasapi_dev.cpp"
49
50 /* Set to 1 to enable tracing */
51 #if 1
52 # define TRACE_(expr) PJ_LOG(4,expr)
53 #else
54 # define TRACE_(expr)
55 #endif
56
57 #define EXIT_ON_ERROR(hr) \
58 do { \
59 if(FAILED(hr)) { \
60 status = PJMEDIA_EAUD_WASAPI_ERROR; \
61 goto on_exit; }\
62 } while (0)
63
64 struct wasapi_stream;
65
66 class AudioActivator;
67
68 #if defined(USE_ASYNC_ACTIVATE)
69
70 class AudioActivator : public RuntimeClass< RuntimeClassFlags< ClassicCom >,
71 FtmBase,
72 IActivateAudioInterfaceCompletionHandler >
73 {
74 STDMETHODIMP ActivateCompleted(IActivateAudioInterfaceAsyncOperation
75 *pAsyncOp);
76 public:
AudioActivator()77 AudioActivator() {};
78 task_completion_event<ComPtr<IAudioClient2>> task_completed;
79 };
80
81 #endif
82
83 /* WASAPI factory */
84 struct wasapi_factory
85 {
86 pjmedia_aud_dev_factory base;
87 pj_pool_t *base_pool;
88 pj_pool_t *pool;
89 pj_pool_factory *pf;
90
91 unsigned dev_count;
92 pjmedia_aud_dev_info devs[1];
93 };
94
95 /* Sound stream. */
96 struct wasapi_stream
97 {
98 pjmedia_aud_stream base; /* Base stream */
99
100 /* Common */
101 pjmedia_aud_param param; /* Settings */
102 pj_pool_t *pool; /* Memory pool */
103 void *user_data; /* Application data */
104 struct wasapi_factory *wf;
105 pj_thread_t *thread; /* Thread handle */
106 HANDLE quit_event;
107 pjmedia_format_id fmt_id; /* Frame format */
108 unsigned bytes_per_sample;
109
110 /* Playback */
111 pjmedia_aud_play_cb pb_cb; /* Playback callback */
112 IAudioClient2 *default_pb_dev; /* Default playback dev */
113 IAudioRenderClient *pb_client; /* Playback client */
114 ISimpleAudioVolume *pb_volume; /* Playback volume */
115 unsigned pb_max_frame_count;
116 HANDLE pb_event; /* Playback event */
117 pj_timestamp pb_timestamp;
118 #if defined(USE_ASYNC_ACTIVATE)
119 ComPtr<AudioActivator> pb_aud_act;
120 Platform::String^ pb_id;
121 #else
122 LPCWSTR pb_id;
123 #endif
124
125 /* Capture */
126 pjmedia_aud_rec_cb cap_cb; /* Capture callback */
127 IAudioClient2 *default_cap_dev; /* Default capture dev */
128 IAudioCaptureClient *cap_client; /* Capture client */
129 unsigned bytes_per_frame; /* Bytes per frame */
130 pj_int16_t *cap_buf; /* Capture audio buffer */
131 unsigned cap_max_frame_count;
132 HANDLE cap_event; /* Capture event */
133 unsigned cap_buf_count;
134 pj_timestamp cap_timestamp;
135 #if defined(USE_ASYNC_ACTIVATE)
136 ComPtr<AudioActivator> cap_aud_act;
137 Platform::String^ cap_id;
138 #else
139 LPCWSTR cap_id;
140 #endif
141 };
142
143 /* Prototypes */
144 static pj_status_t wasapi_factory_init(pjmedia_aud_dev_factory *f);
145 static pj_status_t wasapi_factory_destroy(pjmedia_aud_dev_factory *f);
146 static pj_status_t wasapi_factory_refresh(pjmedia_aud_dev_factory *f);
147 static unsigned wasapi_factory_get_dev_count(pjmedia_aud_dev_factory *f);
148 static pj_status_t wasapi_factory_get_dev_info(pjmedia_aud_dev_factory *f,
149 unsigned index,
150 pjmedia_aud_dev_info *info);
151 static pj_status_t wasapi_factory_default_param(pjmedia_aud_dev_factory *f,
152 unsigned index,
153 pjmedia_aud_param *param);
154 static pj_status_t wasapi_factory_create_stream(pjmedia_aud_dev_factory *f,
155 const pjmedia_aud_param *param,
156 pjmedia_aud_rec_cb rec_cb,
157 pjmedia_aud_play_cb play_cb,
158 void *user_data,
159 pjmedia_aud_stream **p_strm);
160
161 /*
162 * Stream prototypes
163 */
164 static pj_status_t wasapi_stream_get_param(pjmedia_aud_stream *strm,
165 pjmedia_aud_param *param);
166 static pj_status_t wasapi_stream_get_cap(pjmedia_aud_stream *strm,
167 pjmedia_aud_dev_cap cap,
168 void *value);
169 static pj_status_t wasapi_stream_set_cap(pjmedia_aud_stream *strm,
170 pjmedia_aud_dev_cap cap,
171 const void *value);
172 static pj_status_t wasapi_stream_start(pjmedia_aud_stream *strm);
173 static pj_status_t wasapi_stream_stop(pjmedia_aud_stream *strm);
174 static pj_status_t wasapi_stream_destroy(pjmedia_aud_stream *strm);
175
176 /* Operations */
177 static pjmedia_aud_dev_factory_op factory_op =
178 {
179 &wasapi_factory_init,
180 &wasapi_factory_destroy,
181 &wasapi_factory_get_dev_count,
182 &wasapi_factory_get_dev_info,
183 &wasapi_factory_default_param,
184 &wasapi_factory_create_stream,
185 &wasapi_factory_refresh
186 };
187
188 static pjmedia_aud_stream_op stream_op =
189 {
190 &wasapi_stream_get_param,
191 &wasapi_stream_get_cap,
192 &wasapi_stream_set_cap,
193 &wasapi_stream_start,
194 &wasapi_stream_stop,
195 &wasapi_stream_destroy
196 };
197
init_waveformatex(WAVEFORMATEX * wfx,const pjmedia_aud_param * prm,struct wasapi_stream * strm)198 static pj_status_t init_waveformatex(WAVEFORMATEX *wfx,
199 const pjmedia_aud_param *prm,
200 struct wasapi_stream *strm)
201 {
202 if (prm->ext_fmt.id == PJMEDIA_FORMAT_L16) {
203 wfx->wFormatTag = WAVE_FORMAT_PCM;
204 wfx->nChannels = (pj_uint16_t)prm->channel_count;
205 wfx->nSamplesPerSec = prm->clock_rate;
206 wfx->nBlockAlign = (pj_uint16_t)(prm->channel_count *
207 strm->bytes_per_sample);
208 wfx->nAvgBytesPerSec = prm->clock_rate * prm->channel_count *
209 strm->bytes_per_sample;
210 wfx->wBitsPerSample = (WORD)prm->bits_per_sample;
211 wfx->cbSize = 0;
212
213 return PJ_SUCCESS;
214 }
215 else if ((prm->flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT) &&
216 (prm->ext_fmt.id == PJMEDIA_FORMAT_PCMA ||
217 prm->ext_fmt.id == PJMEDIA_FORMAT_PCMU))
218 {
219 unsigned ptime;
220 ptime = prm->samples_per_frame * 1000 /
221 (prm->clock_rate * prm->channel_count);
222 wfx->wFormatTag = (pj_uint16_t)
223 ((prm->ext_fmt.id == PJMEDIA_FORMAT_PCMA) ?
224 WAVE_FORMAT_ALAW : WAVE_FORMAT_MULAW);
225 wfx->nChannels = (pj_uint16_t)prm->channel_count;
226 wfx->nSamplesPerSec = prm->clock_rate;
227 wfx->nAvgBytesPerSec = prm->clock_rate * prm->channel_count;
228 wfx->nBlockAlign = (pj_uint16_t)(wfx->nAvgBytesPerSec * ptime /
229 1000);
230 wfx->wBitsPerSample = 8;
231 wfx->cbSize = 0;
232
233 return PJ_SUCCESS;
234 } else {
235 return PJMEDIA_EAUD_BADFORMAT;
236 }
237 }
238
239 /* WMME capture and playback thread. */
wasapi_dev_thread(void * arg)240 static int PJ_THREAD_FUNC wasapi_dev_thread(void *arg)
241 {
242 struct wasapi_stream *strm = (struct wasapi_stream*)arg;
243 HANDLE events[3];
244 unsigned eventCount;
245 pj_status_t status = PJ_SUCCESS;
246 static unsigned rec_cnt, play_cnt;
247 enum { MAX_BURST = 1000 };
248
249 rec_cnt = play_cnt = 0;
250 strm->cap_buf_count = 0;
251 eventCount = 0;
252 events[eventCount++] = strm->quit_event;
253 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK)
254 events[eventCount++] = strm->pb_event;
255 if (strm->param.dir & PJMEDIA_DIR_CAPTURE)
256 events[eventCount++] = strm->cap_event;
257
258 /*
259 * Loop while not signalled to quit, wait for event objects to be
260 * signalled by Wasapi capture and play buffer.
261 */
262 while (status == PJ_SUCCESS)
263 {
264 DWORD rc;
265 pjmedia_dir signalled_dir;
266 HRESULT hr;
267
268 /* Swap hWaveIn and hWaveOut to get equal opportunity for both */
269 if (eventCount==3) {
270 HANDLE hTemp = events[2];
271 events[2] = events[1];
272 events[1] = hTemp;
273 }
274
275 rc = WaitForMultipleObjectsEx(eventCount, events, FALSE, INFINITE,
276 FALSE);
277
278 if (rc < WAIT_OBJECT_0 || rc >= WAIT_OBJECT_0 + eventCount)
279 continue;
280
281 if (rc == WAIT_OBJECT_0)
282 break;
283
284 if (rc == (WAIT_OBJECT_0 + 1)) {
285 if (events[1] == strm->pb_event)
286 signalled_dir = PJMEDIA_DIR_PLAYBACK;
287 else
288 signalled_dir = PJMEDIA_DIR_CAPTURE;
289 } else {
290 if (events[2] == strm->pb_event)
291 signalled_dir = PJMEDIA_DIR_PLAYBACK;
292 else
293 signalled_dir = PJMEDIA_DIR_CAPTURE;
294 }
295
296 if (signalled_dir == PJMEDIA_DIR_PLAYBACK) {
297 unsigned incoming_frame = 0;
298 unsigned frame_to_render = 0;
299 unsigned padding = 0;
300 BYTE *cur_pb_buf = NULL;
301 pjmedia_frame frame;
302
303 status = PJ_SUCCESS;
304
305 /* Get available space on buffer to render */
306 hr = strm->default_pb_dev->GetCurrentPadding(&padding);
307 if (FAILED(hr)) {
308 continue;
309 }
310
311 incoming_frame = strm->bytes_per_frame /
312 (strm->bytes_per_sample *
313 strm->param.channel_count);
314 frame_to_render = strm->pb_max_frame_count - padding;
315 if (frame_to_render >= incoming_frame) {
316 frame_to_render = incoming_frame;
317 } else {
318 /* Don't get new frame because there's no space */
319 continue;
320 }
321
322 hr = strm->pb_client->GetBuffer(frame_to_render, &cur_pb_buf);
323 if (FAILED(hr)) {
324 PJ_LOG(4, (THIS_FILE, "Error getting wasapi buffer"));
325 continue;
326 }
327
328 if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
329 /* PCM mode */
330 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
331 frame.size = strm->bytes_per_frame;
332 frame.timestamp.u64 = strm->pb_timestamp.u64;
333 frame.bit_info = 0;
334 frame.buf = cur_pb_buf;
335 }
336
337 status = (*strm->pb_cb)(strm->user_data, &frame);
338
339 /* Write to the device. */
340 hr = strm->pb_client->ReleaseBuffer(frame_to_render, 0);
341 if (FAILED(hr)) {
342 PJ_LOG(4, (THIS_FILE, "Error releasing wasapi buffer"));
343 continue;
344 }
345
346 strm->pb_timestamp.u64 += strm->param.samples_per_frame /
347 strm->param.channel_count;
348 } else {
349 /* Capture */
350 pj_uint32_t packet_size = 0;
351
352 hr = strm->cap_client->GetNextPacketSize(&packet_size);
353 if (FAILED(hr)) {
354 PJ_LOG(4, (THIS_FILE, "Error getting next packet size"));
355 continue;
356 }
357
358 while (packet_size) {
359
360 unsigned next_frame_size = 0;
361 DWORD cap_flag;
362 pj_int16_t *cap_buf = NULL;
363 unsigned nsamples = 0;
364
365 hr = strm->cap_client->GetBuffer((BYTE**)&cap_buf,
366 &next_frame_size,
367 &cap_flag,
368 NULL,
369 NULL);
370
371 if (FAILED(hr) || (next_frame_size == 0)) {
372 PJ_LOG(4, (THIS_FILE, "Error getting next buffer, \
373 next frame size : %d", next_frame_size));
374 packet_size = 0;
375 continue;
376 }
377
378 nsamples = (next_frame_size * strm->param.channel_count)
379 + strm->cap_buf_count;
380
381 if (nsamples >= strm->param.samples_per_frame) {
382 /* If buffer is not empty, combine the buffer with the just
383 * incoming samples, then call put_frame.
384 */
385 if (strm->cap_buf_count) {
386 unsigned chunk_count = 0;
387 pjmedia_frame frame;
388
389 chunk_count = strm->param.samples_per_frame -
390 strm->cap_buf_count;
391
392 pjmedia_copy_samples(strm->cap_buf +
393 strm->cap_buf_count,
394 (pj_int16_t*)cap_buf,
395 chunk_count);
396
397 if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
398 /* PCM mode */
399 /* Prepare frame */
400 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
401 frame.buf = (void *)strm->cap_buf;
402 frame.size = strm->bytes_per_frame;
403 frame.timestamp.u64 = strm->cap_timestamp.u64;
404 frame.bit_info = 0;
405 }
406
407 status = (*strm->cap_cb)(strm->user_data, &frame);
408 if (status == PJ_SUCCESS) {
409 /* Update position */
410 cap_buf = (pj_int16_t *)cap_buf + chunk_count;
411 nsamples -= strm->param.samples_per_frame;
412 strm->cap_timestamp.u64 +=
413 strm->param.samples_per_frame /
414 strm->param.channel_count;
415 }
416 }
417
418 /* Give all frame we have */
419 while (nsamples >= strm->param.samples_per_frame &&
420 status == PJ_SUCCESS)
421 {
422 pjmedia_frame frame;
423
424 pjmedia_copy_samples(strm->cap_buf,
425 (pj_int16_t*)cap_buf,
426 strm->param.samples_per_frame);
427
428 if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
429 /* PCM mode */
430 /* Prepare frame */
431 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
432 frame.buf = (void *)strm->cap_buf;
433 frame.size = strm->bytes_per_frame;
434 frame.timestamp.u64 = strm->cap_timestamp.u64;
435 frame.bit_info = 0;
436 }
437 status = (*strm->cap_cb)(strm->user_data, &frame);
438
439 if (status == PJ_SUCCESS) {
440 /* Update position */
441 cap_buf = (pj_int16_t *)cap_buf +
442 strm->param.samples_per_frame;
443
444 nsamples -= strm->param.samples_per_frame;
445 strm->cap_timestamp.u64 +=
446 strm->param.samples_per_frame /
447 strm->param.channel_count;
448 } else {
449 nsamples = 0;
450 }
451 }
452 strm->cap_buf_count = nsamples;
453
454 /* Store the remaining samples into the buffer */
455 if (nsamples && (status == PJ_SUCCESS)) {
456 pjmedia_copy_samples(strm->cap_buf,
457 (pj_int16_t*)cap_buf,
458 nsamples);
459 }
460 } else {
461 /* Not enough samples, let's just store them in the
462 * buffer.
463 */
464 pjmedia_copy_samples(strm->cap_buf + strm->cap_buf_count,
465 (pj_int16_t*)cap_buf,
466 next_frame_size *
467 strm->param.channel_count);
468
469 strm->cap_buf_count = nsamples;
470 }
471 hr = strm->cap_client->ReleaseBuffer(next_frame_size);
472
473 hr = strm->cap_client->GetNextPacketSize(&packet_size);
474 if (FAILED(hr)) {
475 PJ_LOG(4, (THIS_FILE, "Error getting next packet size"));
476 packet_size = 0;
477 }
478 }
479 }
480 }
481
482 PJ_LOG(5,(THIS_FILE, "WASAPI: thread stopping.."));
483 return 0;
484 }
485
activate_capture_dev(struct wasapi_stream * ws)486 static pj_status_t activate_capture_dev(struct wasapi_stream *ws)
487 {
488 HRESULT hr = E_FAIL;
489
490 #if defined(USE_ASYNC_ACTIVATE)
491 ComPtr<IActivateAudioInterfaceAsyncOperation> async_op;
492 ws->cap_id = MediaDevice::GetDefaultAudioCaptureId(
493 AudioDeviceRole::Communications);
494 #else
495 ws->cap_id = GetDefaultAudioCaptureId(AudioDeviceRole::Communications);
496 #endif
497
498 if (!ws->cap_id) {
499 PJ_LOG(4, (THIS_FILE, "Error getting default capture device id"));
500 return PJMEDIA_EAUD_SYSERR;
501 }
502
503 #if defined(USE_ASYNC_ACTIVATE)
504
505 ws->cap_aud_act = Make<AudioActivator>();
506 if (ws->cap_aud_act == NULL) {
507 PJ_LOG(4, (THIS_FILE, "Error activating capture device"));
508 return PJMEDIA_EAUD_SYSERR;
509 }
510 hr = ActivateAudioInterfaceAsync(ws->cap_id->Data(),
511 __uuidof(IAudioClient2),
512 NULL, ws->cap_aud_act.Get(),
513 &async_op);
514
515 //pj_thread_sleep(100);
516 auto task_completed = create_task(ws->cap_aud_act->task_completed);
517 task_completed.wait();
518 ws->default_cap_dev = task_completed.get().Get();
519 #else
520 hr = ActivateAudioInterface(ws->cap_id, __uuidof(IAudioClient2),
521 (void**)&ws->default_cap_dev);
522 #endif
523 AudioClientProperties properties = {};
524 if (SUCCEEDED(hr))
525 {
526 properties.cbSize = sizeof AudioClientProperties;
527 properties.eCategory = AudioCategory_Communications;
528 hr = ws->default_cap_dev->SetClientProperties(&properties);
529 }
530
531 return FAILED(hr) ? PJMEDIA_EAUD_SYSERR : PJ_SUCCESS;
532 }
533
534 /*
535 * Init capture device
536 */
init_capture_dev(struct wasapi_stream * ws,const pjmedia_aud_param * prm)537 static pj_status_t init_capture_dev(struct wasapi_stream *ws,
538 const pjmedia_aud_param *prm)
539 {
540 pj_status_t status;
541 HRESULT hr = E_FAIL;
542 REFERENCE_TIME request_duration = 0;
543 unsigned ptime;
544 unsigned int stream_flags;
545 WAVEFORMATEX wfx;
546 int test;
547
548 status = activate_capture_dev(ws);
549 if (status != PJ_SUCCESS) {
550 PJ_LOG(4, (THIS_FILE, "Failed activating capture device"));
551 goto on_exit;
552 }
553
554 stream_flags = 0x88140000;
555
556 pj_bzero(&wfx, sizeof(WAVEFORMATEX));
557 status = init_waveformatex(&wfx, prm, ws);
558
559 if (status != PJ_SUCCESS) {
560 PJ_LOG(4, (THIS_FILE, "Error initiating wave format"));
561 goto on_exit;
562 }
563 status = PJMEDIA_EAUD_SYSERR;
564
565 ptime = prm->samples_per_frame * 1000 /
566 (prm->clock_rate * prm->channel_count);
567
568 ws->bytes_per_frame = prm->samples_per_frame * ws->bytes_per_sample;
569
570 /* Initialize the stream to play at the minimum latency. */
571 hr = ws->default_cap_dev->GetDevicePeriod(NULL, &request_duration);
572
573 EXIT_ON_ERROR(hr);
574
575 test = ws->param.input_latency_ms * 10000;
576
577 hr = ws->default_cap_dev->Initialize(AUDCLNT_SHAREMODE_SHARED,
578 stream_flags,
579 //2000 * 10000,
580 ptime * ws->param.input_latency_ms *
581 10000,
582 0,
583 &wfx,
584 NULL);
585
586 EXIT_ON_ERROR(hr);
587
588 ws->cap_buf = (pj_int16_t *)pj_pool_zalloc(ws->pool, ws->bytes_per_frame);
589
590 if (!ws->cap_buf) {
591 PJ_LOG(4, (THIS_FILE, "Error creating capture buffer"));
592 status = PJ_ENOMEM;
593
594 goto on_exit;
595 }
596
597 ws->cap_event = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
598 if (!ws->cap_event) {
599 hr = HRESULT_FROM_WIN32(GetLastError());
600
601 goto on_exit;
602 }
603
604 EXIT_ON_ERROR(hr);
605
606 hr = ws->default_cap_dev->SetEventHandle(ws->cap_event);
607
608 EXIT_ON_ERROR(hr);
609
610 hr = ws->default_cap_dev->GetService(__uuidof(IAudioCaptureClient),
611 (void**)&ws->cap_client);
612
613 EXIT_ON_ERROR(hr);
614
615 PJ_LOG(4, (THIS_FILE,
616 "Wasapi Sound recorder initialized ("
617 "clock_rate=%d, latency=%d, "
618 "channel_count=%d, samples_per_frame=%d (%dms))",
619 prm->clock_rate, ws->param.input_latency_ms,
620 prm->channel_count, prm->samples_per_frame,
621 ptime));
622
623 status = PJ_SUCCESS;
624 on_exit:
625 if (status != PJ_SUCCESS)
626 wasapi_stream_destroy(&ws->base);
627
628 return status;
629 }
630
activate_playback_dev(struct wasapi_stream * ws)631 static pj_status_t activate_playback_dev(struct wasapi_stream *ws)
632 {
633 HRESULT hr = E_FAIL;
634
635 #if defined(USE_ASYNC_ACTIVATE)
636 ComPtr<IActivateAudioInterfaceAsyncOperation> async_op;
637
638 ws->pb_id = MediaDevice::GetDefaultAudioRenderId(
639 AudioDeviceRole::Communications);
640 #else
641 ws->pb_id = GetDefaultAudioRenderId(AudioDeviceRole::Communications);
642 #endif
643
644 if (!ws->pb_id) {
645 PJ_LOG(4, (THIS_FILE, "Error getting default playback device id"));
646 return PJMEDIA_EAUD_SYSERR;
647 }
648
649 #if defined(USE_ASYNC_ACTIVATE)
650 ws->pb_aud_act = Make<AudioActivator>();
651 if (ws->pb_aud_act == NULL) {
652 PJ_LOG(4, (THIS_FILE, "Error activating playback device"));
653 return PJMEDIA_EAUD_SYSERR;
654 }
655 hr = ActivateAudioInterfaceAsync(ws->pb_id->Data(),
656 __uuidof(IAudioClient2),
657 NULL, ws->pb_aud_act.Get(),
658 &async_op);
659
660 //pj_thread_sleep(100);
661 auto task_completed = create_task(ws->pb_aud_act->task_completed);
662 task_completed.wait();
663 ws->default_pb_dev = task_completed.get().Get();
664 #else
665 hr = ActivateAudioInterface(ws->pb_id, __uuidof(IAudioClient2),
666 (void**)&ws->default_pb_dev);
667 #endif
668
669 AudioClientProperties properties = {};
670 if (SUCCEEDED(hr))
671 {
672 properties.cbSize = sizeof AudioClientProperties;
673 properties.eCategory = AudioCategory_Communications;
674 hr = ws->default_pb_dev->SetClientProperties(&properties);
675 }
676
677 return FAILED(hr) ? PJMEDIA_EAUD_SYSERR : PJ_SUCCESS;
678 }
679
680 /*
681 * Init playback device
682 */
init_playback_dev(struct wasapi_stream * ws,const pjmedia_aud_param * prm)683 static pj_status_t init_playback_dev(struct wasapi_stream *ws,
684 const pjmedia_aud_param *prm)
685 {
686 HRESULT hr = E_FAIL;
687 pj_status_t status;
688 unsigned ptime;
689 unsigned int stream_flags;
690 WAVEFORMATEX wfx;
691
692 status = activate_playback_dev(ws);
693 if (status != PJ_SUCCESS) {
694 PJ_LOG(4, (THIS_FILE, "Failed activating playback device"));
695 goto on_exit;
696 }
697
698 stream_flags = 0x88140000;
699 pj_bzero(&wfx, sizeof(WAVEFORMATEX));
700
701 status = init_waveformatex(&wfx, prm, ws);
702 if (status != PJ_SUCCESS) {
703 PJ_LOG(4, (THIS_FILE, "Error initiating wave format"));
704
705 goto on_exit;
706 }
707 status = PJMEDIA_EAUD_SYSERR;
708
709 ptime = prm->samples_per_frame * 1000 /
710 (prm->clock_rate * prm->channel_count);
711
712 ws->bytes_per_frame = prm->samples_per_frame * ws->bytes_per_sample;
713
714 hr = ws->default_pb_dev->Initialize(AUDCLNT_SHAREMODE_SHARED,
715 stream_flags,
716 ws->param.output_latency_ms * 10000,
717 //0,
718 0,
719 &wfx,
720 NULL);
721
722 EXIT_ON_ERROR(hr);
723
724 hr = ws->default_pb_dev->GetBufferSize(&ws->pb_max_frame_count);
725
726 /* Create buffer */
727 EXIT_ON_ERROR(hr);
728
729 ws->pb_event = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
730 if (!ws->pb_event) {
731 hr = HRESULT_FROM_WIN32(GetLastError());
732
733 goto on_exit;
734 }
735
736 EXIT_ON_ERROR(hr);
737
738 hr = ws->default_pb_dev->SetEventHandle(ws->pb_event);
739
740 EXIT_ON_ERROR(hr);
741
742 hr = ws->default_pb_dev->GetService(__uuidof(IAudioRenderClient),
743 (void**)&ws->pb_client);
744
745 EXIT_ON_ERROR(hr);
746
747 /* Other/optional supported interfaces */
748 hr = ws->default_pb_dev->GetService(__uuidof(ISimpleAudioVolume),
749 (void**)&ws->pb_volume);
750
751 if (FAILED(hr)) {
752 PJ_LOG(4, (THIS_FILE, "Error getting vol service playback:0x%x", hr));
753 }
754
755 PJ_LOG(4, (THIS_FILE,
756 " Wasapi Sound player initialized ("
757 "clock_rate=%d, latency=%d, "
758 "channel_count=%d, samples_per_frame=%d (%dms))",
759 prm->clock_rate, ws->param.output_latency_ms,
760 prm->channel_count, prm->samples_per_frame,
761 ptime));
762
763 status = PJ_SUCCESS;
764 on_exit:
765 if (status != PJ_SUCCESS)
766 wasapi_stream_destroy(&ws->base);
767
768 return status;
769 }
770
771 /*
772 * wasapi - tests loads the audio units and sets up the driver structure
773 */
wasapi_add_dev(struct wasapi_factory * wf)774 static pj_status_t wasapi_add_dev(struct wasapi_factory *wf)
775 {
776 pjmedia_aud_dev_info *adi;
777 LPCWSTR capture_id = NULL;
778 LPCWSTR render_id = NULL;
779
780 if (wf->dev_count >= PJ_ARRAY_SIZE(wf->devs))
781 return PJ_ETOOMANY;
782
783 adi = &wf->devs[wf->dev_count];
784
785 /* Reset device info */
786 pj_bzero(adi, sizeof(*adi));
787
788 /* Set device name */
789 strcpy(adi->name, "default");
790 strcpy(adi->driver, "wasapi");
791
792 /* Get default capture device */
793 #if defined(USE_ASYNC_ACTIVATE)
794 capture_id = MediaDevice::GetDefaultAudioCaptureId(
795 AudioDeviceRole::Communications)->Data();
796 #else
797 capture_id = GetDefaultAudioCaptureId(AudioDeviceRole::Communications);
798 #endif
799
800 if (!capture_id) {
801 PJ_LOG(4, (THIS_FILE, "Failed to get default audio capture"));
802 }
803
804 /* Get default render device */
805 #if defined(USE_ASYNC_ACTIVATE)
806 render_id = MediaDevice::GetDefaultAudioRenderId(
807 AudioDeviceRole::Communications)->Data();
808 #else
809 render_id = GetDefaultAudioRenderId(AudioDeviceRole::Communications);
810 #endif
811
812 if (!render_id) {
813 PJ_LOG(4, (THIS_FILE, "Failed to get default audio render"));
814 }
815
816 if (!capture_id && !render_id) {
817 PJ_LOG(4, (THIS_FILE, "Unable to open default sound device"));
818 return PJMEDIA_EAUD_NODEV;
819 }
820
821 /* Check the number of capture channels */
822 adi->input_count = (capture_id) ? 1 : 0;
823
824 /* Check the number of playback channels */
825 adi->output_count = (render_id) ? 1 : 0;
826
827 /* Set the default sample rate */
828 adi->default_samples_per_sec = 8000;
829
830 adi->caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
831 PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY |
832 PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE;
833
834 adi->routes = PJMEDIA_AUD_DEV_ROUTE_DEFAULT |
835 PJMEDIA_AUD_DEV_ROUTE_EARPIECE |
836 PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER |
837 PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH;
838
839 ++wf->dev_count;
840
841 PJ_LOG(4, (THIS_FILE, "Added sound device %s", adi->name));
842
843 #if !defined(USE_ASYNC_ACTIVATE)
844 if (capture_id)
845 CoTaskMemFree((LPVOID*)capture_id);
846
847 if (render_id)
848 CoTaskMemFree((LPVOID*)render_id);
849 #endif
850
851 return PJ_SUCCESS;
852 }
853
854 /****************************************************************************
855 * Factory operations
856 */
857 /*
858 * Init wasapi audio driver.
859 */
860
861 #ifdef __cplusplus
862 extern "C"{
863 #endif
pjmedia_wasapi_factory(pj_pool_factory * pf)864 pjmedia_aud_dev_factory* pjmedia_wasapi_factory(pj_pool_factory *pf)
865 {
866 struct wasapi_factory *wf;
867 pj_pool_t *pool;
868
869 pool = pj_pool_create(pf, "WASAPI base", 256, 256, NULL);
870 wf = PJ_POOL_ZALLOC_T(pool, struct wasapi_factory);
871 wf->pf = pf;
872 wf->base_pool = pool;
873 wf->base.op = &factory_op;
874
875 return &wf->base;
876 }
877 #ifdef __cplusplus
878 }
879 #endif
880
881 /* API: init factory */
wasapi_factory_init(pjmedia_aud_dev_factory * f)882 static pj_status_t wasapi_factory_init(pjmedia_aud_dev_factory *f)
883 {
884 pj_status_t status;
885 status = wasapi_factory_refresh(f);
886 if (status != PJ_SUCCESS)
887 return status;
888
889 PJ_LOG(4,(THIS_FILE, "wasapi initialized"));
890 return PJ_SUCCESS;
891 }
892
893 /* API: destroy factory */
wasapi_factory_destroy(pjmedia_aud_dev_factory * f)894 static pj_status_t wasapi_factory_destroy(pjmedia_aud_dev_factory *f)
895 {
896 struct wasapi_factory *wf = (struct wasapi_factory*)f;
897
898 if (wf->pool) {
899 TRACE_((THIS_FILE, "wasapi_factory_destroy()"));
900 pj_pool_release(wf->pool);
901 }
902
903 return PJ_SUCCESS;
904 }
905
906 /* API: refresh the list of devices */
wasapi_factory_refresh(pjmedia_aud_dev_factory * f)907 static pj_status_t wasapi_factory_refresh(pjmedia_aud_dev_factory *f)
908 {
909 struct wasapi_factory *wf = (struct wasapi_factory*)f;
910 pj_status_t status;
911
912 TRACE_((THIS_FILE, "wasapi_factory_refresh()"));
913
914 if (wf->pool != NULL) {
915 pj_pool_release(wf->pool);
916 wf->pool = NULL;
917 }
918
919 wf->pool = pj_pool_create(wf->pf, "wasapi_aud", 256, 256, NULL);
920 wf->dev_count = 0;
921
922 status = wasapi_add_dev(wf);
923
924 PJ_LOG(4,(THIS_FILE, "wasapi driver found %d devices", wf->dev_count));
925
926 return status;
927 }
928
929 /* API: get number of devices */
wasapi_factory_get_dev_count(pjmedia_aud_dev_factory * f)930 static unsigned wasapi_factory_get_dev_count(pjmedia_aud_dev_factory *f)
931 {
932 struct wasapi_factory *wf = (struct wasapi_factory*)f;
933 return wf->dev_count;
934 }
935
936 /* API: get device info */
wasapi_factory_get_dev_info(pjmedia_aud_dev_factory * f,unsigned index,pjmedia_aud_dev_info * info)937 static pj_status_t wasapi_factory_get_dev_info(pjmedia_aud_dev_factory *f,
938 unsigned index,
939 pjmedia_aud_dev_info *info)
940 {
941 struct wasapi_factory *wf = (struct wasapi_factory*)f;
942
943 PJ_ASSERT_RETURN(index>=0 && index<wf->dev_count, PJ_EINVAL);
944
945 pj_memcpy(info, &wf->devs[index], sizeof(*info));
946
947 return PJ_SUCCESS;
948 }
949
950 /* API: create default device parameter */
wasapi_factory_default_param(pjmedia_aud_dev_factory * f,unsigned index,pjmedia_aud_param * param)951 static pj_status_t wasapi_factory_default_param(pjmedia_aud_dev_factory *f,
952 unsigned index,
953 pjmedia_aud_param *param)
954 {
955 struct wasapi_factory *wf = (struct wasapi_factory*)f;
956 struct pjmedia_aud_dev_info *di;
957
958 PJ_ASSERT_RETURN(index>=0 && index<wf->dev_count, PJ_EINVAL);
959
960 di = &wf->devs[index];
961
962 pj_bzero(param, sizeof(*param));
963 if (di->input_count && di->output_count) {
964 param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
965 param->rec_id = index;
966 param->play_id = index;
967 } else if (di->input_count) {
968 param->dir = PJMEDIA_DIR_CAPTURE;
969 param->rec_id = index;
970 param->play_id = PJMEDIA_AUD_INVALID_DEV;
971 } else if (di->output_count) {
972 param->dir = PJMEDIA_DIR_PLAYBACK;
973 param->play_id = index;
974 param->rec_id = PJMEDIA_AUD_INVALID_DEV;
975 } else {
976 return PJMEDIA_EAUD_INVDEV;
977 }
978
979 /* Set the mandatory settings here */
980 /* The values here are just some examples */
981 param->clock_rate = di->default_samples_per_sec;
982 param->channel_count = 2;
983 param->samples_per_frame = di->default_samples_per_sec * 20 / 1000;
984 param->bits_per_sample = 32;
985 param->flags = di->caps;
986 param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
987 param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
988
989 TRACE_((THIS_FILE, "wasapi_factory_default_param clock = %d flags = %d"
990 " spf = %d", param->clock_rate, param->flags,
991 param->samples_per_frame));
992
993 return PJ_SUCCESS;
994 }
995
996 /* API: create stream */
wasapi_factory_create_stream(pjmedia_aud_dev_factory * f,const pjmedia_aud_param * param,pjmedia_aud_rec_cb rec_cb,pjmedia_aud_play_cb play_cb,void * user_data,pjmedia_aud_stream ** p_strm)997 static pj_status_t wasapi_factory_create_stream(pjmedia_aud_dev_factory *f,
998 const pjmedia_aud_param *param,
999 pjmedia_aud_rec_cb rec_cb,
1000 pjmedia_aud_play_cb play_cb,
1001 void *user_data,
1002 pjmedia_aud_stream **p_strm)
1003 {
1004 struct wasapi_factory *wf = (struct wasapi_factory*)f;
1005 pj_pool_t *pool;
1006 struct wasapi_stream *strm;
1007 pj_status_t status;
1008
1009 /* Create and Initialize stream descriptor */
1010 pool = pj_pool_create(wf->pf, "wasapi-dev", 1024, 1024, NULL);
1011 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
1012
1013 strm = PJ_POOL_ZALLOC_T(pool, struct wasapi_stream);
1014 pj_memcpy(&strm->param, param, sizeof(*param));
1015 strm->base.op = &stream_op;
1016 strm->pool = pool;
1017 strm->cap_cb = rec_cb;
1018 strm->pb_cb = play_cb;
1019 strm->fmt_id = (pjmedia_format_id)param->ext_fmt.id;
1020 strm->user_data = user_data;
1021 strm->bytes_per_sample = param->bits_per_sample / 8;
1022
1023 /* Init capture */
1024 if (param->dir & PJMEDIA_DIR_CAPTURE) {
1025 status = init_capture_dev(strm, param);
1026 if (status != PJ_SUCCESS) {
1027 wasapi_stream_destroy(&strm->base);
1028 return status;
1029 }
1030 }
1031 /* Init playback */
1032 if (param->dir & PJMEDIA_DIR_PLAYBACK) {
1033 status = init_playback_dev(strm, param);
1034 if (status != PJ_SUCCESS) {
1035 wasapi_stream_destroy(&strm->base);
1036 return status;
1037 }
1038 }
1039
1040 strm->quit_event = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET,
1041 EVENT_ALL_ACCESS);
1042 if (!strm->quit_event)
1043 {
1044 PJ_LOG(4, (THIS_FILE, "Error creating quit event:0x%x",
1045 HRESULT_FROM_WIN32(GetLastError())));
1046
1047 return PJMEDIA_EAUD_SYSERR;
1048 }
1049
1050 /* Done */
1051 strm->base.op = &stream_op;
1052 *p_strm = &strm->base;
1053
1054 return PJ_SUCCESS;
1055 }
1056
1057 /* API: Get stream info. */
wasapi_stream_get_param(pjmedia_aud_stream * s,pjmedia_aud_param * pi)1058 static pj_status_t wasapi_stream_get_param(pjmedia_aud_stream *s,
1059 pjmedia_aud_param *pi)
1060 {
1061 struct wasapi_stream *strm = (struct wasapi_stream*)s;
1062
1063 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
1064
1065 pj_memcpy(pi, &strm->param, sizeof(*pi));
1066
1067 return PJ_SUCCESS;
1068 }
1069
1070 /* API: get capability */
wasapi_stream_get_cap(pjmedia_aud_stream * s,pjmedia_aud_dev_cap cap,void * pval)1071 static pj_status_t wasapi_stream_get_cap(pjmedia_aud_stream *s,
1072 pjmedia_aud_dev_cap cap,
1073 void *pval)
1074 {
1075 struct wasapi_stream *strm = (struct wasapi_stream*)s;
1076
1077 PJ_UNUSED_ARG(strm);
1078
1079 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1080
1081 if (cap == PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
1082 (strm->param.dir & PJMEDIA_DIR_CAPTURE))
1083 {
1084 /* Recording latency */
1085 *(unsigned*)pval = strm->param.input_latency_ms;
1086 return PJ_SUCCESS;
1087
1088 } else if (cap == PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
1089 (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
1090 {
1091 /* Playback latency */
1092 *(unsigned*)pval = strm->param.output_latency_ms;
1093 return PJ_SUCCESS;
1094
1095 } else if (cap == PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING)
1096 {
1097 /* Output volume setting */
1098 *(unsigned*)pval = 0; // retrieve output device's volume here
1099 return PJ_SUCCESS;
1100
1101 } else {
1102 return PJMEDIA_EAUD_INVCAP;
1103 }
1104 }
1105
1106 /* API: set capability */
wasapi_stream_set_cap(pjmedia_aud_stream * s,pjmedia_aud_dev_cap cap,const void * pval)1107 static pj_status_t wasapi_stream_set_cap(pjmedia_aud_stream *s,
1108 pjmedia_aud_dev_cap cap,
1109 const void *pval)
1110 {
1111 struct wasapi_stream *strm = (struct wasapi_stream*)s;
1112
1113 PJ_UNUSED_ARG(strm);
1114 PJ_UNUSED_ARG(cap);
1115
1116 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1117
1118 return PJMEDIA_EAUD_INVCAP;
1119 }
1120
1121 /* API: Start stream. */
wasapi_stream_start(pjmedia_aud_stream * strm)1122 static pj_status_t wasapi_stream_start(pjmedia_aud_stream *strm)
1123 {
1124 struct wasapi_stream *ws = (struct wasapi_stream*)strm;
1125 HRESULT hr = E_FAIL;
1126 pj_status_t status = PJ_SUCCESS;
1127
1128 PJ_ASSERT_RETURN(ws != NULL, PJ_EINVAL);
1129
1130 TRACE_((THIS_FILE, "Starting wasapi audio stream"));
1131
1132 if (ws->default_pb_dev) {
1133 hr = ws->default_pb_dev->Start();
1134 EXIT_ON_ERROR(hr);
1135 }
1136
1137 if (ws->default_cap_dev) {
1138 hr = ws->default_cap_dev->Start();
1139 EXIT_ON_ERROR(hr);
1140 }
1141
1142 /* Create and start the thread */
1143 if (!ws->thread) {
1144 status = pj_thread_create(ws->pool, "wasapi", &wasapi_dev_thread, strm,
1145 0, 0, &ws->thread);
1146
1147 if (status != PJ_SUCCESS) {
1148 PJ_LOG(4, (THIS_FILE, "Error creating wasapi thread:%d", status));
1149 }
1150 }
1151
1152 on_exit:
1153 if (status != PJ_SUCCESS) {
1154 PJ_LOG(4, (THIS_FILE, "Error starting wasapi:0x%x", hr));
1155 wasapi_stream_stop(&ws->base);
1156 }
1157
1158 return status;
1159 }
1160
1161 /* API: Stop stream. */
wasapi_stream_stop(pjmedia_aud_stream * strm)1162 static pj_status_t wasapi_stream_stop(pjmedia_aud_stream *strm)
1163 {
1164 struct wasapi_stream *ws = (struct wasapi_stream*)strm;
1165 HRESULT hr, hr_tmp;
1166 hr = hr_tmp = S_OK;
1167
1168 PJ_ASSERT_RETURN(ws != NULL, PJ_EINVAL);
1169
1170 if (ws->default_pb_dev) {
1171 hr = ws->default_pb_dev->Stop();
1172 if (FAILED(hr)) {
1173 PJ_LOG(4, (THIS_FILE, "Error stopping wasapi playback stream:0x%x",
1174 hr));
1175 } else {
1176 PJ_LOG(4,(THIS_FILE, "Stopped wasapi playback stream"));
1177 }
1178 }
1179
1180 if (ws->default_cap_dev) {
1181 hr_tmp = ws->default_cap_dev->Stop();
1182 if (FAILED(hr_tmp)) {
1183 hr = hr_tmp;
1184 PJ_LOG(4, (THIS_FILE, "Error stopping wasapi capture stream:0x%x",
1185 hr));
1186 } else {
1187 PJ_LOG(4,(THIS_FILE, "Stopped wasapi capture stream"));
1188 }
1189 }
1190
1191 if (FAILED(hr)) {
1192 return PJMEDIA_EAUD_WASAPI_ERROR;
1193 }
1194
1195 return PJ_SUCCESS;
1196 }
1197
1198 /* API: Destroy stream. */
wasapi_stream_destroy(pjmedia_aud_stream * strm)1199 static pj_status_t wasapi_stream_destroy(pjmedia_aud_stream *strm)
1200 {
1201 struct wasapi_stream *ws = (struct wasapi_stream*)strm;
1202
1203 PJ_ASSERT_RETURN(ws != NULL, PJ_EINVAL);
1204
1205 wasapi_stream_stop(strm);
1206
1207 /* Stop stream thread */
1208 if (ws->thread) {
1209 SetEvent(ws->quit_event);
1210 pj_thread_join(ws->thread);
1211 pj_thread_destroy(ws->thread);
1212 ws->thread = NULL;
1213 }
1214
1215 /* Close thread quit event */
1216 if (ws->quit_event) {
1217 CloseHandle(ws->quit_event);
1218 ws->quit_event = NULL;
1219 }
1220
1221 /* Close playback event */
1222 if (ws->pb_event) {
1223 CloseHandle(ws->pb_event);
1224 ws->pb_event = NULL;
1225 }
1226
1227 /* Close capture event */
1228 if (ws->cap_event) {
1229 CloseHandle(ws->cap_event);
1230 ws->cap_event = NULL;
1231 }
1232
1233 /* Release playback device */
1234 if (ws->default_pb_dev) {
1235 ws->default_pb_dev->Release();
1236 ws->default_pb_dev = NULL;
1237 }
1238
1239 /* Release capture device */
1240 if (ws->default_cap_dev) {
1241 ws->default_cap_dev->Release();
1242 ws->default_cap_dev = NULL;
1243 }
1244
1245 /* Release playback volume interface */
1246 if (ws->pb_volume) {
1247 ws->pb_volume->Release();
1248 ws->pb_volume = NULL;
1249 }
1250
1251
1252 #if defined(USE_ASYNC_ACTIVATE)
1253 /* Release audio activator */
1254 if (ws->cap_aud_act) {
1255 ws->cap_aud_act = nullptr;
1256 }
1257
1258 if (ws->pb_aud_act) {
1259 ws->pb_aud_act = nullptr;
1260 }
1261
1262 if (ws->cap_id) {
1263 ws->cap_id = nullptr;
1264 }
1265
1266 if (ws->pb_id) {
1267 ws->pb_id = nullptr;
1268 }
1269 #else
1270 if (ws->cap_id) {
1271 CoTaskMemFree((LPVOID)ws->cap_id);
1272 }
1273
1274 if (ws->pb_id) {
1275 CoTaskMemFree((LPVOID)ws->pb_id);
1276 }
1277 #endif
1278
1279 pj_pool_release(ws->pool);
1280
1281 return PJ_SUCCESS;
1282 }
1283
1284 #if defined(USE_ASYNC_ACTIVATE)
ActivateCompleted(IActivateAudioInterfaceAsyncOperation * pAsyncOp)1285 HRESULT AudioActivator::ActivateCompleted(
1286 IActivateAudioInterfaceAsyncOperation *pAsyncOp)
1287 {
1288 HRESULT hr = S_OK, hr2 = S_OK;
1289 IUnknown *aud_interface = NULL;
1290 IAudioClient2 *aud_client = NULL;
1291
1292 hr = pAsyncOp->GetActivateResult(&hr2, &aud_interface);
1293 if (SUCCEEDED(hr) && SUCCEEDED(hr2)) {
1294 aud_interface->QueryInterface(IID_PPV_ARGS(&aud_client));
1295 if (aud_client)
1296 {
1297 pj_thread_desc thread_desc;
1298 pj_thread_t *act_thread;
1299 pj_bzero(thread_desc, sizeof(pj_thread_desc));
1300
1301 if (!pj_thread_is_registered()) {
1302 pj_thread_register("activator", thread_desc, &act_thread);
1303 }
1304 }
1305 }
1306
1307 task_completed.set(aud_client);
1308 return hr;
1309 }
1310
1311 #endif
1312
1313 #endif /* PJMEDIA_AUDIO_DEV_HAS_WASAPI */
1314