coreaudio-enum-devices.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  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,
  8. void *data, AudioDeviceID id, 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 = {
  18. kAudioDevicePropertyStreams,
  19. kAudioDevicePropertyScopeOutput,
  20. kAudioObjectPropertyElementMaster
  21. };
  22. /* Check if the device is capable of audio output. */
  23. AudioObjectGetPropertyDataSize(id, &addr, 0, NULL, &size);
  24. if (!allow_inputs && !size)
  25. return true;
  26. size = sizeof(CFStringRef);
  27. addr.mSelector = kAudioDevicePropertyDeviceUID;
  28. stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_uid);
  29. if (!success(stat, "get audio device UID"))
  30. goto fail;
  31. addr.mSelector = kAudioDevicePropertyDeviceNameCFString;
  32. stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_name);
  33. if (!success(stat, "get audio device name"))
  34. goto fail;
  35. name = cfstr_copy_cstr(cf_name, kCFStringEncodingUTF8);
  36. if (!name) {
  37. blog(LOG_WARNING, "%s: failed to convert name", __FUNCTION__);
  38. goto fail;
  39. }
  40. uid = cfstr_copy_cstr(cf_uid, kCFStringEncodingUTF8);
  41. if (!uid) {
  42. blog(LOG_WARNING, "%s: failed to convert uid", __FUNCTION__);
  43. goto fail;
  44. }
  45. cont = cb(data, name, uid);
  46. fail:
  47. bfree(name);
  48. bfree(uid);
  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. }