Mac 扬声器热插拔

本文介绍了如何在Mac系统中使用CoreAudio API注册监听回调,以便在扬声器等音频设备插入、拔出或其属性改变时接收到通知。通过实现AudioObjectPropertyListenerProc回调函数,可以处理默认输出设备的变更事件,并调用HandleDeviceChange方法来更新业务层的音频输出设备设置。
摘要由CSDN通过智能技术生成

 与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);//将变动的设备信息抛出去。
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值