mirror of
https://github.com/obsproject/obs-studio.git
synced 2024-09-20 04:42:18 +02:00
mac-virtualcam: Update obs-plugin to support macOS camera extensions
Co-authored-by: PatTheMav <PatTheMav@users.noreply.github.com>
This commit is contained in:
parent
5c6e471a56
commit
aae2f7e8ff
@ -61,3 +61,9 @@ set_target_properties(
|
||||
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER com.obsproject.obs-mac-virtualcam
|
||||
XCODE_ATTRIBUTE_CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION YES
|
||||
XCODE_ATTRIBUTE_GCC_WARN_SHADOW YES)
|
||||
|
||||
set_target_properties_obs(
|
||||
obs-dal-plugin
|
||||
PROPERTIES OUTPUT_NAME obs-mac-virtualcam
|
||||
FOLDER plugins
|
||||
PREFIX "")
|
||||
|
@ -2,23 +2,20 @@ cmake_minimum_required(VERSION 3.22...3.25)
|
||||
|
||||
legacy_check()
|
||||
|
||||
find_library(APPKIT AppKit)
|
||||
find_library(COREVIDEO CoreVideo)
|
||||
find_library(IOSURFACE IOSurface)
|
||||
mark_as_advanced(APPKIT COREVIDEO IOSURFACE)
|
||||
|
||||
add_library(mac-virtualcam MODULE)
|
||||
add_library(OBS::virtualcam ALIAS mac-virtualcam)
|
||||
|
||||
target_sources(mac-virtualcam PRIVATE Defines.h plugin-main.mm OBSDALMachServer.mm OBSDALMachServer.h)
|
||||
target_compile_features(mac-virtualcam PRIVATE cxx_deleted_functions cxx_rvalue_references cxx_std_17)
|
||||
if(NOT XCODE)
|
||||
target_compile_options(mac-virtualcam PRIVATE -fobjc-arc -fobjc-weak)
|
||||
|
||||
if(XCODE)
|
||||
target_compile_options(mac-virtualcam PRIVATE -fmodules -fcxx-modules)
|
||||
else()
|
||||
target_compile_options(mac-virtualcam PRIVATE -fobjc-arc -fobjc-weak -fmodules -fcxx-modules)
|
||||
endif()
|
||||
|
||||
target_link_libraries(mac-virtualcam PRIVATE OBS::mach-protocol OBS::libobs OBS::frontend-api ${APPKIT} ${COREVIDEO}
|
||||
${IOSURFACE})
|
||||
target_link_libraries(mac-virtualcam PRIVATE OBS::mach-protocol OBS::libobs OBS::frontend-api)
|
||||
|
||||
# cmake-format: off
|
||||
set_target_properties_obs(
|
||||
mac-virtualcam
|
||||
PROPERTIES FOLDER plugins
|
||||
@ -26,4 +23,7 @@ set_target_properties_obs(
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../../"
|
||||
XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES
|
||||
XCODE_ATTRIBUTE_CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION YES
|
||||
XCODE_ATTRIBUTE_GCC_WARN_SHADOW YES)
|
||||
XCODE_ATTRIBUTE_GCC_WARN_SHADOW YES
|
||||
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES YES
|
||||
XCODE_ATTRIBUTE_CLANG_MODULES_AUTOLINK YES)
|
||||
# cmake-format: on
|
||||
|
@ -5,8 +5,8 @@
|
||||
// Created by John Boiles on 5/5/20.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreVideo/CoreVideo.h>
|
||||
@import Foundation;
|
||||
@import CoreVideo;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
#import "OBSDALMachServer.h"
|
||||
#include <obs-module.h>
|
||||
#include <CoreVideo/CoreVideo.h>
|
||||
#include "MachProtocol.h"
|
||||
#include "Defines.h"
|
||||
|
||||
|
@ -23,6 +23,8 @@
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>(c) 2012-${CURRENT_YEAR} Hugh Bailey</string>
|
||||
<string>(c) 2020-${CURRENT_YEAR} John Boiles, Patrick Heyer, Sebastian Beckmann</string>
|
||||
<key>OBSCameraDeviceUUID</key>
|
||||
<string>${VIRTUALCAM_DEVICE_UUID}</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -1 +1,8 @@
|
||||
Plugin_Name="macOS Virtual Webcam"
|
||||
Error.SystemExtension.NotInstalled="The virtual camera is not installed.\n\nPlease allow OBS to install system software in System Settings → Privacy & Security → Security.\n\nYou may need to restart OBS if this message still appears afterward."
|
||||
Error.SystemExtension.CameraUnavailable="Could not find virtual camera.\n\nPlease try again."
|
||||
Error.SystemExtension.CameraNotStarted="Unable to start virtual camera.\n\nPlease try again."
|
||||
Error.SystemExtension.InstallationError="An error has occured while installing the virtual camera:"
|
||||
Error.SystemExtension.WrongLocation="OBS cannot install the virtual camera if it's not in \"/Applications\". Please move OBS to the \"/Applications\" directory."
|
||||
Error.DAL.NotInstalled="The virtual camera could not be installed or updated.\n\nPlease try again and enter your administrator password when prompted."
|
||||
Error.DAL.NotUninstalled="Could not remove the legacy virtual camera.\n\nPlease try again and enter your account password when prompted."
|
||||
|
@ -1,3 +1,6 @@
|
||||
@import CoreMediaIO;
|
||||
@import SystemExtensions;
|
||||
|
||||
#include <obs-module.h>
|
||||
#include "OBSDALMachServer.h"
|
||||
#include "Defines.h"
|
||||
@ -9,33 +12,164 @@ MODULE_EXPORT const char *obs_module_description(void)
|
||||
return "macOS virtual webcam output";
|
||||
}
|
||||
|
||||
NSString *const OBSDalDestination = @"/Library/CoreMediaIO/Plug-Ins/DAL";
|
||||
|
||||
static bool cmio_extension_supported()
|
||||
{
|
||||
if (@available(macOS 13.0, *)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
struct virtualcam_data {
|
||||
obs_output_t *output;
|
||||
obs_video_info videoInfo;
|
||||
CVPixelBufferPoolRef pool;
|
||||
|
||||
// CMIO Extension (available with macOS 13)
|
||||
CMSimpleQueueRef queue;
|
||||
CMIODeviceID deviceID;
|
||||
CMIOStreamID streamID;
|
||||
CMFormatDescriptionRef formatDescription;
|
||||
id extensionDelegate;
|
||||
|
||||
// Legacy DAL (deprecated since macOS 12.3)
|
||||
OBSDALMachServer *machServer;
|
||||
};
|
||||
|
||||
static bool check_dal_plugin()
|
||||
@interface SystemExtensionActivationDelegate
|
||||
: NSObject <OSSystemExtensionRequestDelegate> {
|
||||
@private
|
||||
struct virtualcam_data *_vcam;
|
||||
}
|
||||
|
||||
@property (getter=isInstalled) BOOL installed;
|
||||
@property NSString *lastErrorMessage;
|
||||
- (instancetype)init __unavailable;
|
||||
@end
|
||||
|
||||
@implementation SystemExtensionActivationDelegate
|
||||
|
||||
- (id)initWithVcam:(virtualcam_data *)vcam
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
_vcam = vcam;
|
||||
_installed = NO;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (OSSystemExtensionReplacementAction)
|
||||
request:(nonnull OSSystemExtensionRequest *)request
|
||||
actionForReplacingExtension:
|
||||
(nonnull OSSystemExtensionProperties *)existing
|
||||
withExtension:(nonnull OSSystemExtensionProperties *)ext
|
||||
{
|
||||
NSString *extVersion =
|
||||
[NSString stringWithFormat:@"%@.%@", [ext bundleShortVersion],
|
||||
[ext bundleVersion]];
|
||||
NSString *existingVersion = [NSString
|
||||
stringWithFormat:@"%@.%@", [existing bundleShortVersion],
|
||||
[existing bundleVersion]];
|
||||
|
||||
if ([extVersion compare:existingVersion
|
||||
options:NSNumericSearch] == NSOrderedDescending) {
|
||||
return OSSystemExtensionReplacementActionReplace;
|
||||
} else {
|
||||
return OSSystemExtensionReplacementActionCancel;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)request:(nonnull OSSystemExtensionRequest *)request
|
||||
didFailWithError:(nonnull NSError *)error
|
||||
{
|
||||
NSString *errorMessage;
|
||||
int severity;
|
||||
|
||||
switch (error.code) {
|
||||
case OSSystemExtensionErrorRequestCanceled:
|
||||
errorMessage =
|
||||
@"macOS Camera Extension installation request cancelled.";
|
||||
severity = LOG_INFO;
|
||||
break;
|
||||
case OSSystemExtensionErrorUnsupportedParentBundleLocation:
|
||||
self.lastErrorMessage = [NSString
|
||||
stringWithUTF8String:
|
||||
obs_module_text(
|
||||
"Error.SystemExtension.WrongLocation")];
|
||||
errorMessage = self.lastErrorMessage;
|
||||
severity = LOG_WARNING;
|
||||
break;
|
||||
default:
|
||||
self.lastErrorMessage = error.localizedDescription;
|
||||
errorMessage = [NSString
|
||||
stringWithFormat:
|
||||
@"OSSystemExtensionErrorCode %ld (\"%s\")",
|
||||
error.code,
|
||||
error.localizedDescription.UTF8String];
|
||||
severity = LOG_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
blog(severity, "mac-camera-extension error: %s",
|
||||
errorMessage.UTF8String);
|
||||
}
|
||||
|
||||
- (void)request:(nonnull OSSystemExtensionRequest *)request
|
||||
didFinishWithResult:(OSSystemExtensionRequestResult)result
|
||||
{
|
||||
self.installed = YES;
|
||||
blog(LOG_INFO, "macOS Camera Extension activated successfully.");
|
||||
}
|
||||
|
||||
- (void)requestNeedsUserApproval:(nonnull OSSystemExtensionRequest *)request
|
||||
{
|
||||
self.installed = NO;
|
||||
blog(LOG_INFO, "macOS Camera Extension user approval required.");
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static void install_cmio_system_extension(struct virtualcam_data *vcam)
|
||||
{
|
||||
OSSystemExtensionRequest *request = [OSSystemExtensionRequest
|
||||
activationRequestForExtension:
|
||||
@"com.obsproject.obs-studio.mac-camera-extension"
|
||||
queue:dispatch_get_main_queue()];
|
||||
request.delegate = vcam->extensionDelegate;
|
||||
|
||||
[[OSSystemExtensionManager sharedManager] submitRequest:request];
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
OBSDalPluginNotInstalled,
|
||||
OBSDalPluginInstalled,
|
||||
OBSDalPluginNeedsUpdate
|
||||
} dal_plugin_status;
|
||||
|
||||
static dal_plugin_status check_dal_plugin()
|
||||
{
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
|
||||
NSString *dalPluginDestinationPath =
|
||||
@"/Library/CoreMediaIO/Plug-Ins/DAL/";
|
||||
NSString *dalPluginFileName =
|
||||
@"/Library/CoreMediaIO/Plug-Ins/DAL/obs-mac-virtualcam.plugin";
|
||||
NSString *dalPluginFileName = [OBSDalDestination
|
||||
stringByAppendingString:@"/obs-mac-virtualcam.plugin"];
|
||||
|
||||
BOOL dalPluginDirExists =
|
||||
[fileManager fileExistsAtPath:dalPluginDestinationPath];
|
||||
BOOL dalPluginInstalled =
|
||||
[fileManager fileExistsAtPath:dalPluginFileName];
|
||||
BOOL dalPluginUpdateNeeded = NO;
|
||||
|
||||
if (dalPluginInstalled) {
|
||||
NSDictionary *dalPluginInfoPlist = [NSDictionary
|
||||
dictionaryWithContentsOfURL:
|
||||
[NSURL fileURLWithPath:
|
||||
@"/Library/CoreMediaIO/Plug-Ins/DAL/obs-mac-virtualcam.plugin/Contents/Info.plist"]];
|
||||
[OBSDalDestination
|
||||
stringByAppendingString:
|
||||
@"/obs-mac-virtualcam.plugin/Contents/Info.plist"]]];
|
||||
|
||||
NSString *dalPluginVersion = [dalPluginInfoPlist
|
||||
valueForKey:@"CFBundleShortVersionString"];
|
||||
NSString *dalPluginBuild =
|
||||
@ -45,37 +179,42 @@ static bool check_dal_plugin()
|
||||
objectForKey:@"CFBundleShortVersionString"];
|
||||
NSString *obsBuild = [[[NSBundle mainBundle] infoDictionary]
|
||||
objectForKey:(NSString *)kCFBundleVersionKey];
|
||||
dalPluginUpdateNeeded =
|
||||
BOOL dalPluginUpdateNeeded =
|
||||
!([dalPluginVersion isEqualToString:obsVersion] &&
|
||||
[dalPluginBuild isEqualToString:obsBuild]);
|
||||
|
||||
return dalPluginUpdateNeeded ? OBSDalPluginNeedsUpdate
|
||||
: OBSDalPluginInstalled;
|
||||
}
|
||||
|
||||
if (!dalPluginInstalled || dalPluginUpdateNeeded) {
|
||||
NSString *dalPluginSourcePath;
|
||||
return OBSDalPluginNotInstalled;
|
||||
}
|
||||
|
||||
static bool install_dal_plugin(bool update)
|
||||
{
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
BOOL dalPluginDirExists =
|
||||
[fileManager fileExistsAtPath:OBSDalDestination];
|
||||
|
||||
NSURL *bundleURL = [[NSBundle mainBundle] bundleURL];
|
||||
NSString *pluginPath =
|
||||
@"Contents/Resources/obs-mac-virtualcam.plugin";
|
||||
NSString *pluginPath = @"Contents/Resources/obs-mac-virtualcam.plugin";
|
||||
|
||||
NSURL *pluginUrl =
|
||||
[bundleURL URLByAppendingPathComponent:pluginPath];
|
||||
dalPluginSourcePath = [pluginUrl path];
|
||||
NSURL *pluginUrl = [bundleURL URLByAppendingPathComponent:pluginPath];
|
||||
NSString *dalPluginSourcePath = [pluginUrl path];
|
||||
|
||||
NSString *createPluginDirCmd =
|
||||
(!dalPluginDirExists)
|
||||
? [NSString stringWithFormat:
|
||||
@"mkdir -p '%@' && ",
|
||||
dalPluginDestinationPath]
|
||||
? [NSString stringWithFormat:@"mkdir -p '%@' && ",
|
||||
OBSDalDestination]
|
||||
: @"";
|
||||
NSString *deleteOldPluginCmd =
|
||||
(dalPluginUpdateNeeded)
|
||||
? [NSString stringWithFormat:@"rm -rf '%@' && ",
|
||||
dalPluginFileName]
|
||||
(update) ? [NSString stringWithFormat:@"rm -rf '%@' && ",
|
||||
OBSDalDestination]
|
||||
: @"";
|
||||
NSString *copyPluginCmd =
|
||||
[NSString stringWithFormat:@"cp -R '%@' '%@'",
|
||||
dalPluginSourcePath,
|
||||
dalPluginDestinationPath];
|
||||
NSString *copyPluginCmd = [NSString
|
||||
stringWithFormat:@"cp -R '%@' '%@'", dalPluginSourcePath,
|
||||
OBSDalDestination];
|
||||
|
||||
if ([fileManager fileExistsAtPath:dalPluginSourcePath]) {
|
||||
NSString *copyCmd = [NSString
|
||||
stringWithFormat:
|
||||
@ -91,10 +230,13 @@ static bool check_dal_plugin()
|
||||
const char *errorMessage = [[errorDict
|
||||
objectForKey:@"NSAppleScriptErrorMessage"]
|
||||
UTF8String];
|
||||
|
||||
blog(LOG_INFO,
|
||||
"[macOS] VirtualCam DAL Plugin Installation status: %s",
|
||||
errorMessage);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
blog(LOG_INFO,
|
||||
@ -102,8 +244,29 @@ static bool check_dal_plugin()
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool uninstall_dal_plugin()
|
||||
{
|
||||
NSAppleScript *scriptObject = [[NSAppleScript alloc]
|
||||
initWithSource:
|
||||
[NSString
|
||||
stringWithFormat:
|
||||
@"do shell script \"rm -rf %@/obs-mac-virtualcam.plugin\" with administrator privileges",
|
||||
OBSDalDestination]];
|
||||
|
||||
NSDictionary *errorDict;
|
||||
|
||||
[scriptObject executeAndReturnError:&errorDict];
|
||||
if (errorDict) {
|
||||
blog(LOG_INFO,
|
||||
"[macOS] VirtualCam DAL Plugin could not be uninstalled: %s",
|
||||
[[errorDict objectForKey:NSAppleScriptErrorMessage]
|
||||
UTF8String]);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
FourCharCode convert_video_format_to_mac(enum video_format format,
|
||||
enum video_range_type range)
|
||||
@ -148,7 +311,16 @@ static void *virtualcam_output_create(obs_data_t *settings,
|
||||
(struct virtualcam_data *)bzalloc(sizeof(*vcam));
|
||||
|
||||
vcam->output = output;
|
||||
|
||||
if (cmio_extension_supported()) {
|
||||
vcam->extensionDelegate =
|
||||
[[SystemExtensionActivationDelegate alloc]
|
||||
initWithVcam:vcam];
|
||||
install_cmio_system_extension(vcam);
|
||||
} else {
|
||||
vcam->machServer = [[OBSDALMachServer alloc] init];
|
||||
}
|
||||
|
||||
return vcam;
|
||||
}
|
||||
|
||||
@ -156,7 +328,12 @@ static void virtualcam_output_destroy(void *data)
|
||||
{
|
||||
struct virtualcam_data *vcam = (struct virtualcam_data *)data;
|
||||
|
||||
if (cmio_extension_supported()) {
|
||||
vcam->extensionDelegate = nil;
|
||||
} else {
|
||||
vcam->machServer = nil;
|
||||
}
|
||||
|
||||
bfree(vcam);
|
||||
}
|
||||
|
||||
@ -164,11 +341,58 @@ static bool virtualcam_output_start(void *data)
|
||||
{
|
||||
struct virtualcam_data *vcam = (struct virtualcam_data *)data;
|
||||
|
||||
bool hasDalPlugin = check_dal_plugin();
|
||||
dal_plugin_status dal_status = check_dal_plugin();
|
||||
|
||||
if (!hasDalPlugin) {
|
||||
if (cmio_extension_supported()) {
|
||||
if (dal_status != OBSDalPluginNotInstalled) {
|
||||
if (!uninstall_dal_plugin()) {
|
||||
obs_output_set_last_error(
|
||||
vcam->output,
|
||||
obs_module_text(
|
||||
"Error.DAL.NotUninstalled"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SystemExtensionActivationDelegate *delegate =
|
||||
vcam->extensionDelegate;
|
||||
|
||||
if (!delegate.installed) {
|
||||
if (delegate.lastErrorMessage) {
|
||||
obs_output_set_last_error(
|
||||
vcam->output,
|
||||
[NSString
|
||||
stringWithFormat:
|
||||
@"%s\n\n%@",
|
||||
obs_module_text(
|
||||
"Error.SystemExtension.InstallationError"),
|
||||
delegate.lastErrorMessage]
|
||||
.UTF8String);
|
||||
} else {
|
||||
obs_output_set_last_error(
|
||||
vcam->output,
|
||||
obs_module_text(
|
||||
"Error.SystemExtension.NotInstalled"));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
bool success = false;
|
||||
if (dal_status == OBSDalPluginNotInstalled) {
|
||||
success = install_dal_plugin(false);
|
||||
} else if (dal_status == OBSDalPluginNeedsUpdate) {
|
||||
success = install_dal_plugin(true);
|
||||
} else {
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
obs_output_set_last_error(vcam->output,
|
||||
"Error.DAL.NotInstalled");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
obs_get_video_info(&vcam->videoInfo);
|
||||
|
||||
@ -205,13 +429,99 @@ static bool virtualcam_output_start(void *data)
|
||||
CVReturn status = CVPixelBufferPoolCreate(
|
||||
kCFAllocatorDefault, (__bridge CFDictionaryRef)pAttr,
|
||||
(__bridge CFDictionaryRef)pbAttr, &vcam->pool);
|
||||
|
||||
if (status != kCVReturnSuccess) {
|
||||
blog(LOG_ERROR,
|
||||
"unable to allocate pixel buffer pool (error %d)", status);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cmio_extension_supported()) {
|
||||
UInt32 size;
|
||||
UInt32 used;
|
||||
|
||||
CMIOObjectPropertyAddress address{
|
||||
.mSelector = kCMIOHardwarePropertyDevices,
|
||||
.mScope = kCMIOObjectPropertyScopeGlobal,
|
||||
.mElement = kCMIOObjectPropertyElementMain};
|
||||
CMIOObjectGetPropertyDataSize(kCMIOObjectSystemObject, &address,
|
||||
0, NULL, &size);
|
||||
size_t num_devices = size / sizeof(CMIOObjectID);
|
||||
CMIOObjectID cmio_devices[num_devices];
|
||||
CMIOObjectGetPropertyData(kCMIOObjectSystemObject, &address, 0,
|
||||
NULL, size, &used, &cmio_devices);
|
||||
|
||||
vcam->deviceID = 0;
|
||||
|
||||
NSString *OBSVirtualCamUUID = [[NSBundle
|
||||
bundleWithIdentifier:@"com.obsproject.mac-virtualcam"]
|
||||
objectForInfoDictionaryKey:@"OBSCameraDeviceUUID"];
|
||||
for (size_t i = 0; i < num_devices; i++) {
|
||||
CMIOObjectID cmio_device = cmio_devices[i];
|
||||
address.mSelector = kCMIODevicePropertyDeviceUID;
|
||||
|
||||
UInt32 device_name_size;
|
||||
CMIOObjectGetPropertyDataSize(cmio_device, &address, 0,
|
||||
NULL, &device_name_size);
|
||||
|
||||
CFStringRef uid;
|
||||
CMIOObjectGetPropertyData(cmio_device, &address, 0,
|
||||
NULL, device_name_size, &used,
|
||||
&uid);
|
||||
|
||||
const char *uid_string = CFStringGetCStringPtr(
|
||||
uid, kCFStringEncodingUTF8);
|
||||
if (uid_string &&
|
||||
strcmp(uid_string, OBSVirtualCamUUID.UTF8String) ==
|
||||
0) {
|
||||
vcam->deviceID = cmio_device;
|
||||
CFRelease(uid);
|
||||
break;
|
||||
} else {
|
||||
CFRelease(uid);
|
||||
}
|
||||
}
|
||||
|
||||
if (!vcam->deviceID) {
|
||||
obs_output_set_last_error(
|
||||
vcam->output,
|
||||
obs_module_text(
|
||||
"Error.SystemExtension.CameraUnavailable"));
|
||||
return false;
|
||||
}
|
||||
|
||||
address.mSelector = kCMIODevicePropertyStreams;
|
||||
CMIOObjectGetPropertyDataSize(vcam->deviceID, &address, 0, NULL,
|
||||
&size);
|
||||
CMIOStreamID stream_ids[(size / sizeof(CMIOStreamID))];
|
||||
|
||||
CMIOObjectGetPropertyData(vcam->deviceID, &address, 0, NULL,
|
||||
size, &used, &stream_ids);
|
||||
|
||||
vcam->streamID = stream_ids[1];
|
||||
|
||||
CMIOStreamCopyBufferQueue(
|
||||
vcam->streamID, [](CMIOStreamID, void *, void *) {},
|
||||
NULL, &vcam->queue);
|
||||
CMVideoFormatDescriptionCreate(kCFAllocatorDefault,
|
||||
video_format,
|
||||
vcam->videoInfo.output_width,
|
||||
vcam->videoInfo.output_height,
|
||||
NULL, &vcam->formatDescription);
|
||||
|
||||
OSStatus result =
|
||||
CMIODeviceStartStream(vcam->deviceID, vcam->streamID);
|
||||
|
||||
if (result != noErr) {
|
||||
obs_output_set_last_error(
|
||||
vcam->output,
|
||||
obs_module_text(
|
||||
"Error.SystemExtension.CameraNotStarted"));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
[vcam->machServer run];
|
||||
}
|
||||
|
||||
if (!obs_output_begin_data_capture(vcam->output, 0)) {
|
||||
return false;
|
||||
@ -227,9 +537,13 @@ static void virtualcam_output_stop(void *data, uint64_t ts)
|
||||
struct virtualcam_data *vcam = (struct virtualcam_data *)data;
|
||||
|
||||
obs_output_end_data_capture(vcam->output);
|
||||
[vcam->machServer stop];
|
||||
|
||||
if (cmio_extension_supported()) {
|
||||
CMIODeviceStopStream(vcam->deviceID, vcam->streamID);
|
||||
CFRelease(vcam->formatDescription);
|
||||
CVPixelBufferPoolRelease(vcam->pool);
|
||||
} else {
|
||||
[vcam->machServer stop];
|
||||
}
|
||||
}
|
||||
|
||||
static void virtualcam_output_raw_video(void *data, struct video_data *frame)
|
||||
@ -306,11 +620,23 @@ static void virtualcam_output_raw_video(void *data, struct video_data *frame)
|
||||
|
||||
CVPixelBufferUnlockBaseAddress(frameRef, 0);
|
||||
|
||||
if (cmio_extension_supported()) {
|
||||
CMSampleBufferRef sampleBuffer;
|
||||
CMSampleTimingInfo timingInfo{
|
||||
.presentationTimeStamp =
|
||||
CMTimeMake(frame->timestamp, NSEC_PER_SEC)};
|
||||
|
||||
OSStatus result = CMSampleBufferCreateForImageBuffer(
|
||||
kCFAllocatorDefault, frameRef, true, NULL, NULL,
|
||||
vcam->formatDescription, &timingInfo, &sampleBuffer);
|
||||
result = CMSimpleQueueEnqueue(vcam->queue, sampleBuffer);
|
||||
} else {
|
||||
// Share pixel buffer with clients
|
||||
[vcam->machServer sendPixelBuffer:frameRef
|
||||
timestamp:frame->timestamp
|
||||
fpsNumerator:vcam->videoInfo.fps_num
|
||||
fpsDenominator:vcam->videoInfo.fps_den];
|
||||
}
|
||||
|
||||
CVPixelBufferRelease(frameRef);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user