与windows的IMM接口类似,Mac也采用注册、监听回调的方式来拿到各种设备的插入、拔出、设备采样率、声道的变化等事件。
以下以Mac扬声器插拔为例:
#include <CoreAudio/CoreAudio.h>
#include <ApplicationServices/ApplicationServices.h>
#include <mach/mach.h>
#include <sys/sysctl.h>
//only listen playout device
class MacDeviceChangeObserver {
public:
MacDeviceChangeObserver() { Init(); }
virtual ~MacDeviceChangeObserver() { Uninit(); }
int Init();
int Uninit();
static OSStatus objectListenerProc(AudioObjectID objectId,
UInt32 numberAddresses,
const AudioObjectPropertyAddress addresses[],
void* clientData);
OSStatus implObjectListenerProc(const AudioObjectID objectId,
const UInt32 numberAddresses,
const AudioObjectPropertyAddress addresses[]);
void HandleDeviceChange();
sigslot::signal2<const std::string&, int> SignalDeviceChange;
};
int MacDeviceChangeObserver::Init() {
AudioObjectPropertyAddress propertyAddress = {
kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster };
CFRunLoopRef runLoop = NULL;
UInt32 size = sizeof(CFRunLoopRef);
int aoerr = AudioObjectSetPropertyData(
kAudioObjectSystemObject, &propertyAddress, 0, NULL, size, &runLoop);
if (aoerr != noErr) {
RTC_LOG(LS_ERROR) << "Error in AudioObjectSetPropertyData: "
<< (const char*)&aoerr;
return -1;
}
//Listen for any device changes
//propertyAddress.mSelector = kAudioHardwarePropertyDevices;
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
OSStatus ret = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propertyAddress,
&objectListenerProc, this);
return ret;
}
int MacDeviceChangeObserver::Uninit() {
AudioObjectPropertyAddress propertyAddress = {
kAudioObjectSystemObject, kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster };
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propertyAddress, &objectListenerProc, this);
return 0;
}
OSStatus MacDeviceChangeObserver::objectListenerProc(
AudioObjectID objectId,
UInt32 numberAddresses,
const AudioObjectPropertyAddress addresses[],
void* clientData) {
MacDeviceChangeObserver* ptrThis = (MacDeviceChangeObserver*)clientData;
RTC_DCHECK(ptrThis != NULL);
ptrThis->implObjectListenerProc(objectId, numberAddresses, addresses);
// AudioObjectPropertyListenerProc functions are supposed to return 0
return 0;
}
OSStatus MacDeviceChangeObserver::implObjectListenerProc(
const AudioObjectID objectId,
const UInt32 numberAddresses,
const AudioObjectPropertyAddress addresses[]) {
RTC_LOG(LS_INFO) << "MacDeviceChangeObserver::implObjectListenerProc()";
for (UInt32 i = 0; i < numberAddresses; i++) {
if (addresses[i].mSelector == kAudioHardwarePropertyDevices) {
RTC_LOG(INFO) << "kAudioHardwarePropertyDevices";
} else if (addresses[i].mSelector == kAudioHardwarePropertyDefaultOutputDevice) { //处理默认输出设备变化事件
RTC_LOG(INFO) << "kAudioDevicePropertyDefaultOutputDevice Change";
HandleDeviceChange();
} else if (addresses[i].mSelector == kAudioHardwarePropertyDefaultSystemOutputDevice) {
RTC_LOG(INFO) << "kAudioHardwarePropertyDefaultSystemOutputDevice";
}
}
return 0;
}
//获取默认音频输出设备,抛给业务层处理,业务层重置新得音频输出设备
void MacDeviceChangeObserver::HandleDeviceChange() {
RTC_LOG(INFO) << "MacDeviceChangeObserver::HandleDeviceChange: ";
// If the number is below the number of devices, assume it's "WEBRTC ID"
// otherwise assume it's a CoreAudio ID
AudioDeviceID usedID;
//UInt32 hardwareProperty = kAudioDevicePropertyScopeOutput;
AudioObjectPropertyAddress propertyAddress = {
kAudioHardwarePropertyDefaultSystemOutputDevice, kAudioObjectPropertyScopeOutput,
kAudioObjectPropertyElementMaster};
UInt32 size = sizeof(UInt32);
AudioObjectGetPropertyData(
kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, &usedID);
if (usedID == kAudioDeviceUnknown) {
RTC_LOG(LS_WARNING) << "GetDeviceName(): Default device unknown";
}
propertyAddress = { kAudioDevicePropertyDeviceName, 0, 0};
const int kAdmMaxDeviceNameSize = 128;
UInt32 len = kAdmMaxDeviceNameSize;
char devName[len];
OSStatus ret = AudioObjectGetPropertyData(usedID, &propertyAddress, 0, NULL, &len, devName);
RTC_LOG(LS_INFO) << "ret=" << ret << ", AudioDeviceID: " << usedID << ", default name :" << std::string(devName);
SignalDeviceChange(std::to_string(usedID), 0);//将变动的设备信息抛出去。
}