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