Skip to content

Commit adf2c8e

Browse files
committed
adding method to enumerate video devices on MacOS
1 parent 96cb9e3 commit adf2c8e

4 files changed

Lines changed: 69 additions & 27 deletions

File tree

obs-studio-server/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,8 @@ SET(osn-server_SOURCES
403403
if (APPLE)
404404
SET(osn-server-osx_SOURCES
405405
"${PROJECT_SOURCE_DIR}/source/osn-multitrack-video-system-info-osx.mm"
406+
"${PROJECT_SOURCE_DIR}/source/nodeobs_settings-osx.h"
407+
"${PROJECT_SOURCE_DIR}/source/nodeobs_settings-osx.mm"
406408

407409
###### osx-util ######
408410
"${PROJECT_SOURCE_DIR}/source/util-osx.hpp"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#pragma once
2+
3+
#ifdef __APPLE__
4+
5+
#include <string>
6+
#include <utility>
7+
#include <vector>
8+
9+
// Returns (localizedName, uniqueID) pairs for all connected video capture devices.
10+
// Requires macOS 12 or later; returns an empty vector on older systems.
11+
std::vector<std::pair<std::string, std::string>> getVideoDevicesMacOS();
12+
13+
#endif
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#include "nodeobs_settings-osx.h"
2+
3+
#import <AVFoundation/AVFoundation.h>
4+
5+
std::vector<std::pair<std::string, std::string>> getVideoDevicesMacOS()
6+
{
7+
std::vector<std::pair<std::string, std::string>> result;
8+
9+
if (@available(macOS 12.0, *)) {
10+
NSMutableArray<AVCaptureDeviceType> *deviceTypes =
11+
[NSMutableArray arrayWithObject:AVCaptureDeviceTypeBuiltInWideAngleCamera];
12+
13+
if (@available(macOS 13.0, *)) {
14+
[deviceTypes addObject:AVCaptureDeviceTypeExternal];
15+
} else {
16+
// AVCaptureDeviceTypeExternalUnknown is deprecated in macOS 13 in favour of
17+
// AVCaptureDeviceTypeExternal, but is the correct constant for macOS 12.
18+
#pragma clang diagnostic push
19+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
20+
[deviceTypes addObject:AVCaptureDeviceTypeExternalUnknown];
21+
#pragma clang diagnostic pop
22+
}
23+
24+
AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession
25+
discoverySessionWithDeviceTypes:deviceTypes
26+
mediaType:AVMediaTypeVideo
27+
position:AVCaptureDevicePositionUnspecified];
28+
29+
for (AVCaptureDevice *device in session.devices) {
30+
if (!device.localizedName || !device.uniqueID)
31+
continue;
32+
result.push_back({[device.localizedName UTF8String], [device.uniqueID UTF8String]});
33+
}
34+
}
35+
36+
return result;
37+
}

obs-studio-server/source/nodeobs_settings.cpp

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
#ifdef __APPLE__
3838
#include <sys/types.h>
3939
#include <sys/stat.h>
40+
#include <CoreAudio/CoreAudio.h>
41+
#include "nodeobs_settings-osx.h"
4042
#endif
4143

4244
#include <unordered_set>
@@ -3864,7 +3866,7 @@ void OBS_settings::saveGenericSettings(std::vector<SubCategory> genericSettings,
38643866
config_save_safe(config, "tmp", nullptr);
38653867
}
38663868

3867-
void getDevices(const char *source_id, const char *property_name, bool shouldInitPlugin, std::vector<ipc::value> &rval)
3869+
void getDevices(const char *source_id, const char *property_name, std::vector<ipc::value> &rval)
38683870
{
38693871
auto settings = obs_get_source_defaults(source_id);
38703872
if (!settings)
@@ -3877,16 +3879,7 @@ void getDevices(const char *source_id, const char *property_name, bool shouldIni
38773879
obs_data_set_string(settings, "audio_device_id", dummy_device_name);
38783880
}
38793881

3880-
// Create a dummy source so that the "device" property can be init
3881-
OBSSourceAutoRelease dummy_source = nullptr;
3882-
if (shouldInitPlugin) {
3883-
dummy_source = obs_source_create(source_id, dummy_device_name, settings, nullptr);
3884-
if (!dummy_source) {
3885-
obs_data_release(settings);
3886-
return;
3887-
}
3888-
}
3889-
auto props = dummy_source != nullptr ? obs_source_properties(dummy_source) : obs_get_source_properties(source_id);
3882+
auto props = obs_get_source_properties(source_id);
38903883
if (!props) {
38913884
obs_data_release(settings);
38923885
blog(LOG_WARNING, "Could not get source properties for source id: %s", source_id);
@@ -4067,10 +4060,7 @@ void OBS_settings::OBS_settings_getInputAudioDevices(void *data, const int64_t i
40674060
enumAudioDevices(rval, eCapture);
40684061
#elif __APPLE__
40694062
const char *source_id = "coreaudio_input_capture";
4070-
// Do not init mac-coreaudio plugin. When passed a non-existent object
4071-
// it will create a reconnect_thread and try to initialize it.
4072-
const bool shouldInitCoreAudio = false;
4073-
getDevices(source_id, "device_id", shouldInitCoreAudio, rval);
4063+
getDevices(source_id, "device_id", rval);
40744064
#endif
40754065

40764066
AUTO_DEBUG;
@@ -4087,10 +4077,7 @@ void OBS_settings::OBS_settings_getOutputAudioDevices(void *data, const int64_t
40874077
enumAudioDevices(rval, eRender);
40884078
#elif __APPLE__
40894079
const char *source_id = "coreaudio_output_capture";
4090-
// Do not init mac-coreaudio plugin. When passed a non-existent object
4091-
// it will create a reconnect_thread and try to initialize it.
4092-
const bool shouldInitCoreAudio = false;
4093-
getDevices(source_id, "device_id", shouldInitCoreAudio, rval);
4080+
getDevices(source_id, "device_id", rval);
40944081
#endif
40954082

40964083
AUTO_DEBUG;
@@ -4104,14 +4091,17 @@ void OBS_settings::OBS_settings_getVideoDevices(void *data, const int64_t id, co
41044091
rval.push_back(ipc::value((uint32_t)0));
41054092
enumVideoDevices(rval);
41064093
#elif __APPLE__
4107-
const char *source_id = "macos_avcapture";
4108-
const char *property_name = "device";
4109-
// We must initialize the macos_avcapture plugin so that
4110-
// it can enumerate the video devices -or- we can provide
4111-
// our own custom implementation that enumerates video devices
4112-
// on MacOS.
4113-
const bool shouldInitPlugin = true;
4114-
getDevices(source_id, property_name, shouldInitPlugin, rval);
4094+
// Here we do not invoke getDevices() because the mac-capture
4095+
// plugin will not enumerate video devices unless it is fully initialized.
4096+
// So instead, we will enumerate video devices manually. Hopefully,
4097+
// mac-capture doesn't do anything special to enumerate video devices.
4098+
// If so, then this implementation will need to be updated.
4099+
auto devices = getVideoDevicesMacOS();
4100+
rval.push_back(ipc::value((uint64_t)devices.size()));
4101+
for (const auto &[name, uid] : devices) {
4102+
rval.push_back(ipc::value(name.c_str()));
4103+
rval.push_back(ipc::value(uid.c_str()));
4104+
}
41154105
#endif
41164106

41174107
AUTO_DEBUG;

0 commit comments

Comments
 (0)