yolobs-studio/libobs/audio-monitoring/osx/coreaudio-enum-devices.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

190 lines
4.2 KiB
C
Raw Normal View History

2017-04-19 19:54:15 +00:00
#include <CoreFoundation/CFString.h>
#include <CoreAudio/CoreAudio.h>
#include "../../obs-internal.h"
#include "../../util/dstr.h"
2019-07-27 12:47:10 +00:00
#include "../../util/apple/cfstring-utils.h"
2017-04-19 19:54:15 +00:00
#include "mac-helpers.h"
2017-06-29 19:01:10 +00:00
static bool obs_enum_audio_monitoring_device(obs_enum_audio_device_cb cb,
2019-09-22 21:19:10 +00:00
void *data, AudioDeviceID id,
bool allow_inputs)
2017-04-19 19:54:15 +00:00
{
2019-09-22 21:19:10 +00:00
UInt32 size = 0;
2017-04-19 19:54:15 +00:00
CFStringRef cf_name = NULL;
2019-09-22 21:19:10 +00:00
CFStringRef cf_uid = NULL;
char *name = NULL;
char *uid = NULL;
OSStatus stat;
bool cont = true;
2017-04-19 19:54:15 +00:00
2019-09-22 21:19:10 +00:00
AudioObjectPropertyAddress addr = {kAudioDevicePropertyStreams,
kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMaster};
2017-04-19 19:54:15 +00:00
2018-12-16 16:14:58 +00:00
/* Check if the device is capable of audio output. */
AudioObjectGetPropertyDataSize(id, &addr, 0, NULL, &size);
if (!allow_inputs && !size)
return true;
2017-04-19 19:54:15 +00:00
size = sizeof(CFStringRef);
addr.mSelector = kAudioDevicePropertyDeviceUID;
stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_uid);
if (!success(stat, "get audio device UID"))
2018-12-16 16:14:58 +00:00
goto fail;
2017-04-19 19:54:15 +00:00
addr.mSelector = kAudioDevicePropertyDeviceNameCFString;
stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_name);
if (!success(stat, "get audio device name"))
goto fail;
2019-07-27 12:47:10 +00:00
name = cfstr_copy_cstr(cf_name, kCFStringEncodingUTF8);
if (!name) {
2017-04-19 19:54:15 +00:00
blog(LOG_WARNING, "%s: failed to convert name", __FUNCTION__);
goto fail;
}
2019-07-27 12:47:10 +00:00
uid = cfstr_copy_cstr(cf_uid, kCFStringEncodingUTF8);
if (!uid) {
2017-04-19 19:54:15 +00:00
blog(LOG_WARNING, "%s: failed to convert uid", __FUNCTION__);
goto fail;
}
2017-06-29 19:01:10 +00:00
cont = cb(data, name, uid);
2017-04-19 19:54:15 +00:00
fail:
2019-07-27 12:47:10 +00:00
bfree(name);
bfree(uid);
2017-04-19 19:54:15 +00:00
if (cf_name)
CFRelease(cf_name);
if (cf_uid)
CFRelease(cf_uid);
2017-06-29 19:01:10 +00:00
return cont;
2017-04-19 19:54:15 +00:00
}
2017-06-29 19:01:10 +00:00
static void enum_audio_devices(obs_enum_audio_device_cb cb, void *data,
2019-09-22 21:19:10 +00:00
bool allow_inputs)
2017-04-19 19:54:15 +00:00
{
2019-09-22 21:19:10 +00:00
AudioObjectPropertyAddress addr = {kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster};
2017-04-19 19:54:15 +00:00
2019-09-22 21:19:10 +00:00
UInt32 size = 0;
UInt32 count;
OSStatus stat;
2017-04-19 19:54:15 +00:00
AudioDeviceID *ids;
stat = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr,
2019-09-22 21:19:10 +00:00
0, NULL, &size);
2017-04-19 19:54:15 +00:00
if (!success(stat, "get data size"))
return;
2019-09-22 21:19:10 +00:00
ids = malloc(size);
2017-04-19 19:54:15 +00:00
count = size / sizeof(AudioDeviceID);
2019-09-22 21:19:10 +00:00
stat = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0,
NULL, &size, ids);
2017-04-19 19:54:15 +00:00
if (success(stat, "get data")) {
2017-06-29 19:01:10 +00:00
for (UInt32 i = 0; i < count; i++) {
if (!obs_enum_audio_monitoring_device(cb, data, ids[i],
2019-09-22 21:19:10 +00:00
allow_inputs))
2017-06-29 19:01:10 +00:00
break;
}
2017-04-19 19:54:15 +00:00
}
free(ids);
}
2017-06-29 19:01:10 +00:00
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,
2019-09-22 21:19:10 +00:00
kAudioObjectPropertyElementMaster};
2017-06-29 19:01:10 +00:00
if (*p_id)
return;
2019-09-22 21:19:10 +00:00
OSStatus stat;
2017-06-29 19:01:10 +00:00
AudioDeviceID id = 0;
2019-09-22 21:19:10 +00:00
UInt32 size = sizeof(id);
2017-06-29 19:01:10 +00:00
stat = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0,
2019-09-22 21:19:10 +00:00
NULL, &size, &id);
2017-06-29 19:01:10 +00:00
if (success(stat, "AudioObjectGetPropertyData"))
obs_enum_audio_monitoring_device(alloc_default_id, p_id, id,
2019-09-22 21:19:10 +00:00
true);
2017-06-29 19:01:10 +00:00
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;
}