mac-virtualcam: Do not rely on global state

This change updates the implementation of the mac-virtualcam plugin to
not use any global state and instead rely on the state object that is
passed by the OBS module system.

This approach is similar to the virtual camera implementations for Linux
and Windows.
This commit is contained in:
Fabian Mastenbroek
2022-06-10 13:37:03 +02:00
parent 5edabfe7c1
commit db733032e0
@@ -1,5 +1,4 @@
#include <obs-module.h> #include <obs-module.h>
#include <AppKit/AppKit.h>
#include "OBSDALMachServer.h" #include "OBSDALMachServer.h"
#include "Defines.h" #include "Defines.h"
@@ -10,10 +9,12 @@ MODULE_EXPORT const char *obs_module_description(void)
return "macOS virtual webcam output"; return "macOS virtual webcam output";
} }
obs_output_t *outputRef; struct virtualcam_data {
obs_video_info videoInfo; obs_output_t *output;
CVPixelBufferPoolRef pool; obs_video_info videoInfo;
static OBSDALMachServer *sMachServer; CVPixelBufferPoolRef pool;
OBSDALMachServer *machServer;
};
static bool check_dal_plugin() static bool check_dal_plugin()
{ {
@@ -129,31 +130,29 @@ static const char *virtualcam_output_get_name(void *type_data)
return obs_module_text("Plugin_Name"); return obs_module_text("Plugin_Name");
} }
// This is a dummy pointer so we have something to return from virtualcam_output_create
static void *data = &data;
static void *virtualcam_output_create(obs_data_t *settings, static void *virtualcam_output_create(obs_data_t *settings,
obs_output_t *output) obs_output_t *output)
{ {
UNUSED_PARAMETER(settings); UNUSED_PARAMETER(settings);
outputRef = output; struct virtualcam_data *vcam =
(struct virtualcam_data *)bzalloc(sizeof(*vcam));
blog(LOG_DEBUG, "output_create"); vcam->output = output;
sMachServer = [[OBSDALMachServer alloc] init]; vcam->machServer = [[OBSDALMachServer alloc] init];
return data; return vcam;
} }
static void virtualcam_output_destroy(void *data) static void virtualcam_output_destroy(void *data)
{ {
UNUSED_PARAMETER(data); struct virtualcam_data *vcam = (struct virtualcam_data *)data;
blog(LOG_DEBUG, "output_destroy");
sMachServer = nil; vcam->machServer = nil;
} }
static bool virtualcam_output_start(void *data) static bool virtualcam_output_start(void *data)
{ {
UNUSED_PARAMETER(data); struct virtualcam_data *vcam = (struct virtualcam_data *)data;
bool hasDalPlugin = check_dal_plugin(); bool hasDalPlugin = check_dal_plugin();
@@ -161,24 +160,22 @@ static bool virtualcam_output_start(void *data)
return false; return false;
} }
blog(LOG_DEBUG, "output_start"); obs_get_video_info(&vcam->videoInfo);
obs_get_video_info(&videoInfo);
FourCharCode video_format = FourCharCode video_format =
convert_video_format_to_mac(videoInfo.output_format); convert_video_format_to_mac(vcam->videoInfo.output_format);
if (!video_format) { if (!video_format) {
// Selected output format is not supported natively by CoreVideo, CPU conversion necessary // Selected output format is not supported natively by CoreVideo, CPU conversion necessary
blog(LOG_WARNING, blog(LOG_WARNING,
"Selected output format (%s) not supported by CoreVideo, enabling CPU transcoding...", "Selected output format (%s) not supported by CoreVideo, enabling CPU transcoding...",
get_video_format_name(videoInfo.output_format)); get_video_format_name(vcam->videoInfo.output_format));
struct video_scale_info conversion = {}; struct video_scale_info conversion = {};
conversion.format = VIDEO_FORMAT_NV12; conversion.format = VIDEO_FORMAT_NV12;
conversion.width = videoInfo.output_width; conversion.width = vcam->videoInfo.output_width;
conversion.height = videoInfo.output_height; conversion.height = vcam->videoInfo.output_height;
obs_output_set_video_conversion(outputRef, &conversion); obs_output_set_video_conversion(vcam->output, &conversion);
video_format = convert_video_format_to_mac(conversion.format); video_format = convert_video_format_to_mac(conversion.format);
} }
@@ -186,22 +183,22 @@ static bool virtualcam_output_start(void *data)
NSDictionary *pAttr = @{}; NSDictionary *pAttr = @{};
NSDictionary *pbAttr = @{ NSDictionary *pbAttr = @{
(id)kCVPixelBufferPixelFormatTypeKey: @(video_format), (id)kCVPixelBufferPixelFormatTypeKey: @(video_format),
(id)kCVPixelBufferWidthKey: @(videoInfo.output_width), (id)kCVPixelBufferWidthKey: @(vcam->videoInfo.output_width),
(id)kCVPixelBufferHeightKey: @(videoInfo.output_height), (id)kCVPixelBufferHeightKey: @(vcam->videoInfo.output_height),
(id)kCVPixelBufferIOSurfacePropertiesKey: @{} (id)kCVPixelBufferIOSurfacePropertiesKey: @{}
}; };
CVReturn status = CVPixelBufferPoolCreate( CVReturn status = CVPixelBufferPoolCreate(
kCFAllocatorDefault, (__bridge CFDictionaryRef)pAttr, kCFAllocatorDefault, (__bridge CFDictionaryRef)pAttr,
(__bridge CFDictionaryRef)pbAttr, &pool); (__bridge CFDictionaryRef)pbAttr, &vcam->pool);
if (status != kCVReturnSuccess) { if (status != kCVReturnSuccess) {
blog(LOG_ERROR, blog(LOG_ERROR,
"unable to allocate pixel buffer pool (error %d)", status); "unable to allocate pixel buffer pool (error %d)", status);
return false; return false;
} }
[sMachServer run]; [vcam->machServer run];
if (!obs_output_begin_data_capture(outputRef, 0)) { if (!obs_output_begin_data_capture(vcam->output, 0)) {
return false; return false;
} }
@@ -210,23 +207,23 @@ static bool virtualcam_output_start(void *data)
static void virtualcam_output_stop(void *data, uint64_t ts) static void virtualcam_output_stop(void *data, uint64_t ts)
{ {
UNUSED_PARAMETER(data);
UNUSED_PARAMETER(ts); UNUSED_PARAMETER(ts);
blog(LOG_DEBUG, "output_stop"); struct virtualcam_data *vcam = (struct virtualcam_data *)data;
obs_output_end_data_capture(outputRef);
[sMachServer stop];
CVPixelBufferPoolRelease(pool); obs_output_end_data_capture(vcam->output);
[vcam->machServer stop];
CVPixelBufferPoolRelease(vcam->pool);
} }
static void virtualcam_output_raw_video(void *data, struct video_data *frame) static void virtualcam_output_raw_video(void *data, struct video_data *frame)
{ {
UNUSED_PARAMETER(data); struct virtualcam_data *vcam = (struct virtualcam_data *)data;
CVPixelBufferRef frameRef = NULL; CVPixelBufferRef frameRef = nil;
CVReturn status = CVPixelBufferPoolCreatePixelBuffer( CVReturn status = CVPixelBufferPoolCreatePixelBuffer(
kCFAllocatorDefault, pool, &frameRef); kCFAllocatorDefault, vcam->pool, &frameRef);
if (status != kCVReturnSuccess) { if (status != kCVReturnSuccess) {
blog(LOG_ERROR, "unable to allocate pixel buffer (error %d)", blog(LOG_ERROR, "unable to allocate pixel buffer (error %d)",
@@ -295,10 +292,10 @@ static void virtualcam_output_raw_video(void *data, struct video_data *frame)
CVPixelBufferUnlockBaseAddress(frameRef, 0); CVPixelBufferUnlockBaseAddress(frameRef, 0);
// Share pixel buffer with clients // Share pixel buffer with clients
[sMachServer sendPixelBuffer:frameRef [vcam->machServer sendPixelBuffer:frameRef
timestamp:frame->timestamp timestamp:frame->timestamp
fpsNumerator:videoInfo.fps_num fpsNumerator:vcam->videoInfo.fps_num
fpsDenominator:videoInfo.fps_den]; fpsDenominator:vcam->videoInfo.fps_den];
CVPixelBufferRelease(frameRef); CVPixelBufferRelease(frameRef);
} }