#include #include #include "../../obs-internal.h" #include "../../util/dstr.h" #include "../../util/apple/cfstring-utils.h" #include "mac-helpers.h" 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 = NULL; char *uid = NULL; 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; name = cfstr_copy_cstr(cf_name, kCFStringEncodingUTF8); if (!name) { blog(LOG_WARNING, "%s: failed to convert name", __FUNCTION__); goto fail; } uid = cfstr_copy_cstr(cf_uid, kCFStringEncodingUTF8); if (!uid) { blog(LOG_WARNING, "%s: failed to convert uid", __FUNCTION__); goto fail; } cont = cb(data, name, uid); fail: bfree(name); bfree(uid); 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; }