coreaudio-enum-devices.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. #include <CoreFoundation/CFString.h>
  2. #include <CoreAudio/CoreAudio.h>
  3. #include "../../obs-internal.h"
  4. #include "../../util/dstr.h"
  5. #include "../../util/apple/cfstring-utils.h"
  6. #include "mac-helpers.h"
  7. static bool obs_enum_audio_monitoring_device(obs_enum_audio_device_cb cb, void *data, AudioDeviceID id,
  8. bool allow_inputs)
  9. {
  10. UInt32 size = 0;
  11. CFStringRef cf_name = NULL;
  12. CFStringRef cf_uid = NULL;
  13. char *name = NULL;
  14. char *uid = NULL;
  15. OSStatus stat;
  16. bool cont = true;
  17. AudioObjectPropertyAddress addr = {kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput,
  18. kAudioObjectPropertyElementMain};
  19. /* Check if the device is capable of audio output. */
  20. AudioObjectGetPropertyDataSize(id, &addr, 0, NULL, &size);
  21. if (!allow_inputs && !size)
  22. return true;
  23. size = sizeof(CFStringRef);
  24. addr.mSelector = kAudioDevicePropertyDeviceUID;
  25. stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_uid);
  26. if (!success(stat, "get audio device UID"))
  27. goto fail;
  28. addr.mSelector = kAudioDevicePropertyDeviceNameCFString;
  29. stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_name);
  30. if (!success(stat, "get audio device name"))
  31. goto fail;
  32. name = cfstr_copy_cstr(cf_name, kCFStringEncodingUTF8);
  33. if (!name) {
  34. blog(LOG_WARNING, "%s: failed to convert name", __FUNCTION__);
  35. goto fail;
  36. }
  37. uid = cfstr_copy_cstr(cf_uid, kCFStringEncodingUTF8);
  38. if (!uid) {
  39. blog(LOG_WARNING, "%s: failed to convert uid", __FUNCTION__);
  40. goto fail;
  41. }
  42. cont = cb(data, name, uid);
  43. fail:
  44. bfree(name);
  45. bfree(uid);
  46. if (cf_name)
  47. CFRelease(cf_name);
  48. if (cf_uid)
  49. CFRelease(cf_uid);
  50. return cont;
  51. }
  52. static void enum_audio_devices(obs_enum_audio_device_cb cb, void *data, bool allow_inputs)
  53. {
  54. AudioObjectPropertyAddress addr = {kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
  55. kAudioObjectPropertyElementMain};
  56. UInt32 size = 0;
  57. UInt32 count;
  58. OSStatus stat;
  59. AudioDeviceID *ids;
  60. stat = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0, NULL, &size);
  61. if (!success(stat, "get data size"))
  62. return;
  63. ids = malloc(size);
  64. count = size / sizeof(AudioDeviceID);
  65. stat = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &size, ids);
  66. if (success(stat, "get data")) {
  67. for (UInt32 i = 0; i < count; i++) {
  68. if (!obs_enum_audio_monitoring_device(cb, data, ids[i], allow_inputs))
  69. break;
  70. }
  71. }
  72. free(ids);
  73. }
  74. void obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb, void *data)
  75. {
  76. enum_audio_devices(cb, data, false);
  77. }
  78. static bool alloc_default_id(void *data, const char *name, const char *id)
  79. {
  80. char **p_id = data;
  81. UNUSED_PARAMETER(name);
  82. *p_id = bstrdup(id);
  83. return false;
  84. }
  85. static void get_default_id(char **p_id)
  86. {
  87. AudioObjectPropertyAddress addr = {kAudioHardwarePropertyDefaultSystemOutputDevice,
  88. kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain};
  89. if (*p_id)
  90. return;
  91. OSStatus stat;
  92. AudioDeviceID id = 0;
  93. UInt32 size = sizeof(id);
  94. stat = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &size, &id);
  95. if (success(stat, "AudioObjectGetPropertyData"))
  96. obs_enum_audio_monitoring_device(alloc_default_id, p_id, id, true);
  97. if (!*p_id)
  98. *p_id = bzalloc(1);
  99. }
  100. struct device_name_info {
  101. const char *id;
  102. char *name;
  103. };
  104. static bool enum_device_name(void *data, const char *name, const char *id)
  105. {
  106. struct device_name_info *info = data;
  107. if (strcmp(info->id, id) == 0) {
  108. info->name = bstrdup(name);
  109. return false;
  110. }
  111. return true;
  112. }
  113. bool devices_match(const char *id1, const char *id2)
  114. {
  115. struct device_name_info info = {0};
  116. char *default_id = NULL;
  117. char *name1 = NULL;
  118. char *name2 = NULL;
  119. bool match;
  120. if (!id1 || !id2)
  121. return false;
  122. if (strcmp(id1, "default") == 0) {
  123. get_default_id(&default_id);
  124. id1 = default_id;
  125. }
  126. if (strcmp(id2, "default") == 0) {
  127. get_default_id(&default_id);
  128. id2 = default_id;
  129. }
  130. info.id = id1;
  131. enum_audio_devices(enum_device_name, &info, true);
  132. name1 = info.name;
  133. info.name = NULL;
  134. info.id = id2;
  135. enum_audio_devices(enum_device_name, &info, true);
  136. name2 = info.name;
  137. match = name1 && name2 && strcmp(name1, name2) == 0;
  138. bfree(default_id);
  139. bfree(name1);
  140. bfree(name2);
  141. return match;
  142. }