coreaudio-enum-devices.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  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. kAudioDevicePropertyScopeOutput,
  24. kAudioObjectPropertyElementMaster
  25. };
  26. /* Check if the device is capable of audio output. */
  27. AudioObjectGetPropertyDataSize(id, &addr, 0, NULL, &size);
  28. if (!allow_inputs && !size)
  29. return true;
  30. size = sizeof(CFStringRef);
  31. addr.mSelector = kAudioDevicePropertyDeviceUID;
  32. stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_uid);
  33. if (!success(stat, "get audio device UID"))
  34. goto fail;
  35. addr.mSelector = kAudioDevicePropertyDeviceNameCFString;
  36. stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_name);
  37. if (!success(stat, "get audio device name"))
  38. goto fail;
  39. if (!cf_to_cstr(cf_name, name, sizeof(name))) {
  40. blog(LOG_WARNING, "%s: failed to convert name", __FUNCTION__);
  41. goto fail;
  42. }
  43. if (!cf_to_cstr(cf_uid, uid, sizeof(uid))) {
  44. blog(LOG_WARNING, "%s: failed to convert uid", __FUNCTION__);
  45. goto fail;
  46. }
  47. cont = cb(data, name, uid);
  48. fail:
  49. if (cf_name)
  50. CFRelease(cf_name);
  51. if (cf_uid)
  52. CFRelease(cf_uid);
  53. return cont;
  54. }
  55. static void enum_audio_devices(obs_enum_audio_device_cb cb, void *data,
  56. bool allow_inputs)
  57. {
  58. AudioObjectPropertyAddress addr = {
  59. kAudioHardwarePropertyDevices,
  60. kAudioObjectPropertyScopeGlobal,
  61. kAudioObjectPropertyElementMaster
  62. };
  63. UInt32 size = 0;
  64. UInt32 count;
  65. OSStatus stat;
  66. AudioDeviceID *ids;
  67. stat = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr,
  68. 0, NULL, &size);
  69. if (!success(stat, "get data size"))
  70. return;
  71. ids = malloc(size);
  72. count = size / sizeof(AudioDeviceID);
  73. stat = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
  74. 0, NULL, &size, ids);
  75. if (success(stat, "get data")) {
  76. for (UInt32 i = 0; i < count; i++) {
  77. if (!obs_enum_audio_monitoring_device(cb, data, ids[i],
  78. allow_inputs))
  79. break;
  80. }
  81. }
  82. free(ids);
  83. }
  84. void obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb, void *data)
  85. {
  86. enum_audio_devices(cb, data, false);
  87. }
  88. static bool alloc_default_id(void *data, const char *name, const char *id)
  89. {
  90. char **p_id = data;
  91. UNUSED_PARAMETER(name);
  92. *p_id = bstrdup(id);
  93. return false;
  94. }
  95. static void get_default_id(char **p_id)
  96. {
  97. AudioObjectPropertyAddress addr = {
  98. kAudioHardwarePropertyDefaultSystemOutputDevice,
  99. kAudioObjectPropertyScopeGlobal,
  100. kAudioObjectPropertyElementMaster
  101. };
  102. if (*p_id)
  103. return;
  104. OSStatus stat;
  105. AudioDeviceID id = 0;
  106. UInt32 size = sizeof(id);
  107. stat = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0,
  108. NULL, &size, &id);
  109. if (success(stat, "AudioObjectGetPropertyData"))
  110. obs_enum_audio_monitoring_device(alloc_default_id, p_id, id,
  111. true);
  112. if (!*p_id)
  113. *p_id = bzalloc(1);
  114. }
  115. struct device_name_info {
  116. const char *id;
  117. char *name;
  118. };
  119. static bool enum_device_name(void *data, const char *name, const char *id)
  120. {
  121. struct device_name_info *info = data;
  122. if (strcmp(info->id, id) == 0) {
  123. info->name = bstrdup(name);
  124. return false;
  125. }
  126. return true;
  127. }
  128. bool devices_match(const char *id1, const char *id2)
  129. {
  130. struct device_name_info info = {0};
  131. char *default_id = NULL;
  132. char *name1 = NULL;
  133. char *name2 = NULL;
  134. bool match;
  135. if (!id1 || !id2)
  136. return false;
  137. if (strcmp(id1, "default") == 0) {
  138. get_default_id(&default_id);
  139. id1 = default_id;
  140. }
  141. if (strcmp(id2, "default") == 0) {
  142. get_default_id(&default_id);
  143. id2 = default_id;
  144. }
  145. info.id = id1;
  146. enum_audio_devices(enum_device_name, &info, true);
  147. name1 = info.name;
  148. info.name = NULL;
  149. info.id = id2;
  150. enum_audio_devices(enum_device_name, &info, true);
  151. name2 = info.name;
  152. match = name1 && name2 && strcmp(name1, name2) == 0;
  153. bfree(default_id);
  154. bfree(name1);
  155. bfree(name2);
  156. return match;
  157. }