coreaudio-enum-devices.c 4.2 KB

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