coreaudio-enum-devices.c 4.3 KB

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