Skip to content

Commit d9c7165

Browse files
committed
Mac device
1 parent 261126c commit d9c7165

File tree

6 files changed

+750
-19
lines changed

6 files changed

+750
-19
lines changed

modules/audio_device/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,8 @@ rtc_library("audio_device_impl") {
383383
"mac/audio_device_mac.h",
384384
"mac/audio_mixer_manager_mac.cc",
385385
"mac/audio_mixer_manager_mac.h",
386+
"mac/audio_device_utils_mac.cc",
387+
"mac/audio_device_utils_mac.h",
386388
]
387389
deps += [
388390
":audio_device_impl_frameworks",

modules/audio_device/audio_engine_device.h

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ typedef void* AVAudioEngineManualRenderingBlock;
5959
#include "sdk/objc/base/RTCMacros.h"
6060
#include "sdk/objc/native/src/audio/audio_session_observer.h"
6161

62+
#if TARGET_OS_OSX
63+
#import <CoreAudio/CoreAudio.h>
64+
#endif
65+
6266
RTC_FWD_DECL_OBJC_CLASS(RTC_OBJC_TYPE(RTCNativeAudioSessionDelegateAdapter));
6367

6468
namespace webrtc {
@@ -208,6 +212,9 @@ class AudioEngineDevice : public AudioDeviceModule,
208212
bool advanced_ducking = true;
209213
long ducking_level = 0; // 0 = Default
210214

215+
uint32_t output_device_id = 0; // kAudioObjectUnknown
216+
uint32_t input_device_id = 0; // kAudioObjectUnknown
217+
211218
bool operator==(const EngineState& rhs) const {
212219
return input_enabled == rhs.input_enabled &&
213220
input_running == rhs.input_running &&
@@ -223,7 +230,9 @@ class AudioEngineDevice : public AudioDeviceModule,
223230
voice_processing_bypassed == rhs.voice_processing_bypassed &&
224231
voice_processing_agc_enabled == rhs.voice_processing_agc_enabled &&
225232
advanced_ducking == rhs.advanced_ducking &&
226-
ducking_level == rhs.ducking_level;
233+
ducking_level == rhs.ducking_level &&
234+
output_device_id == rhs.output_device_id &&
235+
input_device_id == rhs.input_device_id;
227236
}
228237

229238
bool operator!=(const EngineState& rhs) const { return !(*this == rhs); }
@@ -306,6 +315,19 @@ class AudioEngineDevice : public AudioDeviceModule,
306315
(prev.IsOutputEnabled() != next.IsOutputEnabled());
307316
}
308317

318+
bool DidUpdateOutputDevice() const {
319+
return prev.output_device_id != next.output_device_id;
320+
}
321+
322+
bool DidUpdateInputDevice() const {
323+
return prev.input_device_id != next.input_device_id;
324+
}
325+
326+
bool IsEngineRestartRequired() const {
327+
return DidUpdateAudioGraph() || DidUpdateOutputDevice() ||
328+
DidUpdateInputDevice();
329+
}
330+
309331
// Special case to re-create engine when switching from Speaker & Mic ->
310332
// Speaker only.
311333
bool IsEngineRecreateRequired() const {
@@ -337,6 +359,15 @@ class AudioEngineDevice : public AudioDeviceModule,
337359
// AudioEngine observer methods. May be called from any thread.
338360
void ReconfigureEngine(bool is_required);
339361

362+
// Device related
363+
#if TARGET_OS_OSX
364+
void UpdateDeviceInformation();
365+
std::vector<AudioObjectID> input_device_ids_;
366+
std::vector<AudioObjectID> output_device_ids_;
367+
std::vector<std::string> output_device_labels_;
368+
std::vector<std::string> input_device_labels_;
369+
#endif
370+
340371
void DebugAudioEngine();
341372

342373
void StartRenderLoop();

modules/audio_device/audio_engine_device.mm

Lines changed: 185 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@
4141
#import "components/audio/RTCNativeAudioSessionDelegateAdapter.h"
4242
#endif
4343

44+
#if TARGET_OS_OSX
45+
#import "./mac/audio_device_utils_mac.h"
46+
#endif
47+
4448
namespace webrtc {
4549

4650
#define LOGI() RTC_LOG(LS_INFO) << "AudioEngineDevice::"
@@ -122,6 +126,14 @@
122126
record_parameters_.reset(config.sampleRate, config.inputNumberOfChannels);
123127
#endif
124128

129+
#if TARGET_OS_OSX
130+
UpdateDeviceInformation();
131+
engine_state_.output_device_id =
132+
mac_audio_utils::GetDefaultOutputDeviceID().value_or(kAudioObjectUnknown);
133+
engine_state_.input_device_id =
134+
mac_audio_utils::GetDefaultInputDeviceID().value_or(kAudioObjectUnknown);
135+
#endif
136+
125137
initialized_ = true;
126138
return 0;
127139
}
@@ -557,7 +569,22 @@
557569
}
558570

559571
int32_t AudioEngineDevice::SetPlayoutDevice(uint16_t index) {
560-
LOGW() << "SetPlayoutDevice: Not implemented, value: " << index;
572+
LOGW() << "SetPlayoutDevice value: " << index;
573+
RTC_DCHECK_RUN_ON(thread_);
574+
575+
#if TARGET_OS_OSX
576+
if (index > (output_device_ids_.size() - 1)) {
577+
LOGE() << "Device index is out of range: " << index;
578+
return -1;
579+
}
580+
581+
AudioDeviceID output_device_id = output_device_ids_[index];
582+
583+
SetEngineState([output_device_id](EngineState state) -> EngineState {
584+
state.output_device_id = output_device_id;
585+
return state;
586+
});
587+
#endif
561588

562589
return 0;
563590
}
@@ -567,32 +594,100 @@
567594

568595
return -1;
569596
}
570-
571597
int32_t AudioEngineDevice::PlayoutDeviceName(uint16_t index, char name[kAdmMaxDeviceNameSize],
572598
char guid[kAdmMaxGuidSize]) {
573-
// LOGW() << "PlayoutDeviceName: Not implemented";
599+
RTC_DCHECK_RUN_ON(thread_);
600+
601+
#if TARGET_OS_OSX
602+
RTC_DCHECK(output_device_ids_.size() == output_device_labels_.size());
603+
604+
if ((index > (output_device_ids_.size() - 1)) || (name == NULL)) {
605+
LOGE() << "Device index is out of range: " << index;
606+
return -1;
607+
}
608+
609+
memset(name, 0, kAdmMaxDeviceNameSize);
610+
memset(guid, 0, kAdmMaxGuidSize);
574611

612+
// Get device name
613+
strncpy(name, output_device_labels_[index].c_str(), kAdmMaxDeviceNameSize - 1);
614+
615+
std::optional<std::string> device_guid =
616+
mac_audio_utils::GetDeviceUniqueID(output_device_ids_[index]);
617+
if (device_guid) {
618+
strncpy(guid, device_guid->c_str(), kAdmMaxGuidSize - 1);
619+
} else {
620+
LOGE() << "Failed to get device unique ID for device: " << output_device_ids_[index];
621+
return -1;
622+
}
623+
624+
return 0;
625+
#else
575626
return -1;
627+
#endif
576628
}
577629

578630
int16_t AudioEngineDevice::PlayoutDevices() {
579-
// LOGI() << "PlayoutDevices";
631+
RTC_DCHECK_RUN_ON(thread_);
580632

633+
#if TARGET_OS_OSX
634+
return output_device_ids_.size();
635+
#else
581636
return (int16_t)1;
637+
#endif
582638
}
583639

584640
// ----------------------------------------------------------------------------------------------------
585641
// Recording Device
586642

587643
int32_t AudioEngineDevice::RecordingDeviceName(uint16_t index, char name[kAdmMaxDeviceNameSize],
588644
char guid[kAdmMaxGuidSize]) {
589-
// LOGW() << "RecordingDeviceName";
645+
#if TARGET_OS_OSX
646+
RTC_DCHECK(input_device_ids_.size() == input_device_labels_.size());
647+
648+
if ((index > (input_device_ids_.size() - 1)) || (name == NULL)) {
649+
LOGE() << "Device index is out of range: " << index;
650+
return -1;
651+
}
652+
653+
memset(name, 0, kAdmMaxDeviceNameSize);
654+
memset(guid, 0, kAdmMaxGuidSize);
655+
656+
// Get device name
657+
strncpy(name, input_device_labels_[index].c_str(), kAdmMaxDeviceNameSize - 1);
658+
659+
std::optional<std::string> device_guid =
660+
mac_audio_utils::GetDeviceUniqueID(input_device_ids_[index]);
661+
if (device_guid) {
662+
strncpy(guid, device_guid->c_str(), kAdmMaxGuidSize - 1);
663+
} else {
664+
LOGE() << "Failed to get device unique ID for device: " << input_device_ids_[index];
665+
return -1;
666+
}
590667

668+
return 0;
669+
#else
591670
return -1;
671+
#endif
592672
}
593673

594674
int32_t AudioEngineDevice::SetRecordingDevice(uint16_t index) {
595675
LOGI() << "SetRecordingDevice, index: " << index;
676+
RTC_DCHECK_RUN_ON(thread_);
677+
678+
#if TARGET_OS_OSX
679+
if (index > (input_device_ids_.size() - 1)) {
680+
RTC_LOG(LS_ERROR) << "Device index is out of range";
681+
return -1;
682+
}
683+
684+
AudioDeviceID input_device_id = input_device_ids_[index];
685+
686+
SetEngineState([input_device_id](EngineState state) -> EngineState {
687+
state.input_device_id = input_device_id;
688+
return state;
689+
});
690+
#endif
596691

597692
return 0;
598693
}
@@ -615,9 +710,13 @@
615710
}
616711

617712
int16_t AudioEngineDevice::RecordingDevices() {
618-
// LOGI() << "RecordingDevices";
713+
RTC_DCHECK_RUN_ON(thread_);
619714

715+
#if TARGET_OS_OSX
716+
return input_device_ids_.size();
717+
#else
620718
return (int16_t)1;
719+
#endif
621720
}
622721

623722
//
@@ -1143,8 +1242,8 @@
11431242
RTC_DCHECK(engine_manual_input_ == nullptr);
11441243

11451244
if (state.prev.IsAnyRunning() &&
1146-
(!state.next.IsAnyRunning() || state.DidUpdateAudioGraph() || state.DidBeginInterruption() ||
1147-
state.IsEngineRecreateRequired())) {
1245+
(!state.next.IsAnyRunning() || state.IsEngineRestartRequired() ||
1246+
state.DidBeginInterruption() || state.IsEngineRecreateRequired())) {
11481247
LOGI() << "Stopping AVAudioEngine...";
11491248
RTC_DCHECK(engine_device_ != nil);
11501249

@@ -1454,6 +1553,42 @@
14541553
this->InputNode().voiceProcessingAGCEnabled = state.next.voice_processing_agc_enabled;
14551554
}
14561555

1556+
#if TARGET_OS_OSX
1557+
1558+
if (state.next.IsAnyEnabled() &&
1559+
(!state.prev.IsAnyEnabled() || state.IsEngineRecreateRequired() ||
1560+
state.DidUpdateInputDevice() || state.DidUpdateOutputDevice())) {
1561+
if (state.next.IsInputEnabled() && state.next.input_device_id != 0) {
1562+
// Input device selection
1563+
OSStatus err = noErr;
1564+
uint32_t input_deviceId = state.next.input_device_id;
1565+
LOGI() << "Setting input device: " << input_deviceId;
1566+
AudioUnit inputUnit = this->InputNode().audioUnit;
1567+
err =
1568+
AudioUnitSetProperty(inputUnit, kAudioOutputUnitProperty_CurrentDevice,
1569+
kAudioUnitScope_Global, 1, &input_deviceId, sizeof(input_deviceId));
1570+
if (err != noErr) {
1571+
LOGE() << "Failed to set input device: " << input_deviceId;
1572+
}
1573+
}
1574+
1575+
if (state.next.IsOutputEnabled() && state.next.output_device_id != 0) {
1576+
// Output device selection
1577+
OSStatus err = noErr;
1578+
uint32_t output_deviceId = state.next.output_device_id;
1579+
LOGI() << "Setting output device: " << output_deviceId;
1580+
AudioUnit outputUnit = this->OutputNode().audioUnit;
1581+
err = AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_CurrentDevice,
1582+
kAudioUnitScope_Global, 0, &output_deviceId,
1583+
sizeof(output_deviceId));
1584+
if (err != noErr) {
1585+
LOGE() << "Failed to set output device: " << output_deviceId;
1586+
}
1587+
}
1588+
}
1589+
1590+
#endif
1591+
14571592
// Start playout buffer if output is running
14581593
if (state.next.IsOutputEnabled() && !audio_device_buffer_->IsPlaying()) {
14591594
if (engine_device_ != nullptr) {
@@ -1477,8 +1612,8 @@
14771612
}
14781613

14791614
if (state.next.IsAnyRunning()) {
1480-
if (!state.prev.IsAnyRunning() || state.DidEndInterruption() || state.DidUpdateAudioGraph() ||
1481-
state.IsEngineRecreateRequired()) {
1615+
if (!state.prev.IsAnyRunning() || state.DidEndInterruption() ||
1616+
state.IsEngineRestartRequired() || state.IsEngineRecreateRequired()) {
14821617
if (observer_ != nullptr) {
14831618
observer_->OnEngineWillStart(engine_device_, state.next.IsOutputEnabled(),
14841619
state.next.IsInputEnabled());
@@ -1601,6 +1736,46 @@
16011736
}
16021737
}
16031738

1739+
// ----------------------------------------------------------------------------------------------------
1740+
// Private - Device access
1741+
1742+
#if TARGET_OS_OSX
1743+
1744+
void AudioEngineDevice::UpdateDeviceInformation() {
1745+
using namespace webrtc::mac_audio_utils;
1746+
1747+
input_device_ids_.clear();
1748+
output_device_ids_.clear();
1749+
input_device_labels_.clear();
1750+
output_device_labels_.clear();
1751+
1752+
std::vector<AudioObjectID> all_device_ids = GetAllAudioDeviceIDs();
1753+
1754+
for (AudioObjectID device_id : all_device_ids) {
1755+
if (IsInputDevice(device_id)) {
1756+
input_device_ids_.push_back(device_id);
1757+
auto label = GetDeviceLabel(device_id, true);
1758+
if (label) {
1759+
input_device_labels_.push_back(*label);
1760+
} else {
1761+
input_device_labels_.push_back("Unknown Input Device");
1762+
}
1763+
}
1764+
1765+
if (IsOutputDevice(device_id)) {
1766+
output_device_ids_.push_back(device_id);
1767+
auto label = GetDeviceLabel(device_id, false);
1768+
if (label) {
1769+
output_device_labels_.push_back(*label);
1770+
} else {
1771+
output_device_labels_.push_back("Unknown Output Device");
1772+
}
1773+
}
1774+
}
1775+
}
1776+
1777+
#endif
1778+
16041779
// ----------------------------------------------------------------------------------------------------
16051780
// Private - Debug
16061781

0 commit comments

Comments
 (0)