#include #include #include "../../obs-internal.h" #include "../../util/dstr.h" #include "mac-helpers.h" static inline bool cf_to_cstr(CFStringRef ref, char *buf, size_t size) { if (!ref) return false; return (bool)CFStringGetCString(ref, buf, size, kCFStringEncodingUTF8); } static bool obs_enum_audio_monitoring_device(obs_enum_audio_device_cb cb, void *data, AudioDeviceID id, bool allow_inputs) { UInt32 size = 0; CFStringRef cf_name = NULL; CFStringRef cf_uid = NULL; char name[1024]; char uid[1024]; OSStatus stat; bool cont = true; AudioObjectPropertyAddress addr = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster }; /* Check if the device is capable of audio output. */ AudioObjectGetPropertyDataSize(id, &addr, 0, NULL, &size); if (!allow_inputs && !size) return true; size = sizeof(CFStringRef); addr.mSelector = kAudioDevicePropertyDeviceUID; stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_uid); if (!success(stat, "get audio device UID")) goto fail; addr.mSelector = kAudioDevicePropertyDeviceNameCFString; stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_name); if (!success(stat, "get audio device name")) goto fail; if (!cf_to_cstr(cf_name, name, sizeof(name))) { blog(LOG_WARNING, "%s: failed to convert name", __FUNCTION__); goto fail; } if (!cf_to_cstr(cf_uid, uid, sizeof(uid))) { blog(LOG_WARNING, "%s: failed to convert uid", __FUNCTION__); goto fail; } cont = cb(data, name, uid); fail: if (cf_name) CFRelease(cf_name); if (cf_uid) CFRelease(cf_uid); return cont; } static void enum_audio_devices(obs_enum_audio_device_cb cb, void *data, bool allow_inputs) { AudioObjectPropertyAddress addr = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; UInt32 size = 0; UInt32 count; OSStatus stat; AudioDeviceID *ids; stat = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0, NULL, &size); if (!success(stat, "get data size")) return; ids = malloc(size); count = size / sizeof(AudioDeviceID); stat = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &size, ids); if (success(stat, "get data")) { for (UInt32 i = 0; i < count; i++) { if (!obs_enum_audio_monitoring_device(cb, data, ids[i], allow_inputs)) break; } } free(ids); } void obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb, void *data) { enum_audio_devices(cb, data, false); } static bool alloc_default_id(void *data, const char *name, const char *id) { char **p_id = data; UNUSED_PARAMETER(name); *p_id = bstrdup(id); return false; } static void get_default_id(char **p_id) { AudioObjectPropertyAddress addr = { kAudioHardwarePropertyDefaultSystemOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; if (*p_id) return; OSStatus stat; AudioDeviceID id = 0; UInt32 size = sizeof(id); stat = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &size, &id); if (success(stat, "AudioObjectGetPropertyData")) obs_enum_audio_monitoring_device(alloc_default_id, p_id, id, true); if (!*p_id) *p_id = bzalloc(1); } struct device_name_info { const char *id; char *name; }; static bool enum_device_name(void *data, const char *name, const char *id) { struct device_name_info *info = data; if (strcmp(info->id, id) == 0) { info->name = bstrdup(name); return false; } return true; } bool devices_match(const char *id1, const char *id2) { struct device_name_info info = {0}; char *default_id = NULL; char *name1 = NULL; char *name2 = NULL; bool match; if (!id1 || !id2) return false; if (strcmp(id1, "default") == 0) { get_default_id(&default_id); id1 = default_id; } if (strcmp(id2, "default") == 0) { get_default_id(&default_id); id2 = default_id; } info.id = id1; enum_audio_devices(enum_device_name, &info, true); name1 = info.name; info.name = NULL; info.id = id2; enum_audio_devices(enum_device_name, &info, true); name2 = info.name; match = name1 && name2 && strcmp(name1, name2) == 0; bfree(default_id); bfree(name1); bfree(name2); return match; }