Line data Source code
1 : /*
2 : * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 : *
4 : * Use of this source code is governed by a BSD-style license
5 : * that can be found in the LICENSE file in the root of the source
6 : * tree. An additional intellectual property rights grant can be found
7 : * in the file PATENTS. All contributing project authors may
8 : * be found in the AUTHORS file in the root of the source tree.
9 : */
10 :
11 : #include <assert.h>
12 :
13 : #include "webrtc/modules/audio_device/linux/audio_mixer_manager_pulse_linux.h"
14 : #include "webrtc/system_wrappers/include/trace.h"
15 : #include "webrtc/base/checks.h"
16 :
17 : extern webrtc_adm_linux_pulse::PulseAudioSymbolTable PaSymbolTable;
18 :
19 : // Accesses Pulse functions through our late-binding symbol table instead of
20 : // directly. This way we don't have to link to libpulse, which means our
21 : // binary will work on systems that don't have it.
22 : #define LATE(sym) \
23 : LATESYM_GET(webrtc_adm_linux_pulse::PulseAudioSymbolTable, \
24 : &PaSymbolTable, sym)
25 :
26 : namespace webrtc
27 : {
28 :
29 : class AutoPulseLock {
30 : public:
31 0 : explicit AutoPulseLock(pa_threaded_mainloop* pa_mainloop)
32 0 : : pa_mainloop_(pa_mainloop) {
33 0 : LATE(pa_threaded_mainloop_lock)(pa_mainloop_);
34 0 : }
35 :
36 0 : ~AutoPulseLock() {
37 0 : LATE(pa_threaded_mainloop_unlock)(pa_mainloop_);
38 0 : }
39 :
40 : private:
41 : pa_threaded_mainloop* const pa_mainloop_;
42 : };
43 :
44 0 : AudioMixerManagerLinuxPulse::AudioMixerManagerLinuxPulse(const int32_t id) :
45 : _id(id),
46 : _paOutputDeviceIndex(-1),
47 : _paInputDeviceIndex(-1),
48 : _paPlayStream(NULL),
49 : _paRecStream(NULL),
50 : _paMainloop(NULL),
51 : _paContext(NULL),
52 : _paVolume(0),
53 : _paMute(0),
54 : _paVolSteps(0),
55 : _paSpeakerMute(false),
56 : _paSpeakerVolume(PA_VOLUME_NORM),
57 : _paChannels(0),
58 0 : _paObjectsSet(false)
59 : {
60 : WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id,
61 : "%s constructed", __FUNCTION__);
62 0 : }
63 :
64 0 : AudioMixerManagerLinuxPulse::~AudioMixerManagerLinuxPulse()
65 : {
66 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
67 : WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id,
68 : "%s destructed", __FUNCTION__);
69 :
70 0 : Close();
71 0 : }
72 :
73 : // ===========================================================================
74 : // PUBLIC METHODS
75 : // ===========================================================================
76 :
77 0 : int32_t AudioMixerManagerLinuxPulse::SetPulseAudioObjects(
78 : pa_threaded_mainloop* mainloop,
79 : pa_context* context)
80 : {
81 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
82 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s",
83 : __FUNCTION__);
84 :
85 0 : if (!mainloop || !context)
86 : {
87 : WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
88 : " could not set PulseAudio objects for mixer");
89 0 : return -1;
90 : }
91 :
92 0 : _paMainloop = mainloop;
93 0 : _paContext = context;
94 0 : _paObjectsSet = true;
95 :
96 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
97 : " the PulseAudio objects for the mixer has been set");
98 :
99 0 : return 0;
100 : }
101 :
102 0 : int32_t AudioMixerManagerLinuxPulse::Close()
103 : {
104 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
105 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s",
106 : __FUNCTION__);
107 :
108 0 : CloseSpeaker();
109 0 : CloseMicrophone();
110 :
111 0 : _paMainloop = NULL;
112 0 : _paContext = NULL;
113 0 : _paObjectsSet = false;
114 :
115 0 : return 0;
116 :
117 : }
118 :
119 0 : int32_t AudioMixerManagerLinuxPulse::CloseSpeaker()
120 : {
121 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
122 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s",
123 : __FUNCTION__);
124 :
125 : // Reset the index to -1
126 0 : _paOutputDeviceIndex = -1;
127 0 : _paPlayStream = NULL;
128 :
129 0 : return 0;
130 : }
131 :
132 0 : int32_t AudioMixerManagerLinuxPulse::CloseMicrophone()
133 : {
134 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
135 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s",
136 : __FUNCTION__);
137 :
138 : // Reset the index to -1
139 0 : _paInputDeviceIndex = -1;
140 0 : _paRecStream = NULL;
141 :
142 0 : return 0;
143 : }
144 :
145 0 : int32_t AudioMixerManagerLinuxPulse::SetPlayStream(pa_stream* playStream)
146 : {
147 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
148 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
149 : "AudioMixerManagerLinuxPulse::SetPlayStream(playStream)");
150 :
151 0 : _paPlayStream = playStream;
152 0 : return 0;
153 : }
154 :
155 0 : int32_t AudioMixerManagerLinuxPulse::SetRecStream(pa_stream* recStream)
156 : {
157 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
158 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
159 : "AudioMixerManagerLinuxPulse::SetRecStream(recStream)");
160 :
161 0 : _paRecStream = recStream;
162 0 : return 0;
163 : }
164 :
165 0 : int32_t AudioMixerManagerLinuxPulse::OpenSpeaker(
166 : uint16_t deviceIndex)
167 : {
168 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
169 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
170 : "AudioMixerManagerLinuxPulse::OpenSpeaker(deviceIndex=%d)",
171 : deviceIndex);
172 :
173 : // No point in opening the speaker
174 : // if PA objects have not been set
175 0 : if (!_paObjectsSet)
176 : {
177 : WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
178 : " PulseAudio objects has not been set");
179 0 : return -1;
180 : }
181 :
182 : // Set the index for the PulseAudio
183 : // output device to control
184 0 : _paOutputDeviceIndex = deviceIndex;
185 :
186 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
187 : " the output mixer device is now open");
188 :
189 0 : return 0;
190 : }
191 :
192 0 : int32_t AudioMixerManagerLinuxPulse::OpenMicrophone(
193 : uint16_t deviceIndex)
194 : {
195 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
196 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
197 : "AudioMixerManagerLinuxPulse::OpenMicrophone"
198 : "(deviceIndex=%d)", deviceIndex);
199 :
200 : // No point in opening the microphone
201 : // if PA objects have not been set
202 0 : if (!_paObjectsSet)
203 : {
204 : WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
205 : " PulseAudio objects have not been set");
206 0 : return -1;
207 : }
208 :
209 : // Set the index for the PulseAudio
210 : // input device to control
211 0 : _paInputDeviceIndex = deviceIndex;
212 :
213 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
214 : " the input mixer device is now open");
215 :
216 0 : return 0;
217 : }
218 :
219 0 : bool AudioMixerManagerLinuxPulse::SpeakerIsInitialized() const
220 : {
221 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
222 : WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s",
223 : __FUNCTION__);
224 :
225 0 : return (_paOutputDeviceIndex != -1);
226 : }
227 :
228 0 : bool AudioMixerManagerLinuxPulse::MicrophoneIsInitialized() const
229 : {
230 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
231 : WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s",
232 : __FUNCTION__);
233 :
234 0 : return (_paInputDeviceIndex != -1);
235 : }
236 :
237 0 : int32_t AudioMixerManagerLinuxPulse::SetSpeakerVolume(
238 : uint32_t volume)
239 : {
240 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
241 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
242 : "AudioMixerManagerLinuxPulse::SetSpeakerVolume(volume=%u)",
243 : volume);
244 :
245 0 : if (_paOutputDeviceIndex == -1)
246 : {
247 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
248 : " output device index has not been set");
249 0 : return -1;
250 : }
251 :
252 0 : bool setFailed(false);
253 :
254 0 : if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream)
255 : != PA_STREAM_UNCONNECTED))
256 : {
257 : // We can only really set the volume if we have a connected stream
258 0 : AutoPulseLock auto_lock(_paMainloop);
259 :
260 : // Get the number of channels from the sample specification
261 : const pa_sample_spec *spec =
262 0 : LATE(pa_stream_get_sample_spec)(_paPlayStream);
263 0 : if (!spec)
264 : {
265 : WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
266 : " could not get sample specification");
267 0 : return -1;
268 : }
269 :
270 : // Set the same volume for all channels
271 : pa_cvolume cVolumes;
272 0 : LATE(pa_cvolume_set)(&cVolumes, spec->channels, volume);
273 :
274 0 : pa_operation* paOperation = NULL;
275 0 : paOperation = LATE(pa_context_set_sink_input_volume)(
276 : _paContext,
277 0 : LATE(pa_stream_get_index)(_paPlayStream),
278 : &cVolumes,
279 0 : PaSetVolumeCallback, NULL);
280 0 : if (!paOperation)
281 : {
282 0 : setFailed = true;
283 : }
284 :
285 : // Don't need to wait for the completion
286 0 : LATE(pa_operation_unref)(paOperation);
287 : } else
288 : {
289 : // We have not created a stream or it's not connected to the sink
290 : // Save the volume to be set at connection
291 0 : _paSpeakerVolume = volume;
292 : }
293 :
294 0 : if (setFailed)
295 : {
296 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
297 : " could not set speaker volume, error%d",
298 : LATE(pa_context_errno)(_paContext));
299 :
300 0 : return -1;
301 : }
302 :
303 0 : return 0;
304 : }
305 :
306 : int32_t
307 0 : AudioMixerManagerLinuxPulse::SpeakerVolume(uint32_t& volume) const
308 : {
309 0 : if (_paOutputDeviceIndex == -1)
310 : {
311 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
312 : " output device index has not been set");
313 0 : return -1;
314 : }
315 :
316 0 : if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream)
317 : != PA_STREAM_UNCONNECTED))
318 : {
319 : // We can only get the volume if we have a connected stream
320 0 : if (!GetSinkInputInfo())
321 0 : return -1;
322 :
323 0 : AutoPulseLock auto_lock(_paMainloop);
324 0 : volume = static_cast<uint32_t> (_paVolume);
325 : } else
326 : {
327 0 : AutoPulseLock auto_lock(_paMainloop);
328 0 : volume = _paSpeakerVolume;
329 : }
330 :
331 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
332 : "\tAudioMixerManagerLinuxPulse::SpeakerVolume() => vol=%i",
333 : volume);
334 :
335 0 : return 0;
336 : }
337 :
338 : int32_t
339 0 : AudioMixerManagerLinuxPulse::MaxSpeakerVolume(uint32_t& maxVolume) const
340 : {
341 :
342 0 : if (_paOutputDeviceIndex == -1)
343 : {
344 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
345 : " output device index has not been set");
346 0 : return -1;
347 : }
348 :
349 : // PA_VOLUME_NORM corresponds to 100% (0db)
350 : // but PA allows up to 150 db amplification
351 0 : maxVolume = static_cast<uint32_t> (PA_VOLUME_NORM);
352 :
353 0 : return 0;
354 : }
355 :
356 : int32_t
357 0 : AudioMixerManagerLinuxPulse::MinSpeakerVolume(uint32_t& minVolume) const
358 : {
359 :
360 0 : if (_paOutputDeviceIndex == -1)
361 : {
362 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
363 : " output device index has not been set");
364 0 : return -1;
365 : }
366 :
367 0 : minVolume = static_cast<uint32_t> (PA_VOLUME_MUTED);
368 :
369 0 : return 0;
370 : }
371 :
372 : int32_t
373 0 : AudioMixerManagerLinuxPulse::SpeakerVolumeStepSize(uint16_t& stepSize) const
374 : {
375 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
376 0 : if (_paOutputDeviceIndex == -1)
377 : {
378 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
379 : " output device index has not been set");
380 0 : return -1;
381 : }
382 :
383 : // The sink input (stream) will always have step size = 1
384 : // There are PA_VOLUME_NORM+1 steps
385 0 : stepSize = 1;
386 :
387 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
388 : "\tAudioMixerManagerLinuxPulse::SpeakerVolumeStepSize() => "
389 : "size=%i", stepSize);
390 :
391 0 : return 0;
392 : }
393 :
394 : int32_t
395 0 : AudioMixerManagerLinuxPulse::SpeakerVolumeIsAvailable(bool& available)
396 : {
397 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
398 0 : if (_paOutputDeviceIndex == -1)
399 : {
400 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
401 : " output device index has not been set");
402 0 : return -1;
403 : }
404 :
405 : // Always available in Pulse Audio
406 0 : available = true;
407 :
408 0 : return 0;
409 : }
410 :
411 : int32_t
412 0 : AudioMixerManagerLinuxPulse::SpeakerMuteIsAvailable(bool& available)
413 : {
414 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
415 0 : if (_paOutputDeviceIndex == -1)
416 : {
417 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
418 : " output device index has not been set");
419 0 : return -1;
420 : }
421 :
422 : // Always available in Pulse Audio
423 0 : available = true;
424 :
425 0 : return 0;
426 : }
427 :
428 0 : int32_t AudioMixerManagerLinuxPulse::SetSpeakerMute(bool enable)
429 : {
430 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
431 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
432 : "AudioMixerManagerLinuxPulse::SetSpeakerMute(enable=%u)",
433 : enable);
434 :
435 0 : if (_paOutputDeviceIndex == -1)
436 : {
437 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
438 : " output device index has not been set");
439 0 : return -1;
440 : }
441 :
442 0 : bool setFailed(false);
443 :
444 0 : if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream)
445 : != PA_STREAM_UNCONNECTED))
446 : {
447 : // We can only really mute if we have a connected stream
448 0 : AutoPulseLock auto_lock(_paMainloop);
449 :
450 0 : pa_operation* paOperation = NULL;
451 0 : paOperation = LATE(pa_context_set_sink_input_mute)(
452 : _paContext,
453 0 : LATE(pa_stream_get_index)(_paPlayStream),
454 : (int) enable,
455 : PaSetVolumeCallback,
456 0 : NULL);
457 0 : if (!paOperation)
458 : {
459 0 : setFailed = true;
460 : }
461 :
462 : // Don't need to wait for the completion
463 0 : LATE(pa_operation_unref)(paOperation);
464 : } else
465 : {
466 : // We have not created a stream or it's not connected to the sink
467 : // Save the mute status to be set at connection
468 0 : _paSpeakerMute = enable;
469 : }
470 :
471 0 : if (setFailed)
472 : {
473 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
474 : " could not mute speaker, error%d",
475 : LATE(pa_context_errno)(_paContext));
476 0 : return -1;
477 : }
478 :
479 0 : return 0;
480 : }
481 :
482 0 : int32_t AudioMixerManagerLinuxPulse::SpeakerMute(bool& enabled) const
483 : {
484 :
485 0 : if (_paOutputDeviceIndex == -1)
486 : {
487 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
488 : " output device index has not been set");
489 0 : return -1;
490 : }
491 :
492 0 : if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream)
493 : != PA_STREAM_UNCONNECTED))
494 : {
495 : // We can only get the mute status if we have a connected stream
496 0 : if (!GetSinkInputInfo())
497 0 : return -1;
498 :
499 0 : enabled = static_cast<bool> (_paMute);
500 : } else
501 : {
502 0 : enabled = _paSpeakerMute;
503 : }
504 :
505 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
506 : " AudioMixerManagerLinuxPulse::SpeakerMute() => "
507 : "enabled=%i, enabled");
508 :
509 0 : return 0;
510 : }
511 :
512 : int32_t
513 0 : AudioMixerManagerLinuxPulse::StereoPlayoutIsAvailable(bool& available)
514 : {
515 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
516 0 : if (_paOutputDeviceIndex == -1)
517 : {
518 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
519 : " output device index has not been set");
520 0 : return -1;
521 : }
522 :
523 0 : uint32_t deviceIndex = (uint32_t) _paOutputDeviceIndex;
524 :
525 : {
526 0 : AutoPulseLock auto_lock(_paMainloop);
527 :
528 : // Get the actual stream device index if we have a connected stream
529 : // The device used by the stream can be changed
530 : // during the call
531 0 : if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream)
532 : != PA_STREAM_UNCONNECTED))
533 : {
534 0 : deviceIndex = LATE(pa_stream_get_device_index)(_paPlayStream);
535 : }
536 : }
537 :
538 0 : if (!GetSinkInfoByIndex(deviceIndex))
539 0 : return -1;
540 :
541 0 : available = static_cast<bool> (_paChannels == 2);
542 :
543 0 : return 0;
544 : }
545 :
546 : int32_t
547 0 : AudioMixerManagerLinuxPulse::StereoRecordingIsAvailable(bool& available)
548 : {
549 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
550 0 : if (_paInputDeviceIndex == -1)
551 : {
552 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
553 : " input device index has not been set");
554 0 : return -1;
555 : }
556 :
557 0 : uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex;
558 :
559 0 : AutoPulseLock auto_lock(_paMainloop);
560 :
561 : // Get the actual stream device index if we have a connected stream
562 : // The device used by the stream can be changed
563 : // during the call
564 0 : if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream)
565 : != PA_STREAM_UNCONNECTED))
566 : {
567 0 : deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
568 : }
569 :
570 0 : pa_operation* paOperation = NULL;
571 :
572 : // Get info for this source
573 : // We want to know if the actual device can record in stereo
574 0 : paOperation = LATE(pa_context_get_source_info_by_index)(
575 : _paContext, deviceIndex,
576 : PaSourceInfoCallback,
577 0 : (void*) this);
578 :
579 0 : WaitForOperationCompletion(paOperation);
580 :
581 0 : available = static_cast<bool> (_paChannels == 2);
582 :
583 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
584 : " AudioMixerManagerLinuxPulse::StereoRecordingIsAvailable()"
585 : " => available=%i, available");
586 :
587 0 : return 0;
588 : }
589 :
590 0 : int32_t AudioMixerManagerLinuxPulse::MicrophoneMuteIsAvailable(
591 : bool& available)
592 : {
593 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
594 0 : if (_paInputDeviceIndex == -1)
595 : {
596 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
597 : " input device index has not been set");
598 0 : return -1;
599 : }
600 :
601 : // Always available in Pulse Audio
602 0 : available = true;
603 :
604 0 : return 0;
605 : }
606 :
607 0 : int32_t AudioMixerManagerLinuxPulse::SetMicrophoneMute(bool enable)
608 : {
609 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
610 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
611 : "AudioMixerManagerLinuxPulse::SetMicrophoneMute(enable=%u)",
612 : enable);
613 :
614 0 : if (_paInputDeviceIndex == -1)
615 : {
616 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
617 : " input device index has not been set");
618 0 : return -1;
619 : }
620 :
621 0 : bool setFailed(false);
622 0 : pa_operation* paOperation = NULL;
623 :
624 0 : uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex;
625 :
626 0 : AutoPulseLock auto_lock(_paMainloop);
627 :
628 : // Get the actual stream device index if we have a connected stream
629 : // The device used by the stream can be changed
630 : // during the call
631 0 : if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream)
632 : != PA_STREAM_UNCONNECTED))
633 : {
634 0 : deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
635 : }
636 :
637 : // Set mute switch for the source
638 0 : paOperation = LATE(pa_context_set_source_mute_by_index)(
639 : _paContext, deviceIndex,
640 : enable,
641 0 : PaSetVolumeCallback, NULL);
642 :
643 0 : if (!paOperation)
644 : {
645 0 : setFailed = true;
646 : }
647 :
648 : // Don't need to wait for this to complete.
649 0 : LATE(pa_operation_unref)(paOperation);
650 :
651 0 : if (setFailed)
652 : {
653 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
654 : " could not mute microphone, error%d",
655 : LATE(pa_context_errno)(_paContext));
656 0 : return -1;
657 : }
658 :
659 0 : return 0;
660 : }
661 :
662 0 : int32_t AudioMixerManagerLinuxPulse::MicrophoneMute(bool& enabled) const
663 : {
664 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
665 0 : if (_paInputDeviceIndex == -1)
666 : {
667 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
668 : " input device index has not been set");
669 0 : return -1;
670 : }
671 :
672 0 : uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex;
673 :
674 : {
675 0 : AutoPulseLock auto_lock(_paMainloop);
676 : // Get the actual stream device index if we have a connected stream
677 : // The device used by the stream can be changed
678 : // during the call
679 0 : if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream)
680 : != PA_STREAM_UNCONNECTED))
681 : {
682 0 : deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
683 : }
684 : }
685 :
686 0 : if (!GetSourceInfoByIndex(deviceIndex))
687 0 : return -1;
688 :
689 0 : enabled = static_cast<bool> (_paMute);
690 :
691 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
692 : "\tAudioMixerManagerLinuxPulse::MicrophoneMute() =>"
693 : " enabled=%i", enabled);
694 :
695 0 : return 0;
696 : }
697 :
698 : int32_t
699 0 : AudioMixerManagerLinuxPulse::MicrophoneBoostIsAvailable(bool& available)
700 : {
701 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
702 0 : if (_paInputDeviceIndex == -1)
703 : {
704 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
705 : " input device index has not been set");
706 0 : return -1;
707 : }
708 :
709 : // Always unavailable in Pulse Audio
710 : // Could make it possible to use PA_VOLUME_MAX
711 : // but that gives bad audio with some sound cards
712 0 : available = false;
713 :
714 0 : return 0;
715 : }
716 :
717 0 : int32_t AudioMixerManagerLinuxPulse::SetMicrophoneBoost(bool enable)
718 : {
719 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
720 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
721 : "AudioMixerManagerLinuxPulse::SetMicrophoneBoost(enable=%u)",
722 : enable);
723 :
724 0 : if (_paInputDeviceIndex == -1)
725 : {
726 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
727 : " input device index has not been set");
728 0 : return -1;
729 : }
730 :
731 : // Ensure the selected microphone destination has a valid boost control
732 0 : bool available(false);
733 0 : MicrophoneBoostIsAvailable(available);
734 0 : if (!available)
735 : {
736 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
737 : " it is not possible to enable microphone boost");
738 0 : return -1;
739 : }
740 :
741 : // It is assumed that the call above fails!
742 :
743 0 : return 0;
744 : }
745 :
746 0 : int32_t AudioMixerManagerLinuxPulse::MicrophoneBoost(bool& enabled) const
747 : {
748 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
749 0 : if (_paInputDeviceIndex == -1)
750 : {
751 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
752 : " input device index has not been set");
753 0 : return -1;
754 : }
755 :
756 : // Microphone boost cannot be enabled on this platform!
757 0 : enabled = false;
758 :
759 0 : return 0;
760 : }
761 :
762 0 : int32_t AudioMixerManagerLinuxPulse::MicrophoneVolumeIsAvailable(
763 : bool& available)
764 : {
765 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
766 0 : if (_paInputDeviceIndex == -1)
767 : {
768 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
769 : " input device index has not been set");
770 0 : return -1;
771 : }
772 :
773 : // Always available in Pulse Audio
774 0 : available = true;
775 :
776 0 : return 0;
777 : }
778 :
779 : int32_t
780 0 : AudioMixerManagerLinuxPulse::SetMicrophoneVolume(uint32_t volume)
781 : {
782 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
783 : "AudioMixerManagerLinuxPulse::SetMicrophoneVolume"
784 : "(volume=%u)", volume);
785 :
786 0 : if (_paInputDeviceIndex == -1)
787 : {
788 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
789 : " input device index has not been set");
790 0 : return -1;
791 : }
792 :
793 : // Unlike output streams, input streams have no concept of a stream
794 : // volume, only a device volume. So we have to change the volume of the
795 : // device itself.
796 :
797 : // The device may have a different number of channels than the stream and
798 : // their mapping may be different, so we don't want to use the channel
799 : // count from our sample spec. We could use PA_CHANNELS_MAX to cover our
800 : // bases, and the server allows that even if the device's channel count
801 : // is lower, but some buggy PA clients don't like that (the pavucontrol
802 : // on Hardy dies in an assert if the channel count is different). So
803 : // instead we look up the actual number of channels that the device has.
804 0 : AutoPulseLock auto_lock(_paMainloop);
805 0 : uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex;
806 :
807 : // Get the actual stream device index if we have a connected stream
808 : // The device used by the stream can be changed
809 : // during the call
810 0 : if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream)
811 : != PA_STREAM_UNCONNECTED))
812 : {
813 0 : deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
814 : }
815 :
816 0 : bool setFailed(false);
817 0 : pa_operation* paOperation = NULL;
818 :
819 : // Get the number of channels for this source
820 : paOperation
821 0 : = LATE(pa_context_get_source_info_by_index)(_paContext, deviceIndex,
822 : PaSourceInfoCallback,
823 0 : (void*) this);
824 :
825 0 : WaitForOperationCompletion(paOperation);
826 :
827 0 : uint8_t channels = _paChannels;
828 : pa_cvolume cVolumes;
829 0 : LATE(pa_cvolume_set)(&cVolumes, channels, volume);
830 :
831 : // Set the volume for the source
832 : paOperation
833 0 : = LATE(pa_context_set_source_volume_by_index)(_paContext, deviceIndex,
834 : &cVolumes,
835 : PaSetVolumeCallback,
836 0 : NULL);
837 :
838 0 : if (!paOperation)
839 : {
840 0 : setFailed = true;
841 : }
842 :
843 : // Don't need to wait for this to complete.
844 0 : LATE(pa_operation_unref)(paOperation);
845 :
846 0 : if (setFailed)
847 : {
848 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
849 : " could not set microphone volume, error%d",
850 : LATE(pa_context_errno)(_paContext));
851 0 : return -1;
852 : }
853 :
854 0 : return 0;
855 : }
856 :
857 : int32_t
858 0 : AudioMixerManagerLinuxPulse::MicrophoneVolume(uint32_t& volume) const
859 : {
860 :
861 0 : if (_paInputDeviceIndex == -1)
862 : {
863 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
864 : " input device index has not been set");
865 0 : return -1;
866 : }
867 :
868 0 : uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex;
869 :
870 : {
871 0 : AutoPulseLock auto_lock(_paMainloop);
872 : // Get the actual stream device index if we have a connected stream.
873 : // The device used by the stream can be changed during the call.
874 0 : if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream)
875 : != PA_STREAM_UNCONNECTED))
876 : {
877 0 : deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
878 : }
879 : }
880 :
881 0 : if (!GetSourceInfoByIndex(deviceIndex))
882 0 : return -1;
883 :
884 : {
885 0 : AutoPulseLock auto_lock(_paMainloop);
886 0 : volume = static_cast<uint32_t> (_paVolume);
887 : }
888 :
889 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
890 : " AudioMixerManagerLinuxPulse::MicrophoneVolume()"
891 : " => vol=%i, volume");
892 :
893 0 : return 0;
894 : }
895 :
896 : int32_t
897 0 : AudioMixerManagerLinuxPulse::MaxMicrophoneVolume(uint32_t& maxVolume) const
898 : {
899 :
900 0 : if (_paInputDeviceIndex == -1)
901 : {
902 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
903 : " input device index has not been set");
904 0 : return -1;
905 : }
906 :
907 : // PA_VOLUME_NORM corresponds to 100% (0db)
908 : // PA allows up to 150 db amplification (PA_VOLUME_MAX)
909 : // but that doesn't work well for all sound cards
910 0 : maxVolume = static_cast<uint32_t> (PA_VOLUME_NORM);
911 :
912 0 : return 0;
913 : }
914 :
915 : int32_t
916 0 : AudioMixerManagerLinuxPulse::MinMicrophoneVolume(uint32_t& minVolume) const
917 : {
918 :
919 0 : if (_paInputDeviceIndex == -1)
920 : {
921 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
922 : " input device index has not been set");
923 0 : return -1;
924 : }
925 :
926 0 : minVolume = static_cast<uint32_t> (PA_VOLUME_MUTED);
927 :
928 0 : return 0;
929 : }
930 :
931 0 : int32_t AudioMixerManagerLinuxPulse::MicrophoneVolumeStepSize(
932 : uint16_t& stepSize) const
933 : {
934 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
935 0 : if (_paInputDeviceIndex == -1)
936 : {
937 : WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
938 : " input device index has not been set");
939 0 : return -1;
940 : }
941 :
942 0 : uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex;
943 :
944 0 : AutoPulseLock auto_lock(_paMainloop);
945 :
946 : // Get the actual stream device index if we have a connected stream
947 : // The device used by the stream can be changed
948 : // during the call
949 0 : if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream)
950 : != PA_STREAM_UNCONNECTED))
951 : {
952 0 : deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
953 : }
954 :
955 0 : pa_operation* paOperation = NULL;
956 :
957 : // Get info for this source
958 : paOperation
959 0 : = LATE(pa_context_get_source_info_by_index)(_paContext, deviceIndex,
960 : PaSourceInfoCallback,
961 0 : (void*) this);
962 :
963 0 : WaitForOperationCompletion(paOperation);
964 :
965 0 : stepSize = static_cast<uint16_t> ((PA_VOLUME_NORM + 1) / _paVolSteps);
966 :
967 : WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
968 : "\tAudioMixerManagerLinuxPulse::MicrophoneVolumeStepSize()"
969 : " => size=%i", stepSize);
970 :
971 0 : return 0;
972 : }
973 :
974 : // ===========================================================================
975 : // Private Methods
976 : // ===========================================================================
977 :
978 : void
979 0 : AudioMixerManagerLinuxPulse::PaSinkInfoCallback(pa_context */*c*/,
980 : const pa_sink_info *i,
981 : int eol,
982 : void *pThis)
983 : {
984 : static_cast<AudioMixerManagerLinuxPulse*> (pThis)->
985 0 : PaSinkInfoCallbackHandler(i, eol);
986 0 : }
987 :
988 : void
989 0 : AudioMixerManagerLinuxPulse::PaSinkInputInfoCallback(
990 : pa_context */*c*/,
991 : const pa_sink_input_info *i,
992 : int eol,
993 : void *pThis)
994 : {
995 : static_cast<AudioMixerManagerLinuxPulse*> (pThis)->
996 0 : PaSinkInputInfoCallbackHandler(i, eol);
997 0 : }
998 :
999 :
1000 : void
1001 0 : AudioMixerManagerLinuxPulse::PaSourceInfoCallback(pa_context */*c*/,
1002 : const pa_source_info *i,
1003 : int eol,
1004 : void *pThis)
1005 : {
1006 : static_cast<AudioMixerManagerLinuxPulse*> (pThis)->
1007 0 : PaSourceInfoCallbackHandler(i, eol);
1008 0 : }
1009 :
1010 : void
1011 0 : AudioMixerManagerLinuxPulse::PaSetVolumeCallback(pa_context * c,
1012 : int success,
1013 : void */*pThis*/)
1014 : {
1015 : if (!success)
1016 : {
1017 : WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1,
1018 : " failed to set volume");
1019 : }
1020 0 : }
1021 :
1022 0 : void AudioMixerManagerLinuxPulse::PaSinkInfoCallbackHandler(
1023 : const pa_sink_info *i,
1024 : int eol)
1025 : {
1026 0 : if (eol)
1027 : {
1028 : // Signal that we are done
1029 0 : LATE(pa_threaded_mainloop_signal)(_paMainloop, 0);
1030 0 : return;
1031 : }
1032 :
1033 0 : _paChannels = i->channel_map.channels; // Get number of channels
1034 0 : pa_volume_t paVolume = PA_VOLUME_MUTED; // Minimum possible value.
1035 0 : for (int j = 0; j < _paChannels; ++j)
1036 : {
1037 0 : if (paVolume < i->volume.values[j])
1038 : {
1039 0 : paVolume = i->volume.values[j];
1040 : }
1041 : }
1042 0 : _paVolume = paVolume; // get the max volume for any channel
1043 0 : _paMute = i->mute; // get mute status
1044 :
1045 : // supported since PA 0.9.15
1046 : //_paVolSteps = i->n_volume_steps; // get the number of volume steps
1047 : // default value is PA_VOLUME_NORM+1
1048 0 : _paVolSteps = PA_VOLUME_NORM + 1;
1049 : }
1050 :
1051 0 : void AudioMixerManagerLinuxPulse::PaSinkInputInfoCallbackHandler(
1052 : const pa_sink_input_info *i,
1053 : int eol)
1054 : {
1055 0 : if (eol)
1056 : {
1057 : // Signal that we are done
1058 0 : LATE(pa_threaded_mainloop_signal)(_paMainloop, 0);
1059 0 : return;
1060 : }
1061 :
1062 0 : _paChannels = i->channel_map.channels; // Get number of channels
1063 0 : pa_volume_t paVolume = PA_VOLUME_MUTED; // Minimum possible value.
1064 0 : for (int j = 0; j < _paChannels; ++j)
1065 : {
1066 0 : if (paVolume < i->volume.values[j])
1067 : {
1068 0 : paVolume = i->volume.values[j];
1069 : }
1070 : }
1071 0 : _paVolume = paVolume; // Get the max volume for any channel
1072 0 : _paMute = i->mute; // Get mute status
1073 : }
1074 :
1075 0 : void AudioMixerManagerLinuxPulse::PaSourceInfoCallbackHandler(
1076 : const pa_source_info *i,
1077 : int eol)
1078 : {
1079 0 : if (eol)
1080 : {
1081 : // Signal that we are done
1082 0 : LATE(pa_threaded_mainloop_signal)(_paMainloop, 0);
1083 0 : return;
1084 : }
1085 :
1086 0 : _paChannels = i->channel_map.channels; // Get number of channels
1087 0 : pa_volume_t paVolume = PA_VOLUME_MUTED; // Minimum possible value.
1088 0 : for (int j = 0; j < _paChannels; ++j)
1089 : {
1090 0 : if (paVolume < i->volume.values[j])
1091 : {
1092 0 : paVolume = i->volume.values[j];
1093 : }
1094 : }
1095 0 : _paVolume = paVolume; // Get the max volume for any channel
1096 0 : _paMute = i->mute; // Get mute status
1097 :
1098 : // supported since PA 0.9.15
1099 : //_paVolSteps = i->n_volume_steps; // Get the number of volume steps
1100 : // default value is PA_VOLUME_NORM+1
1101 0 : _paVolSteps = PA_VOLUME_NORM + 1;
1102 : }
1103 :
1104 0 : void AudioMixerManagerLinuxPulse::WaitForOperationCompletion(
1105 : pa_operation* paOperation) const
1106 : {
1107 0 : while (LATE(pa_operation_get_state)(paOperation) == PA_OPERATION_RUNNING)
1108 : {
1109 0 : LATE(pa_threaded_mainloop_wait)(_paMainloop);
1110 : }
1111 :
1112 0 : LATE(pa_operation_unref)(paOperation);
1113 0 : }
1114 :
1115 0 : bool AudioMixerManagerLinuxPulse::GetSinkInputInfo() const {
1116 0 : pa_operation* paOperation = NULL;
1117 :
1118 0 : AutoPulseLock auto_lock(_paMainloop);
1119 : // Get info for this stream (sink input).
1120 0 : paOperation = LATE(pa_context_get_sink_input_info)(
1121 0 : _paContext,
1122 0 : LATE(pa_stream_get_index)(_paPlayStream),
1123 : PaSinkInputInfoCallback,
1124 0 : (void*) this);
1125 :
1126 0 : WaitForOperationCompletion(paOperation);
1127 0 : return true;
1128 : }
1129 :
1130 0 : bool AudioMixerManagerLinuxPulse::GetSinkInfoByIndex(
1131 : int device_index) const {
1132 0 : pa_operation* paOperation = NULL;
1133 :
1134 0 : AutoPulseLock auto_lock(_paMainloop);
1135 0 : paOperation = LATE(pa_context_get_sink_info_by_index)(_paContext,
1136 0 : device_index, PaSinkInfoCallback, (void*) this);
1137 :
1138 0 : WaitForOperationCompletion(paOperation);
1139 0 : return true;
1140 : }
1141 :
1142 0 : bool AudioMixerManagerLinuxPulse::GetSourceInfoByIndex(
1143 : int device_index) const {
1144 0 : pa_operation* paOperation = NULL;
1145 :
1146 0 : AutoPulseLock auto_lock(_paMainloop);
1147 0 : paOperation = LATE(pa_context_get_source_info_by_index)(
1148 0 : _paContext, device_index, PaSourceInfoCallback, (void*) this);
1149 :
1150 0 : WaitForOperationCompletion(paOperation);
1151 0 : return true;
1152 : }
1153 :
1154 : }
|