clang-format: Update formatting of all ObjC and ObjC++ files

This commit is contained in:
PatTheMav
2023-06-11 14:56:45 +02:00
committed by Patrick Heyer
parent db7491083c
commit f8e00d6071
36 changed files with 6822 additions and 7956 deletions
File diff suppressed because it is too large Load Diff
+68 -88
View File
@@ -35,8 +35,7 @@ typedef struct CF_BRIDGED_TYPE(id) CGDisplayStream *CGDisplayStreamRef;
regions were merely copied from one place to another. A routine is provided to merge two update refs together in cases
where apps need to coalesce the values because they decided to skip processing for one or more frames.
*/
typedef const struct CF_BRIDGED_TYPE(id)
CGDisplayStreamUpdate *CGDisplayStreamUpdateRef;
typedef const struct CF_BRIDGED_TYPE(id) CGDisplayStreamUpdate *CGDisplayStreamUpdateRef;
/*!
@enum CGDisplayStreamUpdateRectType
@@ -47,10 +46,10 @@ typedef const struct CF_BRIDGED_TYPE(id)
@const kCGDisplayStreamUpdateReducedDirtyRects A possibly simplified (but overstated) array of dirty rectangles
*/
typedef CF_ENUM(int32_t, CGDisplayStreamUpdateRectType) {
kCGDisplayStreamUpdateRefreshedRects,
kCGDisplayStreamUpdateMovedRects,
kCGDisplayStreamUpdateDirtyRects,
kCGDisplayStreamUpdateReducedDirtyRects,
kCGDisplayStreamUpdateRefreshedRects,
kCGDisplayStreamUpdateMovedRects,
kCGDisplayStreamUpdateDirtyRects,
kCGDisplayStreamUpdateReducedDirtyRects,
};
/*!
@@ -62,10 +61,10 @@ typedef CF_ENUM(int32_t, CGDisplayStreamUpdateRectType) {
@const kCGDisplayStreamFrameStatusStopped The display stream has stopped and no more calls will be made to the handler until the stream is started.
*/
typedef CF_ENUM(int32_t, CGDisplayStreamFrameStatus) {
kCGDisplayStreamFrameStatusFrameComplete,
kCGDisplayStreamFrameStatusFrameIdle,
kCGDisplayStreamFrameStatusFrameBlank,
kCGDisplayStreamFrameStatusStopped,
kCGDisplayStreamFrameStatusFrameComplete,
kCGDisplayStreamFrameStatusFrameIdle,
kCGDisplayStreamFrameStatusFrameBlank,
kCGDisplayStreamFrameStatusStopped,
};
/*
@@ -84,20 +83,18 @@ typedef CF_ENUM(int32_t, CGDisplayStreamFrameStatus) {
of the handler, you must CFRetain() it, as it will be CFRelease()'d by the CGDisplayStream after the handler returns. The updateRef will be NULL in cases
when status is not kCGDisplayStreamFrameStatusFrameComplete.
*/
typedef void (^CGDisplayStreamFrameAvailableHandler)(
CGDisplayStreamFrameStatus status, uint64_t displayTime,
IOSurfaceRef __nullable frameSurface,
CGDisplayStreamUpdateRef __nullable updateRef);
typedef void (^CGDisplayStreamFrameAvailableHandler)(CGDisplayStreamFrameStatus status, uint64_t displayTime,
IOSurfaceRef __nullable frameSurface,
CGDisplayStreamUpdateRef __nullable updateRef);
/*!
@function CGDisplayStreamUpdateGetTypeID
@abstract Returns the CF "class" ID for CGDisplayStreamUpdate
@result The CFTypeID of the CGDisplayStreamUpdate class.
*/
CG_EXTERN CFTypeID
CGDisplayStreamUpdateGetTypeID(void) CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"There is no direct replacement for this function. Please use ScreenCaptureKit API's SCStreamOutputType instead");
CG_EXTERN CFTypeID CGDisplayStreamUpdateGetTypeID(void) CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"There is no direct replacement for this function. Please use ScreenCaptureKit API's SCStreamOutputType instead");
/*!
@function CGDisplayStreamUpdateGetRects
@@ -107,12 +104,11 @@ CGDisplayStreamUpdateGetTypeID(void) CG_AVAILABLE_BUT_DEPRECATED(
@param rectCount A pointer to where the count of the number of rectangles in the array is to be returned. Must not be NULL.
@result A pointer to the array of CGRectangles. This array should not be freed by the caller.
*/
CG_EXTERN const CGRect *__nullable CGDisplayStreamUpdateGetRects(
CGDisplayStreamUpdateRef __nullable updateRef,
CGDisplayStreamUpdateRectType rectType, size_t *rectCount)
CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"Please use ScreenCaptureKit API's SCStreamFrameInfo with SCStreamFrameInfoContentRect instead");
CG_EXTERN const CGRect *__nullable CGDisplayStreamUpdateGetRects(CGDisplayStreamUpdateRef __nullable updateRef,
CGDisplayStreamUpdateRectType rectType,
size_t *rectCount)
CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0, "Please use ScreenCaptureKit API's SCStreamFrameInfo with SCStreamFrameInfoContentRect instead");
/*!
@function CGDisplayStreamUpdateCreateMerged
@@ -126,13 +122,11 @@ CG_EXTERN const CGRect *__nullable CGDisplayStreamUpdateGetRects(
@param secondUpdate The second update (in a temporal sense)
@result The new CGDisplayStreamUpdateRef
*/
CG_EXTERN CGDisplayStreamUpdateRef __nullable
CGDisplayStreamUpdateCreateMergedUpdate(
CGDisplayStreamUpdateRef __nullable firstUpdate,
CGDisplayStreamUpdateRef __nullable secondUpdate)
CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"There is no direct replacement for this function. Please use ScreenCaptureKit API's SCStreamFrameInfo to replace CGDisplayStreamUpdate");
CG_EXTERN CGDisplayStreamUpdateRef __nullable CGDisplayStreamUpdateCreateMergedUpdate(
CGDisplayStreamUpdateRef __nullable firstUpdate, CGDisplayStreamUpdateRef __nullable secondUpdate)
CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"There is no direct replacement for this function. Please use ScreenCaptureKit API's SCStreamFrameInfo to replace CGDisplayStreamUpdate");
/*!
@function CGDisplayStreamUpdateGetMovedRectsDelta
@@ -142,11 +136,10 @@ CGDisplayStreamUpdateCreateMergedUpdate(
@param dy A pointer to a CGFloat to store the y component of the movement delta
@discussion The delta values describe the offset from the moved rectangles back to the source location.
*/
CG_EXTERN void CGDisplayStreamUpdateGetMovedRectsDelta(
CGDisplayStreamUpdateRef __nullable updateRef, CGFloat *dx, CGFloat *dy)
CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"Please use ScreenCaptureKit API's SCStreamFrameInfo with SCStreamFrameInfoContentRect instead");
CG_EXTERN void CGDisplayStreamUpdateGetMovedRectsDelta(CGDisplayStreamUpdateRef __nullable updateRef, CGFloat *dx,
CGFloat *dy)
CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0, "Please use ScreenCaptureKit API's SCStreamFrameInfo with SCStreamFrameInfoContentRect instead");
/*!
@function CGDisplayStreamGetDropCount
@@ -156,11 +149,9 @@ CG_EXTERN void CGDisplayStreamUpdateGetMovedRectsDelta(
@discussion This call is primarily useful for performance measurement to determine if the client is keeping up with
all WindowServer updates.
*/
CG_EXTERN size_t
CGDisplayStreamUpdateGetDropCount(CGDisplayStreamUpdateRef __nullable updateRef)
CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"There is no direct replacement for this function. Please use ScreenCaptureKit API's SCStreamFrameInfo to replace CGDisplayStreamUpdate");
CG_EXTERN size_t CGDisplayStreamUpdateGetDropCount(CGDisplayStreamUpdateRef __nullable updateRef) CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"There is no direct replacement for this function. Please use ScreenCaptureKit API's SCStreamFrameInfo to replace CGDisplayStreamUpdate");
/* Optional CGDisplayStream Properties */
@@ -172,8 +163,8 @@ CGDisplayStreamUpdateGetDropCount(CGDisplayStreamUpdateRef __nullable updateRef)
HiDPI displays.
*/
CG_EXTERN const CFStringRef kCGDisplayStreamSourceRect CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"Please use ScreenCaptureKit API's SCStreamConfiguration sourceRect property instead"); /* Source rectangle to capture - defaults to entire display */
11.0, 14.0,
"Please use ScreenCaptureKit API's SCStreamConfiguration sourceRect property instead"); /* Source rectangle to capture - defaults to entire display */
/*!
@const kCGDisplayStreamDestinationRect
@@ -183,8 +174,8 @@ CG_EXTERN const CFStringRef kCGDisplayStreamSourceRect CG_AVAILABLE_BUT_DEPRECAT
specified in terms of pixels.
*/
CG_EXTERN const CFStringRef kCGDisplayStreamDestinationRect CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"Please use ScreenCaptureKit API's SCStreamConfiguration destinationRect property instead"); /* Destination rectangle - defaults to entire buffer */
11.0, 14.0,
"Please use ScreenCaptureKit API's SCStreamConfiguration destinationRect property instead"); /* Destination rectangle - defaults to entire buffer */
/*!
@const kCGDisplayStreamPreserveAspectRatio
@@ -194,40 +185,40 @@ CG_EXTERN const CFStringRef kCGDisplayStreamDestinationRect CG_AVAILABLE_BUT_DEP
in order to preserve the source aspect ratio.
*/
CG_EXTERN const CFStringRef kCGDisplayStreamPreserveAspectRatio CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"Please use ScreenCaptureKit API's SCStreamConfiguration preserveAspectRatio property instead"); /* CFBoolean - defaults to true */
11.0, 14.0,
"Please use ScreenCaptureKit API's SCStreamConfiguration preserveAspectRatio property instead"); /* CFBoolean - defaults to true */
/*!
@const kCGDisplayStreamColorSpace
@discussion Set the desired CGColorSpace of the output frames. By default the color space will be that of the display.
*/
CG_EXTERN const CFStringRef kCGDisplayStreamColorSpace CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"Please use ScreenCaptureKit API's SCStreamConfiguration colorSpaceName property instead"); /* Desired output color space (CGColorSpaceRef) - defaults to display color space */
11.0, 14.0,
"Please use ScreenCaptureKit API's SCStreamConfiguration colorSpaceName property instead"); /* Desired output color space (CGColorSpaceRef) - defaults to display color space */
/*!
@const kCGDisplayStreamMinimumFrameTime
@discussion Request that the delta between frame updates be at least as much specified by this value.
*/
CG_EXTERN const CFStringRef kCGDisplayStreamMinimumFrameTime CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"Please use ScreenCaptureKit API's SCStreamConfiguration minimumFrameInterval property instead"); /* CFNumber in seconds, defaults to zero. */
11.0, 14.0,
"Please use ScreenCaptureKit API's SCStreamConfiguration minimumFrameInterval property instead"); /* CFNumber in seconds, defaults to zero. */
/*!
@const kCGDisplayStreamShowCursor
@discussion Controls whether the cursor is embedded within the provided buffers or not.
*/
CG_EXTERN const CFStringRef kCGDisplayStreamShowCursor CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"Please use ScreenCaptureKit API's SCStreamConfiguration showsCursor property instead"); /* CFBoolean - defaults to false */
11.0, 14.0,
"Please use ScreenCaptureKit API's SCStreamConfiguration showsCursor property instead"); /* CFBoolean - defaults to false */
/*!
@const kCGDisplayStreamQueueDepth
@discussion Controls how many frames deep the frame queue will be. Defaults to N.
*/
CG_EXTERN const CFStringRef kCGDisplayStreamQueueDepth CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"Please use ScreenCaptureKit API's SCStreamConfiguration queueDepth property instead"); /* Queue depth in frames. Defaults to 3. */
11.0, 14.0,
"Please use ScreenCaptureKit API's SCStreamConfiguration queueDepth property instead"); /* Queue depth in frames. Defaults to 3. */
/*!
@const kCGDisplayStreamYCbCrMatrix
@@ -235,8 +226,7 @@ CG_EXTERN const CFStringRef kCGDisplayStreamQueueDepth CG_AVAILABLE_BUT_DEPRECAT
The value should be one of the three kCGDisplayStreamYCbCrMatrix values specified below.
*/
CG_EXTERN const CFStringRef kCGDisplayStreamYCbCrMatrix CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"Please use ScreenCaptureKit API's SCStreamConfiguration colorMatrix property");
11.0, 14.0, "Please use ScreenCaptureKit API's SCStreamConfiguration colorMatrix property");
/* Supported YCbCr matrices. Note that these strings have identical values to the equivalent CoreVideo strings. */
CG_EXTERN const CFStringRef kCGDisplayStreamYCbCrMatrix_ITU_R_709_2;
@@ -249,8 +239,8 @@ CG_EXTERN const CFStringRef kCGDisplayStreamYCbCrMatrix_SMPTE_240M_1995;
@result The CFTypeID of the CGDisplayStream class.
*/
CG_EXTERN CFTypeID CGDisplayStreamGetTypeID(void) CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"There is no direct replacement for this function. Please use ScreenCaptureKit API's SCStream to replace CGDisplayStream");
11.0, 14.0,
"There is no direct replacement for this function. Please use ScreenCaptureKit API's SCStream to replace CGDisplayStream");
/*!
@function CGDisplayStreamCreate
@@ -272,13 +262,12 @@ CG_EXTERN CFTypeID CGDisplayStreamGetTypeID(void) CG_AVAILABLE_BUT_DEPRECATED(
@param handler A block that will be called for frame deliver.
@result The new CGDisplayStream object.
*/
CG_EXTERN CGDisplayStreamRef __nullable CGDisplayStreamCreate(
CGDirectDisplayID display, size_t outputWidth, size_t outputHeight,
int32_t pixelFormat, CFDictionaryRef __nullable properties,
CGDisplayStreamFrameAvailableHandler __nullable handler)
CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"Please use ScreenCaptureKit API's initWithFilter:configuration:delegate: instead");
CG_EXTERN CGDisplayStreamRef __nullable CGDisplayStreamCreate(CGDirectDisplayID display, size_t outputWidth,
size_t outputHeight, int32_t pixelFormat,
CFDictionaryRef __nullable properties,
CGDisplayStreamFrameAvailableHandler __nullable handler)
CG_AVAILABLE_BUT_DEPRECATED(11.0, 14.0,
"Please use ScreenCaptureKit API's initWithFilter:configuration:delegate: instead");
/*!
@function CGDisplayStreamCreateWithDispatchQueue
@@ -295,13 +284,11 @@ CG_EXTERN CGDisplayStreamRef __nullable CGDisplayStreamCreate(
@result The new CGDisplayStream object.
*/
CG_EXTERN CGDisplayStreamRef __nullable CGDisplayStreamCreateWithDispatchQueue(
CGDirectDisplayID display, size_t outputWidth, size_t outputHeight,
int32_t pixelFormat, CFDictionaryRef __nullable properties,
dispatch_queue_t queue,
CGDisplayStreamFrameAvailableHandler __nullable handler)
CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"Please use ScreenCaptureKit API's initWithFilter:configuration:delegate: instead");
CGDirectDisplayID display, size_t outputWidth, size_t outputHeight, int32_t pixelFormat,
CFDictionaryRef __nullable properties, dispatch_queue_t queue,
CGDisplayStreamFrameAvailableHandler __nullable handler)
CG_AVAILABLE_BUT_DEPRECATED(11.0, 14.0,
"Please use ScreenCaptureKit API's initWithFilter:configuration:delegate: instead");
/*!
@function CGDisplayStreamStart
@@ -309,11 +296,8 @@ CG_EXTERN CGDisplayStreamRef __nullable CGDisplayStreamCreateWithDispatchQueue(
@param displayStream to be started
@result kCGErrorSuccess If the display stream was started, otherwise an error.
*/
CG_EXTERN CGError
CGDisplayStreamStart(CGDisplayStreamRef cg_nullable displayStream)
CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"Please use ScreenCaptureKit API's startCaptureWithCompletionHandler: to start a stream instead");
CG_EXTERN CGError CGDisplayStreamStart(CGDisplayStreamRef cg_nullable displayStream) CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0, "Please use ScreenCaptureKit API's startCaptureWithCompletionHandler: to start a stream instead");
/*!
@function CGDisplayStreamStop
@@ -324,11 +308,8 @@ CGDisplayStreamStart(CGDisplayStreamRef cg_nullable displayStream)
status of kCGDisplayStreamFrameStatusStopped. After that point it is safe to release the CGDisplayStream.
It is safe to call this function from within the handler block, but the previous caveat still applies.
*/
CG_EXTERN CGError
CGDisplayStreamStop(CGDisplayStreamRef cg_nullable displayStream)
CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"Please use ScreenCaptureKit API's stopCaptureWithCompletionHandler: to stop a stream instead");
CG_EXTERN CGError CGDisplayStreamStop(CGDisplayStreamRef cg_nullable displayStream) CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0, "Please use ScreenCaptureKit API's stopCaptureWithCompletionHandler: to stop a stream instead");
/*!
@function CGDisplayStreamGetRunLoopSource
@@ -337,11 +318,10 @@ CGDisplayStreamStop(CGDisplayStreamRef cg_nullable displayStream)
@result The CFRunLoopSource for this displayStream. Note: This function will return NULL if the
display stream was created via CGDisplayStreamCreateWithDispatchQueue().
*/
CG_EXTERN CFRunLoopSourceRef __nullable
CGDisplayStreamGetRunLoopSource(CGDisplayStreamRef cg_nullable displayStream)
CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"There is no direct replacement for this function. Please use ScreenCaptureKit API's SCStream to replace CGDisplayStream");
CG_EXTERN CFRunLoopSourceRef __nullable CGDisplayStreamGetRunLoopSource(CGDisplayStreamRef cg_nullable displayStream)
CG_AVAILABLE_BUT_DEPRECATED(
11.0, 14.0,
"There is no direct replacement for this function. Please use ScreenCaptureKit API's SCStream to replace CGDisplayStream");
#endif /* __BLOCKS__ */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+117 -128
View File
@@ -9,226 +9,215 @@
#include "window-utils.h"
struct window_capture {
obs_source_t *source;
obs_source_t *source;
struct cocoa_window window;
struct cocoa_window window;
//CGRect bounds;
//CGWindowListOption window_option;
CGWindowImageOption image_option;
//CGRect bounds;
//CGWindowListOption window_option;
CGWindowImageOption image_option;
CGColorSpaceRef color_space;
CGColorSpaceRef color_space;
DARRAY(uint8_t) buffer;
DARRAY(uint8_t) buffer;
pthread_t capture_thread;
os_event_t *capture_event;
os_event_t *stop_event;
pthread_t capture_thread;
os_event_t *capture_event;
os_event_t *stop_event;
};
static CGImageRef get_image(struct window_capture *wc)
{
NSArray *arr = (NSArray *)CGWindowListCreate(
kCGWindowListOptionIncludingWindow, wc->window.window_id);
[arr autorelease];
NSArray *arr = (NSArray *) CGWindowListCreate(kCGWindowListOptionIncludingWindow, wc->window.window_id);
[arr autorelease];
if (!arr.count && !find_window(&wc->window, NULL, false))
return NULL;
if (!arr.count && !find_window(&wc->window, NULL, false))
return NULL;
return CGWindowListCreateImage(CGRectNull,
kCGWindowListOptionIncludingWindow,
wc->window.window_id, wc->image_option);
return CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, wc->window.window_id,
wc->image_option);
}
static inline void capture_frame(struct window_capture *wc)
{
uint64_t ts = os_gettime_ns();
CGImageRef img = get_image(wc);
if (!img)
return;
uint64_t ts = os_gettime_ns();
CGImageRef img = get_image(wc);
if (!img)
return;
size_t width = CGImageGetWidth(img);
size_t height = CGImageGetHeight(img);
size_t width = CGImageGetWidth(img);
size_t height = CGImageGetHeight(img);
if (!width || !height || CGImageGetBitsPerPixel(img) != 32 ||
CGImageGetBitsPerComponent(img) != 8) {
CGImageRelease(img);
return;
}
if (!width || !height || CGImageGetBitsPerPixel(img) != 32 || CGImageGetBitsPerComponent(img) != 8) {
CGImageRelease(img);
return;
}
CGDataProviderRef provider = CGImageGetDataProvider(img);
CFDataRef data = CGDataProviderCopyData(provider);
CGDataProviderRef provider = CGImageGetDataProvider(img);
CFDataRef data = CGDataProviderCopyData(provider);
struct obs_source_frame frame = {
.format = VIDEO_FORMAT_BGRA,
.width = (uint32_t)width,
.height = (uint32_t)height,
.data[0] = (uint8_t *)CFDataGetBytePtr(data),
.linesize[0] = (uint32_t)CGImageGetBytesPerRow(img),
.timestamp = ts,
};
struct obs_source_frame frame = {
.format = VIDEO_FORMAT_BGRA,
.width = (uint32_t) width,
.height = (uint32_t) height,
.data[0] = (uint8_t *) CFDataGetBytePtr(data),
.linesize[0] = (uint32_t) CGImageGetBytesPerRow(img),
.timestamp = ts,
};
obs_source_output_video(wc->source, &frame);
obs_source_output_video(wc->source, &frame);
CGImageRelease(img);
CFRelease(data);
CGImageRelease(img);
CFRelease(data);
}
static void *capture_thread(void *data)
{
struct window_capture *wc = data;
struct window_capture *wc = data;
for (;;) {
os_event_wait(wc->capture_event);
if (os_event_try(wc->stop_event) != EAGAIN)
break;
for (;;) {
os_event_wait(wc->capture_event);
if (os_event_try(wc->stop_event) != EAGAIN)
break;
@autoreleasepool {
capture_frame(wc);
}
}
@autoreleasepool {
capture_frame(wc);
}
}
return NULL;
return NULL;
}
static inline void *window_capture_create_internal(obs_data_t *settings,
obs_source_t *source)
static inline void *window_capture_create_internal(obs_data_t *settings, obs_source_t *source)
{
struct window_capture *wc = bzalloc(sizeof(struct window_capture));
struct window_capture *wc = bzalloc(sizeof(struct window_capture));
wc->source = source;
wc->source = source;
wc->color_space = CGColorSpaceCreateDeviceRGB();
wc->color_space = CGColorSpaceCreateDeviceRGB();
da_init(wc->buffer);
da_init(wc->buffer);
init_window(&wc->window, settings);
init_window(&wc->window, settings);
wc->image_option = obs_data_get_bool(settings, "show_shadow")
? kCGWindowImageDefault
: kCGWindowImageBoundsIgnoreFraming;
wc->image_option = obs_data_get_bool(settings, "show_shadow") ? kCGWindowImageDefault
: kCGWindowImageBoundsIgnoreFraming;
os_event_init(&wc->capture_event, OS_EVENT_TYPE_AUTO);
os_event_init(&wc->stop_event, OS_EVENT_TYPE_MANUAL);
os_event_init(&wc->capture_event, OS_EVENT_TYPE_AUTO);
os_event_init(&wc->stop_event, OS_EVENT_TYPE_MANUAL);
pthread_create(&wc->capture_thread, NULL, capture_thread, wc);
pthread_create(&wc->capture_thread, NULL, capture_thread, wc);
return wc;
return wc;
}
static void *window_capture_create(obs_data_t *settings, obs_source_t *source)
{
@autoreleasepool {
return window_capture_create_internal(settings, source);
}
@autoreleasepool {
return window_capture_create_internal(settings, source);
}
}
static void window_capture_destroy(void *data)
{
struct window_capture *cap = data;
struct window_capture *cap = data;
os_event_signal(cap->stop_event);
os_event_signal(cap->capture_event);
os_event_signal(cap->stop_event);
os_event_signal(cap->capture_event);
pthread_join(cap->capture_thread, NULL);
pthread_join(cap->capture_thread, NULL);
CGColorSpaceRelease(cap->color_space);
CGColorSpaceRelease(cap->color_space);
da_free(cap->buffer);
da_free(cap->buffer);
os_event_destroy(cap->capture_event);
os_event_destroy(cap->stop_event);
os_event_destroy(cap->capture_event);
os_event_destroy(cap->stop_event);
destroy_window(&cap->window);
destroy_window(&cap->window);
bfree(cap);
bfree(cap);
}
static void window_capture_defaults(obs_data_t *settings)
{
obs_data_set_default_bool(settings, "show_shadow", false);
window_defaults(settings);
obs_data_set_default_bool(settings, "show_shadow", false);
window_defaults(settings);
}
static obs_properties_t *window_capture_properties(void *unused)
{
UNUSED_PARAMETER(unused);
UNUSED_PARAMETER(unused);
obs_properties_t *props = obs_properties_create();
obs_properties_t *props = obs_properties_create();
add_window_properties(props);
add_window_properties(props);
obs_properties_add_bool(props, "show_shadow",
obs_module_text("WindowCapture.ShowShadow"));
obs_properties_add_bool(props, "show_shadow", obs_module_text("WindowCapture.ShowShadow"));
return props;
return props;
}
static inline void window_capture_update_internal(struct window_capture *wc,
obs_data_t *settings)
static inline void window_capture_update_internal(struct window_capture *wc, obs_data_t *settings)
{
wc->image_option = obs_data_get_bool(settings, "show_shadow")
? kCGWindowImageDefault
: kCGWindowImageBoundsIgnoreFraming;
wc->image_option = obs_data_get_bool(settings, "show_shadow") ? kCGWindowImageDefault
: kCGWindowImageBoundsIgnoreFraming;
update_window(&wc->window, settings);
update_window(&wc->window, settings);
if (wc->window.window_name.length) {
blog(LOG_INFO,
"[window-capture: '%s'] update settings:\n"
"\twindow: %s\n"
"\towner: %s",
obs_source_get_name(wc->source),
[wc->window.window_name UTF8String],
[wc->window.owner_name UTF8String]);
}
if (wc->window.window_name.length) {
blog(LOG_INFO,
"[window-capture: '%s'] update settings:\n"
"\twindow: %s\n"
"\towner: %s",
obs_source_get_name(wc->source), [wc->window.window_name UTF8String], [wc->window.owner_name UTF8String]);
}
}
static void window_capture_update(void *data, obs_data_t *settings)
{
@autoreleasepool {
return window_capture_update_internal(data, settings);
}
@autoreleasepool {
return window_capture_update_internal(data, settings);
}
}
static const char *window_capture_getname(void *unused)
{
UNUSED_PARAMETER(unused);
return obs_module_text("WindowCapture");
UNUSED_PARAMETER(unused);
return obs_module_text("WindowCapture");
}
static inline void window_capture_tick_internal(struct window_capture *wc,
float seconds)
static inline void window_capture_tick_internal(struct window_capture *wc, float seconds)
{
UNUSED_PARAMETER(seconds);
os_event_signal(wc->capture_event);
UNUSED_PARAMETER(seconds);
os_event_signal(wc->capture_event);
}
static void window_capture_tick(void *data, float seconds)
{
struct window_capture *wc = data;
struct window_capture *wc = data;
if (!obs_source_showing(wc->source))
return;
if (!obs_source_showing(wc->source))
return;
@autoreleasepool {
return window_capture_tick_internal(data, seconds);
}
@autoreleasepool {
return window_capture_tick_internal(data, seconds);
}
}
struct obs_source_info window_capture_info = {
.id = "window_capture",
.type = OBS_SOURCE_TYPE_INPUT,
.get_name = window_capture_getname,
.id = "window_capture",
.type = OBS_SOURCE_TYPE_INPUT,
.get_name = window_capture_getname,
.create = window_capture_create,
.destroy = window_capture_destroy,
.create = window_capture_create,
.destroy = window_capture_destroy,
.output_flags = OBS_SOURCE_ASYNC_VIDEO,
.video_tick = window_capture_tick,
.output_flags = OBS_SOURCE_ASYNC_VIDEO,
.video_tick = window_capture_tick,
.get_defaults = window_capture_defaults,
.get_properties = window_capture_properties,
.update = window_capture_update,
.icon_type = OBS_ICON_TYPE_WINDOW_CAPTURE,
.get_defaults = window_capture_defaults,
.get_properties = window_capture_properties,
.update = window_capture_update,
.icon_type = OBS_ICON_TYPE_WINDOW_CAPTURE,
};
+6 -6
View File
@@ -5,14 +5,14 @@
#include <obs-module.h>
struct cocoa_window {
CGWindowID window_id;
int owner_pid;
CGWindowID window_id;
int owner_pid;
pthread_mutex_t name_lock;
NSString *owner_name;
NSString *window_name;
pthread_mutex_t name_lock;
NSString *owner_name;
NSString *window_name;
uint64_t next_search_time;
uint64_t next_search_time;
};
typedef struct cocoa_window *cocoa_window_t;
+144 -162
View File
@@ -2,35 +2,34 @@
#include <util/platform.h>
#define WINDOW_NAME ((NSString *)kCGWindowName)
#define WINDOW_NUMBER ((NSString *)kCGWindowNumber)
#define OWNER_NAME ((NSString *)kCGWindowOwnerName)
#define OWNER_PID ((NSNumber *)kCGWindowOwnerPID)
#define WINDOW_NAME ((NSString *) kCGWindowName)
#define WINDOW_NUMBER ((NSString *) kCGWindowNumber)
#define OWNER_NAME ((NSString *) kCGWindowOwnerName)
#define OWNER_PID ((NSNumber *) kCGWindowOwnerPID)
static NSComparator win_info_cmp = ^(NSDictionary *o1, NSDictionary *o2) {
NSComparisonResult res = [o1[OWNER_NAME] compare:o2[OWNER_NAME]];
if (res != NSOrderedSame)
return res;
NSComparisonResult res = [o1[OWNER_NAME] compare:o2[OWNER_NAME]];
if (res != NSOrderedSame)
return res;
res = [o1[OWNER_PID] compare:o2[OWNER_PID]];
if (res != NSOrderedSame)
return res;
res = [o1[OWNER_PID] compare:o2[OWNER_PID]];
if (res != NSOrderedSame)
return res;
res = [o1[WINDOW_NAME] compare:o2[WINDOW_NAME]];
if (res != NSOrderedSame)
return res;
res = [o1[WINDOW_NAME] compare:o2[WINDOW_NAME]];
if (res != NSOrderedSame)
return res;
return [o1[WINDOW_NUMBER] compare:o2[WINDOW_NUMBER]];
return [o1[WINDOW_NUMBER] compare:o2[WINDOW_NUMBER]];
};
NSArray *enumerate_windows(void)
{
NSArray *arr = (NSArray *)CGWindowListCopyWindowInfo(
kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
NSArray *arr = (NSArray *) CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
[arr autorelease];
[arr autorelease];
return [arr sortedArrayUsingComparator:win_info_cmp];
return [arr sortedArrayUsingComparator:win_info_cmp];
}
#define WAIT_TIME_MS 500
@@ -39,227 +38,210 @@ NSArray *enumerate_windows(void)
bool find_window(cocoa_window_t cw, obs_data_t *settings, bool force)
{
if (!force && cw->next_search_time > os_gettime_ns())
return false;
if (!force && cw->next_search_time > os_gettime_ns())
return false;
cw->next_search_time = os_gettime_ns() + WAIT_TIME_NS;
cw->next_search_time = os_gettime_ns() + WAIT_TIME_NS;
pthread_mutex_lock(&cw->name_lock);
pthread_mutex_lock(&cw->name_lock);
if (!cw->window_name.length && !cw->owner_name.length)
goto invalid_name;
if (!cw->window_name.length && !cw->owner_name.length)
goto invalid_name;
for (NSDictionary *dict in enumerate_windows()) {
if (![cw->owner_name isEqualToString:dict[OWNER_NAME]])
continue;
for (NSDictionary *dict in enumerate_windows()) {
if (![cw->owner_name isEqualToString:dict[OWNER_NAME]])
continue;
if (![cw->window_name isEqualToString:dict[WINDOW_NAME]])
continue;
if (![cw->window_name isEqualToString:dict[WINDOW_NAME]])
continue;
pthread_mutex_unlock(&cw->name_lock);
pthread_mutex_unlock(&cw->name_lock);
NSNumber *window_id = (NSNumber *)dict[WINDOW_NUMBER];
cw->window_id = window_id.intValue;
NSNumber *owner_pid = (NSNumber *)dict[OWNER_PID];
cw->owner_pid = owner_pid.intValue;
NSNumber *window_id = (NSNumber *) dict[WINDOW_NUMBER];
cw->window_id = window_id.intValue;
NSNumber *owner_pid = (NSNumber *) dict[OWNER_PID];
cw->owner_pid = owner_pid.intValue;
obs_data_set_int(settings, "window", cw->window_id);
obs_data_set_int(settings, "owner_pid", cw->owner_pid);
return true;
}
obs_data_set_int(settings, "window", cw->window_id);
obs_data_set_int(settings, "owner_pid", cw->owner_pid);
return true;
}
invalid_name:
pthread_mutex_unlock(&cw->name_lock);
return false;
pthread_mutex_unlock(&cw->name_lock);
return false;
}
void init_window(cocoa_window_t cw, obs_data_t *settings)
{
pthread_mutex_init(&cw->name_lock, NULL);
pthread_mutex_init(&cw->name_lock, NULL);
cw->owner_name = @(obs_data_get_string(settings, "owner_name"));
cw->window_name = @(obs_data_get_string(settings, "window_name"));
[cw->owner_name retain];
[cw->window_name retain];
cw->owner_name = @(obs_data_get_string(settings, "owner_name"));
cw->window_name = @(obs_data_get_string(settings, "window_name"));
[cw->owner_name retain];
[cw->window_name retain];
// Find initial window.
pthread_mutex_lock(&cw->name_lock);
// Find initial window.
pthread_mutex_lock(&cw->name_lock);
if (!cw->window_name.length && !cw->owner_name.length)
goto invalid_name;
if (!cw->window_name.length && !cw->owner_name.length)
goto invalid_name;
NSNumber *owner_pid = @(obs_data_get_int(settings, "owner_pid"));
NSNumber *window_id = @(obs_data_get_int(settings, "window"));
for (NSDictionary *dict in enumerate_windows()) {
bool owner_names_match =
[cw->owner_name isEqualToString:dict[OWNER_NAME]];
bool ids_match =
[owner_pid isEqualToNumber:dict[OWNER_PID]] &&
[window_id isEqualToNumber:dict[WINDOW_NUMBER]];
bool window_names_match =
[cw->window_name isEqualToString:dict[WINDOW_NAME]];
NSNumber *owner_pid = @(obs_data_get_int(settings, "owner_pid"));
NSNumber *window_id = @(obs_data_get_int(settings, "window"));
for (NSDictionary *dict in enumerate_windows()) {
bool owner_names_match = [cw->owner_name isEqualToString:dict[OWNER_NAME]];
bool ids_match = [owner_pid isEqualToNumber:dict[OWNER_PID]] && [window_id isEqualToNumber:dict[WINDOW_NUMBER]];
bool window_names_match = [cw->window_name isEqualToString:dict[WINDOW_NAME]];
if (owner_names_match && (ids_match || window_names_match)) {
pthread_mutex_unlock(&cw->name_lock);
if (owner_names_match && (ids_match || window_names_match)) {
pthread_mutex_unlock(&cw->name_lock);
cw->window_id = [dict[WINDOW_NUMBER] intValue];
cw->owner_pid = [dict[OWNER_PID] intValue];
cw->window_id = [dict[WINDOW_NUMBER] intValue];
cw->owner_pid = [dict[OWNER_PID] intValue];
obs_data_set_int(settings, "window", cw->window_id);
obs_data_set_int(settings, "owner_pid", cw->owner_pid);
return;
}
}
obs_data_set_int(settings, "window", cw->window_id);
obs_data_set_int(settings, "owner_pid", cw->owner_pid);
return;
}
}
invalid_name:
pthread_mutex_unlock(&cw->name_lock);
return;
pthread_mutex_unlock(&cw->name_lock);
return;
}
void destroy_window(cocoa_window_t cw)
{
pthread_mutex_destroy(&cw->name_lock);
[cw->owner_name release];
[cw->window_name release];
pthread_mutex_destroy(&cw->name_lock);
[cw->owner_name release];
[cw->window_name release];
}
void update_window(cocoa_window_t cw, obs_data_t *settings)
{
pthread_mutex_lock(&cw->name_lock);
[cw->owner_name release];
[cw->window_name release];
cw->owner_name = @(obs_data_get_string(settings, "owner_name"));
cw->window_name = @(obs_data_get_string(settings, "window_name"));
[cw->owner_name retain];
[cw->window_name retain];
pthread_mutex_unlock(&cw->name_lock);
pthread_mutex_lock(&cw->name_lock);
[cw->owner_name release];
[cw->window_name release];
cw->owner_name = @(obs_data_get_string(settings, "owner_name"));
cw->window_name = @(obs_data_get_string(settings, "window_name"));
[cw->owner_name retain];
[cw->window_name retain];
pthread_mutex_unlock(&cw->name_lock);
cw->owner_pid = (int)obs_data_get_int(settings, "owner_pid");
cw->window_id = (unsigned int)obs_data_get_int(settings, "window");
cw->owner_pid = (int) obs_data_get_int(settings, "owner_pid");
cw->window_id = (unsigned int) obs_data_get_int(settings, "window");
}
static inline const char *make_name(NSString *owner, NSString *name)
{
if (!owner.length)
return "";
if (!owner.length)
return "";
NSString *str = [NSString stringWithFormat:@"[%@] %@", owner, name];
return str.UTF8String;
NSString *str = [NSString stringWithFormat:@"[%@] %@", owner, name];
return str.UTF8String;
}
static inline NSDictionary *find_window_dict(NSArray *arr, int window_id)
{
for (NSDictionary *dict in arr) {
NSNumber *wid = (NSNumber *)dict[WINDOW_NUMBER];
if (wid.intValue == window_id)
return dict;
}
for (NSDictionary *dict in arr) {
NSNumber *wid = (NSNumber *) dict[WINDOW_NUMBER];
if (wid.intValue == window_id)
return dict;
}
return nil;
return nil;
}
static inline bool window_changed_internal(obs_property_t *p,
obs_data_t *settings)
static inline bool window_changed_internal(obs_property_t *p, obs_data_t *settings)
{
int window_id = (int)obs_data_get_int(settings, "window");
NSString *window_owner = @(obs_data_get_string(settings, "owner_name"));
NSString *window_name = @(obs_data_get_string(settings, "window_name"));
int window_id = (int) obs_data_get_int(settings, "window");
NSString *window_owner = @(obs_data_get_string(settings, "owner_name"));
NSString *window_name = @(obs_data_get_string(settings, "window_name"));
NSDictionary *win_info = @{
OWNER_NAME: window_owner,
WINDOW_NAME: window_name,
};
NSDictionary *win_info = @ {
OWNER_NAME: window_owner,
WINDOW_NAME: window_name,
};
NSArray *arr = enumerate_windows();
NSArray *arr = enumerate_windows();
bool show_empty_names = obs_data_get_bool(settings, "show_empty_names");
bool show_empty_names = obs_data_get_bool(settings, "show_empty_names");
NSDictionary *cur = find_window_dict(arr, window_id);
bool window_found = cur != nil;
bool window_added = window_found;
NSDictionary *cur = find_window_dict(arr, window_id);
bool window_found = cur != nil;
bool window_added = window_found;
obs_property_list_clear(p);
for (NSDictionary *dict in arr) {
NSString *owner = (NSString *)dict[OWNER_NAME];
NSString *name = (NSString *)dict[WINDOW_NAME];
NSNumber *wid = (NSNumber *)dict[WINDOW_NUMBER];
obs_property_list_clear(p);
for (NSDictionary *dict in arr) {
NSString *owner = (NSString *) dict[OWNER_NAME];
NSString *name = (NSString *) dict[WINDOW_NAME];
NSNumber *wid = (NSNumber *) dict[WINDOW_NUMBER];
if (!window_added &&
win_info_cmp(win_info, dict) == NSOrderedAscending) {
window_added = true;
size_t idx = obs_property_list_add_int(
p, make_name(window_owner, window_name),
window_id);
obs_property_list_item_disable(p, idx, true);
}
if (!window_added && win_info_cmp(win_info, dict) == NSOrderedAscending) {
window_added = true;
size_t idx = obs_property_list_add_int(p, make_name(window_owner, window_name), window_id);
obs_property_list_item_disable(p, idx, true);
}
if (!show_empty_names && !name.length &&
window_id != wid.intValue)
continue;
if (!show_empty_names && !name.length && window_id != wid.intValue)
continue;
obs_property_list_add_int(p, make_name(owner, name),
wid.intValue);
}
obs_property_list_add_int(p, make_name(owner, name), wid.intValue);
}
if (!window_added) {
size_t idx = obs_property_list_add_int(
p, make_name(window_owner, window_name), window_id);
obs_property_list_item_disable(p, idx, true);
}
if (!window_added) {
size_t idx = obs_property_list_add_int(p, make_name(window_owner, window_name), window_id);
obs_property_list_item_disable(p, idx, true);
}
if (!window_found)
return true;
if (!window_found)
return true;
NSString *owner = (NSString *)cur[OWNER_NAME];
NSString *window = (NSString *)cur[WINDOW_NAME];
NSString *owner = (NSString *) cur[OWNER_NAME];
NSString *window = (NSString *) cur[WINDOW_NAME];
obs_data_set_string(settings, "owner_name", owner.UTF8String);
obs_data_set_string(settings, "window_name", window.UTF8String);
obs_data_set_string(settings, "owner_name", owner.UTF8String);
obs_data_set_string(settings, "window_name", window.UTF8String);
return true;
return true;
}
static bool window_changed(obs_properties_t *props, obs_property_t *p,
obs_data_t *settings)
static bool window_changed(obs_properties_t *props, obs_property_t *p, obs_data_t *settings)
{
UNUSED_PARAMETER(props);
UNUSED_PARAMETER(props);
@autoreleasepool {
return window_changed_internal(p, settings);
}
@autoreleasepool {
return window_changed_internal(p, settings);
}
}
static bool toggle_empty_names(obs_properties_t *props, obs_property_t *p,
obs_data_t *settings)
static bool toggle_empty_names(obs_properties_t *props, obs_property_t *p, obs_data_t *settings)
{
UNUSED_PARAMETER(p);
UNUSED_PARAMETER(p);
return window_changed(props, obs_properties_get(props, "window"),
settings);
return window_changed(props, obs_properties_get(props, "window"), settings);
}
void window_defaults(obs_data_t *settings)
{
obs_data_set_default_int(settings, "window", kCGNullWindowID);
obs_data_set_default_bool(settings, "show_empty_names", false);
obs_data_set_default_int(settings, "window", kCGNullWindowID);
obs_data_set_default_bool(settings, "show_empty_names", false);
}
void add_window_properties(obs_properties_t *props)
{
obs_property_t *window_list = obs_properties_add_list(
props, "window", obs_module_text("WindowUtils.Window"),
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
obs_property_set_modified_callback(window_list, window_changed);
obs_property_t *window_list = obs_properties_add_list(props, "window", obs_module_text("WindowUtils.Window"),
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
obs_property_set_modified_callback(window_list, window_changed);
obs_property_t *empty = obs_properties_add_bool(
props, "show_empty_names",
obs_module_text("WindowUtils.ShowEmptyNames"));
obs_property_set_modified_callback(empty, toggle_empty_names);
obs_property_t *empty =
obs_properties_add_bool(props, "show_empty_names", obs_module_text("WindowUtils.ShowEmptyNames"));
obs_property_set_modified_callback(empty, toggle_empty_names);
}
void show_window_properties(obs_properties_t *props, bool show)
{
obs_property_set_visible(obs_properties_get(props, "window"), show);
obs_property_set_visible(obs_properties_get(props, "show_empty_names"),
show);
obs_property_set_visible(obs_properties_get(props, "window"), show);
obs_property_set_visible(obs_properties_get(props, "show_empty_names"), show);
}
File diff suppressed because it is too large Load Diff
@@ -7,22 +7,19 @@
#import "CMSampleBufferUtils.h"
CMSampleTimingInfo CMSampleTimingInfoForTimestamp(uint64_t timestampNanos,
uint32_t fpsNumerator,
uint32_t fpsDenominator)
CMSampleTimingInfo CMSampleTimingInfoForTimestamp(uint64_t timestampNanos, uint32_t fpsNumerator,
uint32_t fpsDenominator)
{
// The timing here is quite important. For frames to be delivered correctly and successfully be recorded by apps
// like QuickTime Player, we need to be accurate in both our timestamps _and_ have a sensible scale. Using large
// timestamps and scales like clock_gettime_nsec_np() and NSEC_PER_SEC will work for display, but will error out
// when trying to record.
//
// 600 is a common default in Apple's docs https://developer.apple.com/documentation/avfoundation/avmutablemovie/1390622-timescale
CMTimeScale scale = 600;
CMSampleTimingInfo timing;
timing.duration =
CMTimeMake(fpsDenominator * scale, fpsNumerator * scale);
timing.presentationTimeStamp = CMTimeMakeWithSeconds(
timestampNanos / (double)NSEC_PER_SEC, scale);
timing.decodeTimeStamp = kCMTimeInvalid;
return timing;
// The timing here is quite important. For frames to be delivered correctly and successfully be recorded by apps
// like QuickTime Player, we need to be accurate in both our timestamps _and_ have a sensible scale. Using large
// timestamps and scales like clock_gettime_nsec_np() and NSEC_PER_SEC will work for display, but will error out
// when trying to record.
//
// 600 is a common default in Apple's docs https://developer.apple.com/documentation/avfoundation/avmutablemovie/1390622-timescale
CMTimeScale scale = 600;
CMSampleTimingInfo timing;
timing.duration = CMTimeMake(fpsDenominator * scale, fpsNumerator * scale);
timing.presentationTimeStamp = CMTimeMakeWithSeconds(timestampNanos / (double) NSEC_PER_SEC, scale);
timing.decodeTimeStamp = kCMTimeInvalid;
return timing;
}
@@ -17,5 +17,5 @@
// You should have received a copy of the GNU General Public License
// along with obs-mac-virtualcam. If not, see <http://www.gnu.org/licenses/>.
#define PLUGIN_NAME @"mac-virtualcam"
#define PLUGIN_NAME @"mac-virtualcam"
#define PLUGIN_VERSION @"1.3.0"
@@ -23,18 +23,17 @@
#include "Defines.h"
#ifdef DEBUG
#define DLog(fmt, ...) NSLog((PLUGIN_NAME @"(DAL): " fmt), ##__VA_ARGS__)
#define DLogFunc(fmt, ...) \
NSLog((PLUGIN_NAME @"(DAL): %s " fmt), __FUNCTION__, ##__VA_ARGS__)
#define DLog(fmt, ...) NSLog((PLUGIN_NAME @"(DAL): " fmt), ##__VA_ARGS__)
#define DLogFunc(fmt, ...) NSLog((PLUGIN_NAME @"(DAL): %s " fmt), __FUNCTION__, ##__VA_ARGS__)
#else
#define DLog(...) (void)sizeof(__VA_ARGS__)
#define DLogFunc(...) (void)sizeof(__VA_ARGS__)
#define DLog(...) (void) sizeof(__VA_ARGS__)
#define DLogFunc(...) (void) sizeof(__VA_ARGS__)
#endif
#define VLog(fmt, ...)
#define VLogFunc(fmt, ...)
#define ELog(fmt, ...) DLog(fmt, ##__VA_ARGS__)
#define UNUSED_PARAMETER(param) (void)param
#define UNUSED_PARAMETER(param) (void) param
#endif /* Logging_h */
@@ -33,247 +33,240 @@
// Note that the DAL's API calls HasProperty before calling GetPropertyDataSize. This means that it can be assumed that address is valid for the property involved.
- (UInt32)getPropertyDataSizeWithAddress:(CMIOObjectPropertyAddress)address
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(nonnull const void *)qualifierData
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(nonnull const void *)qualifierData
{
switch (address.mSelector) {
case kCMIOObjectPropertyName:
return sizeof(CFStringRef);
case kCMIOObjectPropertyManufacturer:
return sizeof(CFStringRef);
case kCMIOObjectPropertyElementCategoryName:
return sizeof(CFStringRef);
case kCMIOObjectPropertyElementNumberName:
return sizeof(CFStringRef);
case kCMIODevicePropertyPlugIn:
return sizeof(CMIOObjectID);
case kCMIODevicePropertyDeviceUID:
return sizeof(CFStringRef);
case kCMIODevicePropertyModelUID:
return sizeof(CFStringRef);
case kCMIODevicePropertyTransportType:
return sizeof(UInt32);
case kCMIODevicePropertyDeviceIsAlive:
return sizeof(UInt32);
case kCMIODevicePropertyDeviceHasChanged:
return sizeof(UInt32);
case kCMIODevicePropertyDeviceIsRunning:
return sizeof(UInt32);
case kCMIODevicePropertyDeviceIsRunningSomewhere:
return sizeof(UInt32);
case kCMIODevicePropertyDeviceCanBeDefaultDevice:
return sizeof(UInt32);
case kCMIODevicePropertyHogMode:
return sizeof(pid_t);
case kCMIODevicePropertyLatency:
return sizeof(UInt32);
case kCMIODevicePropertyStreams:
// Only one stream
return sizeof(CMIOStreamID) * 1;
case kCMIODevicePropertyStreamConfiguration:
// Only one stream
return sizeof(UInt32) + (sizeof(UInt32) * 1);
case kCMIODevicePropertyExcludeNonDALAccess:
return sizeof(UInt32);
case kCMIODevicePropertyCanProcessAVCCommand:
return sizeof(Boolean);
case kCMIODevicePropertyCanProcessRS422Command:
return sizeof(Boolean);
case kCMIODevicePropertyLinkedCoreAudioDeviceUID:
return sizeof(CFStringRef);
case kCMIODevicePropertyDeviceMaster:
return sizeof(pid_t);
default:
break;
};
switch (address.mSelector) {
case kCMIOObjectPropertyName:
return sizeof(CFStringRef);
case kCMIOObjectPropertyManufacturer:
return sizeof(CFStringRef);
case kCMIOObjectPropertyElementCategoryName:
return sizeof(CFStringRef);
case kCMIOObjectPropertyElementNumberName:
return sizeof(CFStringRef);
case kCMIODevicePropertyPlugIn:
return sizeof(CMIOObjectID);
case kCMIODevicePropertyDeviceUID:
return sizeof(CFStringRef);
case kCMIODevicePropertyModelUID:
return sizeof(CFStringRef);
case kCMIODevicePropertyTransportType:
return sizeof(UInt32);
case kCMIODevicePropertyDeviceIsAlive:
return sizeof(UInt32);
case kCMIODevicePropertyDeviceHasChanged:
return sizeof(UInt32);
case kCMIODevicePropertyDeviceIsRunning:
return sizeof(UInt32);
case kCMIODevicePropertyDeviceIsRunningSomewhere:
return sizeof(UInt32);
case kCMIODevicePropertyDeviceCanBeDefaultDevice:
return sizeof(UInt32);
case kCMIODevicePropertyHogMode:
return sizeof(pid_t);
case kCMIODevicePropertyLatency:
return sizeof(UInt32);
case kCMIODevicePropertyStreams:
// Only one stream
return sizeof(CMIOStreamID) * 1;
case kCMIODevicePropertyStreamConfiguration:
// Only one stream
return sizeof(UInt32) + (sizeof(UInt32) * 1);
case kCMIODevicePropertyExcludeNonDALAccess:
return sizeof(UInt32);
case kCMIODevicePropertyCanProcessAVCCommand:
return sizeof(Boolean);
case kCMIODevicePropertyCanProcessRS422Command:
return sizeof(Boolean);
case kCMIODevicePropertyLinkedCoreAudioDeviceUID:
return sizeof(CFStringRef);
case kCMIODevicePropertyDeviceMaster:
return sizeof(pid_t);
default:
break;
};
return 0;
return 0;
}
// Note that the DAL's API calls HasProperty before calling GetPropertyData. This means that it can be assumed that address is valid for the property involved.
- (void)getPropertyDataWithAddress:(CMIOObjectPropertyAddress)address
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(nonnull const void *)qualifierData
dataSize:(UInt32)dataSize
dataUsed:(nonnull UInt32 *)dataUsed
data:(nonnull void *)data
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(nonnull const void *)qualifierData
dataSize:(UInt32)dataSize
dataUsed:(nonnull UInt32 *)dataUsed
data:(nonnull void *)data
{
switch (address.mSelector) {
case kCMIOObjectPropertyName:
*static_cast<CFStringRef *>(data) = CFSTR("OBS Virtual Camera");
*dataUsed = sizeof(CFStringRef);
break;
case kCMIOObjectPropertyManufacturer:
*static_cast<CFStringRef *>(data) = CFSTR("John Boiles");
*dataUsed = sizeof(CFStringRef);
break;
case kCMIOObjectPropertyElementCategoryName:
*static_cast<CFStringRef *>(data) = CFSTR("Virtual Camera");
*dataUsed = sizeof(CFStringRef);
break;
case kCMIOObjectPropertyElementNumberName:
*static_cast<CFStringRef *>(data) = CFSTR("0001");
*dataUsed = sizeof(CFStringRef);
break;
case kCMIODevicePropertyPlugIn:
*static_cast<CMIOObjectID *>(data) = self.pluginId;
*dataUsed = sizeof(CMIOObjectID);
break;
case kCMIODevicePropertyDeviceUID:
*static_cast<CFStringRef *>(data) =
CFSTR("obs-virtual-cam-device");
*dataUsed = sizeof(CFStringRef);
break;
case kCMIODevicePropertyModelUID:
*static_cast<CFStringRef *>(data) =
CFSTR("obs-virtual-cam-model");
*dataUsed = sizeof(CFStringRef);
break;
case kCMIODevicePropertyTransportType:
*static_cast<UInt32 *>(data) =
kIOAudioDeviceTransportTypeBuiltIn;
*dataUsed = sizeof(UInt32);
break;
case kCMIODevicePropertyDeviceIsAlive:
*static_cast<UInt32 *>(data) = 1;
*dataUsed = sizeof(UInt32);
break;
case kCMIODevicePropertyDeviceHasChanged:
*static_cast<UInt32 *>(data) = 0;
*dataUsed = sizeof(UInt32);
break;
case kCMIODevicePropertyDeviceIsRunning:
*static_cast<UInt32 *>(data) = 1;
*dataUsed = sizeof(UInt32);
break;
case kCMIODevicePropertyDeviceIsRunningSomewhere:
*static_cast<UInt32 *>(data) = 1;
*dataUsed = sizeof(UInt32);
break;
case kCMIODevicePropertyDeviceCanBeDefaultDevice:
*static_cast<UInt32 *>(data) = 1;
*dataUsed = sizeof(UInt32);
break;
case kCMIODevicePropertyHogMode:
*static_cast<pid_t *>(data) = -1;
*dataUsed = sizeof(pid_t);
break;
case kCMIODevicePropertyLatency:
*static_cast<UInt32 *>(data) = 0;
*dataUsed = sizeof(UInt32);
break;
case kCMIODevicePropertyStreams:
*static_cast<CMIOObjectID *>(data) = self.streamId;
*dataUsed = sizeof(CMIOObjectID);
break;
case kCMIODevicePropertyStreamConfiguration:
DLog(@"TODO kCMIODevicePropertyStreamConfiguration");
break;
case kCMIODevicePropertyExcludeNonDALAccess:
*static_cast<UInt32 *>(data) = self.excludeNonDALAccess ? 1 : 0;
*dataUsed = sizeof(UInt32);
break;
case kCMIODevicePropertyCanProcessAVCCommand:
*static_cast<Boolean *>(data) = false;
*dataUsed = sizeof(Boolean);
break;
case kCMIODevicePropertyCanProcessRS422Command:
*static_cast<Boolean *>(data) = false;
*dataUsed = sizeof(Boolean);
break;
case kCMIODevicePropertyDeviceMaster:
*static_cast<pid_t *>(data) = self.masterPid;
*dataUsed = sizeof(pid_t);
break;
default:
break;
};
switch (address.mSelector) {
case kCMIOObjectPropertyName:
*static_cast<CFStringRef *>(data) = CFSTR("OBS Virtual Camera");
*dataUsed = sizeof(CFStringRef);
break;
case kCMIOObjectPropertyManufacturer:
*static_cast<CFStringRef *>(data) = CFSTR("John Boiles");
*dataUsed = sizeof(CFStringRef);
break;
case kCMIOObjectPropertyElementCategoryName:
*static_cast<CFStringRef *>(data) = CFSTR("Virtual Camera");
*dataUsed = sizeof(CFStringRef);
break;
case kCMIOObjectPropertyElementNumberName:
*static_cast<CFStringRef *>(data) = CFSTR("0001");
*dataUsed = sizeof(CFStringRef);
break;
case kCMIODevicePropertyPlugIn:
*static_cast<CMIOObjectID *>(data) = self.pluginId;
*dataUsed = sizeof(CMIOObjectID);
break;
case kCMIODevicePropertyDeviceUID:
*static_cast<CFStringRef *>(data) = CFSTR("obs-virtual-cam-device");
*dataUsed = sizeof(CFStringRef);
break;
case kCMIODevicePropertyModelUID:
*static_cast<CFStringRef *>(data) = CFSTR("obs-virtual-cam-model");
*dataUsed = sizeof(CFStringRef);
break;
case kCMIODevicePropertyTransportType:
*static_cast<UInt32 *>(data) = kIOAudioDeviceTransportTypeBuiltIn;
*dataUsed = sizeof(UInt32);
break;
case kCMIODevicePropertyDeviceIsAlive:
*static_cast<UInt32 *>(data) = 1;
*dataUsed = sizeof(UInt32);
break;
case kCMIODevicePropertyDeviceHasChanged:
*static_cast<UInt32 *>(data) = 0;
*dataUsed = sizeof(UInt32);
break;
case kCMIODevicePropertyDeviceIsRunning:
*static_cast<UInt32 *>(data) = 1;
*dataUsed = sizeof(UInt32);
break;
case kCMIODevicePropertyDeviceIsRunningSomewhere:
*static_cast<UInt32 *>(data) = 1;
*dataUsed = sizeof(UInt32);
break;
case kCMIODevicePropertyDeviceCanBeDefaultDevice:
*static_cast<UInt32 *>(data) = 1;
*dataUsed = sizeof(UInt32);
break;
case kCMIODevicePropertyHogMode:
*static_cast<pid_t *>(data) = -1;
*dataUsed = sizeof(pid_t);
break;
case kCMIODevicePropertyLatency:
*static_cast<UInt32 *>(data) = 0;
*dataUsed = sizeof(UInt32);
break;
case kCMIODevicePropertyStreams:
*static_cast<CMIOObjectID *>(data) = self.streamId;
*dataUsed = sizeof(CMIOObjectID);
break;
case kCMIODevicePropertyStreamConfiguration:
DLog(@"TODO kCMIODevicePropertyStreamConfiguration");
break;
case kCMIODevicePropertyExcludeNonDALAccess:
*static_cast<UInt32 *>(data) = self.excludeNonDALAccess ? 1 : 0;
*dataUsed = sizeof(UInt32);
break;
case kCMIODevicePropertyCanProcessAVCCommand:
*static_cast<Boolean *>(data) = false;
*dataUsed = sizeof(Boolean);
break;
case kCMIODevicePropertyCanProcessRS422Command:
*static_cast<Boolean *>(data) = false;
*dataUsed = sizeof(Boolean);
break;
case kCMIODevicePropertyDeviceMaster:
*static_cast<pid_t *>(data) = self.masterPid;
*dataUsed = sizeof(pid_t);
break;
default:
break;
};
}
- (BOOL)hasPropertyWithAddress:(CMIOObjectPropertyAddress)address
{
switch (address.mSelector) {
case kCMIOObjectPropertyName:
case kCMIOObjectPropertyManufacturer:
case kCMIOObjectPropertyElementCategoryName:
case kCMIOObjectPropertyElementNumberName:
case kCMIODevicePropertyPlugIn:
case kCMIODevicePropertyDeviceUID:
case kCMIODevicePropertyModelUID:
case kCMIODevicePropertyTransportType:
case kCMIODevicePropertyDeviceIsAlive:
case kCMIODevicePropertyDeviceHasChanged:
case kCMIODevicePropertyDeviceIsRunning:
case kCMIODevicePropertyDeviceIsRunningSomewhere:
case kCMIODevicePropertyDeviceCanBeDefaultDevice:
case kCMIODevicePropertyHogMode:
case kCMIODevicePropertyLatency:
case kCMIODevicePropertyStreams:
case kCMIODevicePropertyExcludeNonDALAccess:
case kCMIODevicePropertyCanProcessAVCCommand:
case kCMIODevicePropertyCanProcessRS422Command:
case kCMIODevicePropertyDeviceMaster:
return true;
case kCMIODevicePropertyStreamConfiguration:
case kCMIODevicePropertyLinkedCoreAudioDeviceUID:
return false;
default:
return false;
};
switch (address.mSelector) {
case kCMIOObjectPropertyName:
case kCMIOObjectPropertyManufacturer:
case kCMIOObjectPropertyElementCategoryName:
case kCMIOObjectPropertyElementNumberName:
case kCMIODevicePropertyPlugIn:
case kCMIODevicePropertyDeviceUID:
case kCMIODevicePropertyModelUID:
case kCMIODevicePropertyTransportType:
case kCMIODevicePropertyDeviceIsAlive:
case kCMIODevicePropertyDeviceHasChanged:
case kCMIODevicePropertyDeviceIsRunning:
case kCMIODevicePropertyDeviceIsRunningSomewhere:
case kCMIODevicePropertyDeviceCanBeDefaultDevice:
case kCMIODevicePropertyHogMode:
case kCMIODevicePropertyLatency:
case kCMIODevicePropertyStreams:
case kCMIODevicePropertyExcludeNonDALAccess:
case kCMIODevicePropertyCanProcessAVCCommand:
case kCMIODevicePropertyCanProcessRS422Command:
case kCMIODevicePropertyDeviceMaster:
return true;
case kCMIODevicePropertyStreamConfiguration:
case kCMIODevicePropertyLinkedCoreAudioDeviceUID:
return false;
default:
return false;
};
}
- (BOOL)isPropertySettableWithAddress:(CMIOObjectPropertyAddress)address
{
switch (address.mSelector) {
case kCMIOObjectPropertyName:
case kCMIOObjectPropertyManufacturer:
case kCMIOObjectPropertyElementCategoryName:
case kCMIOObjectPropertyElementNumberName:
case kCMIODevicePropertyPlugIn:
case kCMIODevicePropertyDeviceUID:
case kCMIODevicePropertyModelUID:
case kCMIODevicePropertyTransportType:
case kCMIODevicePropertyDeviceIsAlive:
case kCMIODevicePropertyDeviceHasChanged:
case kCMIODevicePropertyDeviceIsRunning:
case kCMIODevicePropertyDeviceIsRunningSomewhere:
case kCMIODevicePropertyDeviceCanBeDefaultDevice:
case kCMIODevicePropertyHogMode:
case kCMIODevicePropertyLatency:
case kCMIODevicePropertyStreams:
case kCMIODevicePropertyStreamConfiguration:
case kCMIODevicePropertyCanProcessAVCCommand:
case kCMIODevicePropertyCanProcessRS422Command:
case kCMIODevicePropertyLinkedCoreAudioDeviceUID:
return false;
case kCMIODevicePropertyExcludeNonDALAccess:
case kCMIODevicePropertyDeviceMaster:
return true;
default:
return false;
};
switch (address.mSelector) {
case kCMIOObjectPropertyName:
case kCMIOObjectPropertyManufacturer:
case kCMIOObjectPropertyElementCategoryName:
case kCMIOObjectPropertyElementNumberName:
case kCMIODevicePropertyPlugIn:
case kCMIODevicePropertyDeviceUID:
case kCMIODevicePropertyModelUID:
case kCMIODevicePropertyTransportType:
case kCMIODevicePropertyDeviceIsAlive:
case kCMIODevicePropertyDeviceHasChanged:
case kCMIODevicePropertyDeviceIsRunning:
case kCMIODevicePropertyDeviceIsRunningSomewhere:
case kCMIODevicePropertyDeviceCanBeDefaultDevice:
case kCMIODevicePropertyHogMode:
case kCMIODevicePropertyLatency:
case kCMIODevicePropertyStreams:
case kCMIODevicePropertyStreamConfiguration:
case kCMIODevicePropertyCanProcessAVCCommand:
case kCMIODevicePropertyCanProcessRS422Command:
case kCMIODevicePropertyLinkedCoreAudioDeviceUID:
return false;
case kCMIODevicePropertyExcludeNonDALAccess:
case kCMIODevicePropertyDeviceMaster:
return true;
default:
return false;
};
}
- (void)setPropertyDataWithAddress:(CMIOObjectPropertyAddress)address
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(nonnull const void *)qualifierData
dataSize:(UInt32)dataSize
data:(nonnull const void *)data
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(nonnull const void *)qualifierData
dataSize:(UInt32)dataSize
data:(nonnull const void *)data
{
switch (address.mSelector) {
case kCMIODevicePropertyExcludeNonDALAccess:
self.excludeNonDALAccess =
(*static_cast<const UInt32 *>(data) != 0);
break;
case kCMIODevicePropertyDeviceMaster:
self.masterPid = *static_cast<const pid_t *>(data);
break;
default:
break;
};
switch (address.mSelector) {
case kCMIODevicePropertyExcludeNonDALAccess:
self.excludeNonDALAccess = (*static_cast<const UInt32 *>(data) != 0);
break;
case kCMIODevicePropertyDeviceMaster:
self.masterPid = *static_cast<const pid_t *>(data);
break;
default:
break;
};
}
@end
@@ -13,9 +13,9 @@ NS_ASSUME_NONNULL_BEGIN
@protocol MachClientDelegate
- (void)receivedPixelBuffer:(CVPixelBufferRef)frame
timestamp:(uint64_t)timestamp
fpsNumerator:(uint32_t)fpsNumerator
fpsDenominator:(uint32_t)fpsDenominator;
timestamp:(uint64_t)timestamp
fpsNumerator:(uint32_t)fpsNumerator
fpsDenominator:(uint32_t)fpsDenominator;
- (void)receivedStop;
@end
@@ -10,7 +10,7 @@
#import "Logging.h"
@interface OBSDALMachClient () <NSPortDelegate> {
NSPort *_receivePort;
NSPort *_receivePort;
}
@end
@@ -18,8 +18,8 @@
- (void)dealloc
{
DLogFunc(@"");
_receivePort.delegate = nil;
DLogFunc(@"");
_receivePort.delegate = nil;
}
- (NSPort *)serverPort
@@ -27,154 +27,129 @@
// See note in MachServer.mm and don't judge me
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [[NSMachBootstrapServer sharedInstance]
portForName:@MACH_SERVICE_NAME];
return [[NSMachBootstrapServer sharedInstance] portForName:@MACH_SERVICE_NAME];
#pragma clang diagnostic pop
}
- (BOOL)isServerAvailable
{
return [self serverPort] != nil;
return [self serverPort] != nil;
}
- (NSPort *)receivePort
{
if (_receivePort == nil) {
NSPort *receivePort = [NSMachPort port];
_receivePort = receivePort;
_receivePort.delegate = self;
__weak __typeof(self) weakSelf = self;
dispatch_async(
dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:receivePort
forMode:NSDefaultRunLoopMode];
// weakSelf should become nil when this object gets destroyed
while (weakSelf) {
[[NSRunLoop currentRunLoop]
runUntilDate:
[NSDate dateWithTimeIntervalSinceNow:
0.1]];
}
DLog(@"Shutting down receive run loop");
});
DLog(@"Initialized mach port %d for receiving",
((NSMachPort *)_receivePort).machPort);
}
return _receivePort;
if (_receivePort == nil) {
NSPort *receivePort = [NSMachPort port];
_receivePort = receivePort;
_receivePort.delegate = self;
__weak __typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:receivePort forMode:NSDefaultRunLoopMode];
// weakSelf should become nil when this object gets destroyed
while (weakSelf) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
}
DLog(@"Shutting down receive run loop");
});
DLog(@"Initialized mach port %d for receiving", ((NSMachPort *) _receivePort).machPort);
}
return _receivePort;
}
- (BOOL)connectToServer
{
DLogFunc(@"");
DLogFunc(@"");
NSPort *sendPort = [self serverPort];
if (sendPort == nil) {
ELog(@"Unable to connect to server port");
return NO;
}
NSPort *sendPort = [self serverPort];
if (sendPort == nil) {
ELog(@"Unable to connect to server port");
return NO;
}
NSPortMessage *message = [[NSPortMessage alloc]
initWithSendPort:sendPort
receivePort:self.receivePort
components:nil];
message.msgid = MachMsgIdConnect;
NSPortMessage *message = [[NSPortMessage alloc] initWithSendPort:sendPort receivePort:self.receivePort
components:nil];
message.msgid = MachMsgIdConnect;
NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:5.0];
if (![message sendBeforeDate:timeout]) {
ELog(@"sendBeforeDate failed");
return NO;
}
NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:5.0];
if (![message sendBeforeDate:timeout]) {
ELog(@"sendBeforeDate failed");
return NO;
}
return YES;
return YES;
}
- (void)handlePortMessage:(NSPortMessage *)message
{
__strong id<MachClientDelegate> strongDelegate = self.delegate;
__strong id<MachClientDelegate> strongDelegate = self.delegate;
VLogFunc(@"");
NSArray *components = message.components;
switch (message.msgid) {
case MachMsgIdConnect:
DLog(@"Received connect response");
break;
case MachMsgIdFrame:
VLog(@"Received frame message");
VLogFunc(@"");
NSArray *components = message.components;
switch (message.msgid) {
case MachMsgIdConnect:
DLog(@"Received connect response");
break;
case MachMsgIdFrame:
VLog(@"Received frame message");
if (components.count < 4)
return;
if (components.count < 4)
return;
@autoreleasepool {
NSMachPort *framePort = (NSMachPort *)components[0];
@autoreleasepool {
NSMachPort *framePort = (NSMachPort *) components[0];
if (!framePort)
return;
if (!framePort)
return;
IOSurfaceRef surface = IOSurfaceLookupFromMachPort(
[framePort machPort]);
IOSurfaceRef surface = IOSurfaceLookupFromMachPort([framePort machPort]);
if (surface) {
/*
if (surface) {
/*
* IOSurfaceLocks are only necessary on non Apple Silicon devices, as those have
* unified memory. On Intel machines, the lock ensures that the IOSurface is copied back
* from GPU memory to CPU memory so we can process the pixel buffer.
*/
#ifndef __aarch64__
IOSurfaceLock(surface, kIOSurfaceLockReadOnly,
NULL);
IOSurfaceLock(surface, kIOSurfaceLockReadOnly, NULL);
#endif
CVPixelBufferRef frame;
CVPixelBufferCreateWithIOSurface(
kCFAllocatorDefault, surface, NULL,
&frame);
CVPixelBufferRef frame;
CVPixelBufferCreateWithIOSurface(kCFAllocatorDefault, surface, NULL, &frame);
#ifndef __aarch64__
IOSurfaceUnlock(surface, kIOSurfaceLockReadOnly,
NULL);
IOSurfaceUnlock(surface, kIOSurfaceLockReadOnly, NULL);
#endif
CFRelease(surface);
CFRelease(surface);
uint64_t timestamp;
[components[1] getBytes:&timestamp
length:sizeof(timestamp)];
uint64_t timestamp;
[components[1] getBytes:&timestamp length:sizeof(timestamp)];
VLog(@"Received frame data: %zux%zu (%llu)",
CVPixelBufferGetWidth(frame),
CVPixelBufferGetHeight(frame), timestamp);
VLog(@"Received frame data: %zux%zu (%llu)", CVPixelBufferGetWidth(frame),
CVPixelBufferGetHeight(frame), timestamp);
uint32_t fpsNumerator;
[components[2] getBytes:&fpsNumerator
length:sizeof(fpsNumerator)];
uint32_t fpsDenominator;
[components[3] getBytes:&fpsDenominator
length:sizeof(fpsDenominator)];
uint32_t fpsNumerator;
[components[2] getBytes:&fpsNumerator length:sizeof(fpsNumerator)];
uint32_t fpsDenominator;
[components[3] getBytes:&fpsDenominator length:sizeof(fpsDenominator)];
[strongDelegate
receivedPixelBuffer:frame
timestamp:timestamp
fpsNumerator:fpsNumerator
fpsDenominator:fpsDenominator];
[strongDelegate receivedPixelBuffer:frame timestamp:timestamp fpsNumerator:fpsNumerator
fpsDenominator:fpsDenominator];
CVPixelBufferRelease(frame);
} else {
ELog(@"Failed to obtain IOSurface from Mach port");
}
[framePort invalidate];
mach_port_deallocate(mach_task_self(),
[framePort machPort]);
}
break;
case MachMsgIdStop:
DLog(@"Received stop message");
[strongDelegate receivedStop];
break;
default:
ELog(@"Received unexpected response msgid %u",
(unsigned)message.msgid);
break;
}
CVPixelBufferRelease(frame);
} else {
ELog(@"Failed to obtain IOSurface from Mach port");
}
[framePort invalidate];
mach_port_deallocate(mach_task_self(), [framePort machPort]);
}
break;
case MachMsgIdStop:
DLog(@"Received stop message");
[strongDelegate receivedStop];
break;
default:
ELog(@"Received unexpected response msgid %u", (unsigned) message.msgid);
break;
}
}
@end
@@ -27,19 +27,19 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)hasPropertyWithAddress:(CMIOObjectPropertyAddress)address;
- (BOOL)isPropertySettableWithAddress:(CMIOObjectPropertyAddress)address;
- (UInt32)getPropertyDataSizeWithAddress:(CMIOObjectPropertyAddress)address
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(const void *)qualifierData;
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(const void *)qualifierData;
- (void)getPropertyDataWithAddress:(CMIOObjectPropertyAddress)address
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(const void *)qualifierData
dataSize:(UInt32)dataSize
dataUsed:(UInt32 *)dataUsed
data:(void *)data;
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(const void *)qualifierData
dataSize:(UInt32)dataSize
dataUsed:(UInt32 *)dataUsed
data:(void *)data;
- (void)setPropertyDataWithAddress:(CMIOObjectPropertyAddress)address
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(const void *)qualifierData
dataSize:(UInt32)dataSize
data:(const void *)data;
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(const void *)qualifierData
dataSize:(UInt32)dataSize
data:(const void *)data;
@end
@@ -28,254 +28,252 @@
// 4-byte selectors to string for easy debugging
+ (NSString *)StringFromPropertySelector:(CMIOObjectPropertySelector)selector
{
switch (selector) {
case kCMIODevicePropertyPlugIn:
return @"kCMIODevicePropertyPlugIn";
case kCMIODevicePropertyDeviceUID:
return @"kCMIODevicePropertyDeviceUID";
case kCMIODevicePropertyModelUID:
return @"kCMIODevicePropertyModelUID";
case kCMIODevicePropertyTransportType:
return @"kCMIODevicePropertyTransportType";
case kCMIODevicePropertyDeviceIsAlive:
return @"kCMIODevicePropertyDeviceIsAlive";
case kCMIODevicePropertyDeviceHasChanged:
return @"kCMIODevicePropertyDeviceHasChanged";
case kCMIODevicePropertyDeviceIsRunning:
return @"kCMIODevicePropertyDeviceIsRunning";
case kCMIODevicePropertyDeviceIsRunningSomewhere:
return @"kCMIODevicePropertyDeviceIsRunningSomewhere";
case kCMIODevicePropertyDeviceCanBeDefaultDevice:
return @"kCMIODevicePropertyDeviceCanBeDefaultDevice";
case kCMIODevicePropertyHogMode:
return @"kCMIODevicePropertyHogMode";
case kCMIODevicePropertyLatency:
return @"kCMIODevicePropertyLatency";
case kCMIODevicePropertyStreams:
return @"kCMIODevicePropertyStreams";
case kCMIODevicePropertyStreamConfiguration:
return @"kCMIODevicePropertyStreamConfiguration";
case kCMIODevicePropertyDeviceMaster:
return @"kCMIODevicePropertyDeviceMaster";
case kCMIODevicePropertyExcludeNonDALAccess:
return @"kCMIODevicePropertyExcludeNonDALAccess";
case kCMIODevicePropertyClientSyncDiscontinuity:
return @"kCMIODevicePropertyClientSyncDiscontinuity";
case kCMIODevicePropertySMPTETimeCallback:
return @"kCMIODevicePropertySMPTETimeCallback";
case kCMIODevicePropertyCanProcessAVCCommand:
return @"kCMIODevicePropertyCanProcessAVCCommand";
case kCMIODevicePropertyAVCDeviceType:
return @"kCMIODevicePropertyAVCDeviceType";
case kCMIODevicePropertyAVCDeviceSignalMode:
return @"kCMIODevicePropertyAVCDeviceSignalMode";
case kCMIODevicePropertyCanProcessRS422Command:
return @"kCMIODevicePropertyCanProcessRS422Command";
case kCMIODevicePropertyLinkedCoreAudioDeviceUID:
return @"kCMIODevicePropertyLinkedCoreAudioDeviceUID";
case kCMIODevicePropertyVideoDigitizerComponents:
return @"kCMIODevicePropertyVideoDigitizerComponents";
case kCMIODevicePropertySuspendedByUser:
return @"kCMIODevicePropertySuspendedByUser";
case kCMIODevicePropertyLinkedAndSyncedCoreAudioDeviceUID:
return @"kCMIODevicePropertyLinkedAndSyncedCoreAudioDeviceUID";
case kCMIODevicePropertyIIDCInitialUnitSpace:
return @"kCMIODevicePropertyIIDCInitialUnitSpace";
case kCMIODevicePropertyIIDCCSRData:
return @"kCMIODevicePropertyIIDCCSRData";
case kCMIODevicePropertyCanSwitchFrameRatesWithoutFrameDrops:
return @"kCMIODevicePropertyCanSwitchFrameRatesWithoutFrameDrops";
case kCMIODevicePropertyLocation:
return @"kCMIODevicePropertyLocation";
case kCMIODevicePropertyDeviceHasStreamingError:
return @"kCMIODevicePropertyDeviceHasStreamingError";
case kCMIODevicePropertyScopeInput:
return @"kCMIODevicePropertyScopeInput";
case kCMIODevicePropertyScopeOutput:
return @"kCMIODevicePropertyScopeOutput";
case kCMIODevicePropertyScopePlayThrough:
return @"kCMIODevicePropertyScopePlayThrough";
case kCMIOObjectPropertyClass:
return @"kCMIOObjectPropertyClass";
case kCMIOObjectPropertyOwner:
return @"kCMIOObjectPropertyOwner";
case kCMIOObjectPropertyCreator:
return @"kCMIOObjectPropertyCreator";
case kCMIOObjectPropertyName:
return @"kCMIOObjectPropertyName";
case kCMIOObjectPropertyManufacturer:
return @"kCMIOObjectPropertyManufacturer";
case kCMIOObjectPropertyElementName:
return @"kCMIOObjectPropertyElementName";
case kCMIOObjectPropertyElementCategoryName:
return @"kCMIOObjectPropertyElementCategoryName";
case kCMIOObjectPropertyElementNumberName:
return @"kCMIOObjectPropertyElementNumberName";
case kCMIOObjectPropertyOwnedObjects:
return @"kCMIOObjectPropertyOwnedObjects";
case kCMIOObjectPropertyListenerAdded:
return @"kCMIOObjectPropertyListenerAdded";
case kCMIOObjectPropertyListenerRemoved:
return @"kCMIOObjectPropertyListenerRemoved";
case kCMIOStreamPropertyDirection:
return @"kCMIOStreamPropertyDirection";
case kCMIOStreamPropertyTerminalType:
return @"kCMIOStreamPropertyTerminalType";
case kCMIOStreamPropertyStartingChannel:
return @"kCMIOStreamPropertyStartingChannel";
// Same value as kCMIODevicePropertyLatency
// case kCMIOStreamPropertyLatency:
// return @"kCMIOStreamPropertyLatency";
case kCMIOStreamPropertyFormatDescription:
return @"kCMIOStreamPropertyFormatDescription";
case kCMIOStreamPropertyFormatDescriptions:
return @"kCMIOStreamPropertyFormatDescriptions";
case kCMIOStreamPropertyStillImage:
return @"kCMIOStreamPropertyStillImage";
case kCMIOStreamPropertyStillImageFormatDescriptions:
return @"kCMIOStreamPropertyStillImageFormatDescriptions";
case kCMIOStreamPropertyFrameRate:
return @"kCMIOStreamPropertyFrameRate";
case kCMIOStreamPropertyMinimumFrameRate:
return @"kCMIOStreamPropertyMinimumFrameRate";
case kCMIOStreamPropertyFrameRates:
return @"kCMIOStreamPropertyFrameRates";
case kCMIOStreamPropertyFrameRateRanges:
return @"kCMIOStreamPropertyFrameRateRanges";
case kCMIOStreamPropertyNoDataTimeoutInMSec:
return @"kCMIOStreamPropertyNoDataTimeoutInMSec";
case kCMIOStreamPropertyDeviceSyncTimeoutInMSec:
return @"kCMIOStreamPropertyDeviceSyncTimeoutInMSec";
case kCMIOStreamPropertyNoDataEventCount:
return @"kCMIOStreamPropertyNoDataEventCount";
case kCMIOStreamPropertyOutputBufferUnderrunCount:
return @"kCMIOStreamPropertyOutputBufferUnderrunCount";
case kCMIOStreamPropertyOutputBufferRepeatCount:
return @"kCMIOStreamPropertyOutputBufferRepeatCount";
case kCMIOStreamPropertyOutputBufferQueueSize:
return @"kCMIOStreamPropertyOutputBufferQueueSize";
case kCMIOStreamPropertyOutputBuffersRequiredForStartup:
return @"kCMIOStreamPropertyOutputBuffersRequiredForStartup";
case kCMIOStreamPropertyOutputBuffersNeededForThrottledPlayback:
return @"kCMIOStreamPropertyOutputBuffersNeededForThrottledPlayback";
case kCMIOStreamPropertyFirstOutputPresentationTimeStamp:
return @"kCMIOStreamPropertyFirstOutputPresentationTimeStamp";
case kCMIOStreamPropertyEndOfData:
return @"kCMIOStreamPropertyEndOfData";
case kCMIOStreamPropertyClock:
return @"kCMIOStreamPropertyClock";
case kCMIOStreamPropertyCanProcessDeckCommand:
return @"kCMIOStreamPropertyCanProcessDeckCommand";
case kCMIOStreamPropertyDeck:
return @"kCMIOStreamPropertyDeck";
case kCMIOStreamPropertyDeckFrameNumber:
return @"kCMIOStreamPropertyDeckFrameNumber";
case kCMIOStreamPropertyDeckDropness:
return @"kCMIOStreamPropertyDeckDropness";
case kCMIOStreamPropertyDeckThreaded:
return @"kCMIOStreamPropertyDeckThreaded";
case kCMIOStreamPropertyDeckLocal:
return @"kCMIOStreamPropertyDeckLocal";
case kCMIOStreamPropertyDeckCueing:
return @"kCMIOStreamPropertyDeckCueing";
case kCMIOStreamPropertyInitialPresentationTimeStampForLinkedAndSyncedAudio:
return @"kCMIOStreamPropertyInitialPresentationTimeStampForLinkedAndSyncedAudio";
case kCMIOStreamPropertyScheduledOutputNotificationProc:
return @"kCMIOStreamPropertyScheduledOutputNotificationProc";
case kCMIOStreamPropertyPreferredFormatDescription:
return @"kCMIOStreamPropertyPreferredFormatDescription";
case kCMIOStreamPropertyPreferredFrameRate:
return @"kCMIOStreamPropertyPreferredFrameRate";
case kCMIOControlPropertyScope:
return @"kCMIOControlPropertyScope";
case kCMIOControlPropertyElement:
return @"kCMIOControlPropertyElement";
case kCMIOControlPropertyVariant:
return @"kCMIOControlPropertyVariant";
case kCMIOHardwarePropertyProcessIsMaster:
return @"kCMIOHardwarePropertyProcessIsMaster";
case kCMIOHardwarePropertyIsInitingOrExiting:
return @"kCMIOHardwarePropertyIsInitingOrExiting";
case kCMIOHardwarePropertyDevices:
return @"kCMIOHardwarePropertyDevices";
case kCMIOHardwarePropertyDefaultInputDevice:
return @"kCMIOHardwarePropertyDefaultInputDevice";
case kCMIOHardwarePropertyDefaultOutputDevice:
return @"kCMIOHardwarePropertyDefaultOutputDevice";
case kCMIOHardwarePropertyDeviceForUID:
return @"kCMIOHardwarePropertyDeviceForUID";
case kCMIOHardwarePropertySleepingIsAllowed:
return @"kCMIOHardwarePropertySleepingIsAllowed";
case kCMIOHardwarePropertyUnloadingIsAllowed:
return @"kCMIOHardwarePropertyUnloadingIsAllowed";
case kCMIOHardwarePropertyPlugInForBundleID:
return @"kCMIOHardwarePropertyPlugInForBundleID";
case kCMIOHardwarePropertyUserSessionIsActiveOrHeadless:
return @"kCMIOHardwarePropertyUserSessionIsActiveOrHeadless";
case kCMIOHardwarePropertySuspendedBySystem:
return @"kCMIOHardwarePropertySuspendedBySystem";
case kCMIOHardwarePropertyAllowScreenCaptureDevices:
return @"kCMIOHardwarePropertyAllowScreenCaptureDevices";
case kCMIOHardwarePropertyAllowWirelessScreenCaptureDevices:
return @"kCMIOHardwarePropertyAllowWirelessScreenCaptureDevices";
default:
uint8_t *chars = (uint8_t *)&selector;
return [NSString stringWithFormat:@"Unknown selector: %c%c%c%c",
chars[0], chars[1], chars[2],
chars[3]];
}
switch (selector) {
case kCMIODevicePropertyPlugIn:
return @"kCMIODevicePropertyPlugIn";
case kCMIODevicePropertyDeviceUID:
return @"kCMIODevicePropertyDeviceUID";
case kCMIODevicePropertyModelUID:
return @"kCMIODevicePropertyModelUID";
case kCMIODevicePropertyTransportType:
return @"kCMIODevicePropertyTransportType";
case kCMIODevicePropertyDeviceIsAlive:
return @"kCMIODevicePropertyDeviceIsAlive";
case kCMIODevicePropertyDeviceHasChanged:
return @"kCMIODevicePropertyDeviceHasChanged";
case kCMIODevicePropertyDeviceIsRunning:
return @"kCMIODevicePropertyDeviceIsRunning";
case kCMIODevicePropertyDeviceIsRunningSomewhere:
return @"kCMIODevicePropertyDeviceIsRunningSomewhere";
case kCMIODevicePropertyDeviceCanBeDefaultDevice:
return @"kCMIODevicePropertyDeviceCanBeDefaultDevice";
case kCMIODevicePropertyHogMode:
return @"kCMIODevicePropertyHogMode";
case kCMIODevicePropertyLatency:
return @"kCMIODevicePropertyLatency";
case kCMIODevicePropertyStreams:
return @"kCMIODevicePropertyStreams";
case kCMIODevicePropertyStreamConfiguration:
return @"kCMIODevicePropertyStreamConfiguration";
case kCMIODevicePropertyDeviceMaster:
return @"kCMIODevicePropertyDeviceMaster";
case kCMIODevicePropertyExcludeNonDALAccess:
return @"kCMIODevicePropertyExcludeNonDALAccess";
case kCMIODevicePropertyClientSyncDiscontinuity:
return @"kCMIODevicePropertyClientSyncDiscontinuity";
case kCMIODevicePropertySMPTETimeCallback:
return @"kCMIODevicePropertySMPTETimeCallback";
case kCMIODevicePropertyCanProcessAVCCommand:
return @"kCMIODevicePropertyCanProcessAVCCommand";
case kCMIODevicePropertyAVCDeviceType:
return @"kCMIODevicePropertyAVCDeviceType";
case kCMIODevicePropertyAVCDeviceSignalMode:
return @"kCMIODevicePropertyAVCDeviceSignalMode";
case kCMIODevicePropertyCanProcessRS422Command:
return @"kCMIODevicePropertyCanProcessRS422Command";
case kCMIODevicePropertyLinkedCoreAudioDeviceUID:
return @"kCMIODevicePropertyLinkedCoreAudioDeviceUID";
case kCMIODevicePropertyVideoDigitizerComponents:
return @"kCMIODevicePropertyVideoDigitizerComponents";
case kCMIODevicePropertySuspendedByUser:
return @"kCMIODevicePropertySuspendedByUser";
case kCMIODevicePropertyLinkedAndSyncedCoreAudioDeviceUID:
return @"kCMIODevicePropertyLinkedAndSyncedCoreAudioDeviceUID";
case kCMIODevicePropertyIIDCInitialUnitSpace:
return @"kCMIODevicePropertyIIDCInitialUnitSpace";
case kCMIODevicePropertyIIDCCSRData:
return @"kCMIODevicePropertyIIDCCSRData";
case kCMIODevicePropertyCanSwitchFrameRatesWithoutFrameDrops:
return @"kCMIODevicePropertyCanSwitchFrameRatesWithoutFrameDrops";
case kCMIODevicePropertyLocation:
return @"kCMIODevicePropertyLocation";
case kCMIODevicePropertyDeviceHasStreamingError:
return @"kCMIODevicePropertyDeviceHasStreamingError";
case kCMIODevicePropertyScopeInput:
return @"kCMIODevicePropertyScopeInput";
case kCMIODevicePropertyScopeOutput:
return @"kCMIODevicePropertyScopeOutput";
case kCMIODevicePropertyScopePlayThrough:
return @"kCMIODevicePropertyScopePlayThrough";
case kCMIOObjectPropertyClass:
return @"kCMIOObjectPropertyClass";
case kCMIOObjectPropertyOwner:
return @"kCMIOObjectPropertyOwner";
case kCMIOObjectPropertyCreator:
return @"kCMIOObjectPropertyCreator";
case kCMIOObjectPropertyName:
return @"kCMIOObjectPropertyName";
case kCMIOObjectPropertyManufacturer:
return @"kCMIOObjectPropertyManufacturer";
case kCMIOObjectPropertyElementName:
return @"kCMIOObjectPropertyElementName";
case kCMIOObjectPropertyElementCategoryName:
return @"kCMIOObjectPropertyElementCategoryName";
case kCMIOObjectPropertyElementNumberName:
return @"kCMIOObjectPropertyElementNumberName";
case kCMIOObjectPropertyOwnedObjects:
return @"kCMIOObjectPropertyOwnedObjects";
case kCMIOObjectPropertyListenerAdded:
return @"kCMIOObjectPropertyListenerAdded";
case kCMIOObjectPropertyListenerRemoved:
return @"kCMIOObjectPropertyListenerRemoved";
case kCMIOStreamPropertyDirection:
return @"kCMIOStreamPropertyDirection";
case kCMIOStreamPropertyTerminalType:
return @"kCMIOStreamPropertyTerminalType";
case kCMIOStreamPropertyStartingChannel:
return @"kCMIOStreamPropertyStartingChannel";
// Same value as kCMIODevicePropertyLatency
// case kCMIOStreamPropertyLatency:
// return @"kCMIOStreamPropertyLatency";
case kCMIOStreamPropertyFormatDescription:
return @"kCMIOStreamPropertyFormatDescription";
case kCMIOStreamPropertyFormatDescriptions:
return @"kCMIOStreamPropertyFormatDescriptions";
case kCMIOStreamPropertyStillImage:
return @"kCMIOStreamPropertyStillImage";
case kCMIOStreamPropertyStillImageFormatDescriptions:
return @"kCMIOStreamPropertyStillImageFormatDescriptions";
case kCMIOStreamPropertyFrameRate:
return @"kCMIOStreamPropertyFrameRate";
case kCMIOStreamPropertyMinimumFrameRate:
return @"kCMIOStreamPropertyMinimumFrameRate";
case kCMIOStreamPropertyFrameRates:
return @"kCMIOStreamPropertyFrameRates";
case kCMIOStreamPropertyFrameRateRanges:
return @"kCMIOStreamPropertyFrameRateRanges";
case kCMIOStreamPropertyNoDataTimeoutInMSec:
return @"kCMIOStreamPropertyNoDataTimeoutInMSec";
case kCMIOStreamPropertyDeviceSyncTimeoutInMSec:
return @"kCMIOStreamPropertyDeviceSyncTimeoutInMSec";
case kCMIOStreamPropertyNoDataEventCount:
return @"kCMIOStreamPropertyNoDataEventCount";
case kCMIOStreamPropertyOutputBufferUnderrunCount:
return @"kCMIOStreamPropertyOutputBufferUnderrunCount";
case kCMIOStreamPropertyOutputBufferRepeatCount:
return @"kCMIOStreamPropertyOutputBufferRepeatCount";
case kCMIOStreamPropertyOutputBufferQueueSize:
return @"kCMIOStreamPropertyOutputBufferQueueSize";
case kCMIOStreamPropertyOutputBuffersRequiredForStartup:
return @"kCMIOStreamPropertyOutputBuffersRequiredForStartup";
case kCMIOStreamPropertyOutputBuffersNeededForThrottledPlayback:
return @"kCMIOStreamPropertyOutputBuffersNeededForThrottledPlayback";
case kCMIOStreamPropertyFirstOutputPresentationTimeStamp:
return @"kCMIOStreamPropertyFirstOutputPresentationTimeStamp";
case kCMIOStreamPropertyEndOfData:
return @"kCMIOStreamPropertyEndOfData";
case kCMIOStreamPropertyClock:
return @"kCMIOStreamPropertyClock";
case kCMIOStreamPropertyCanProcessDeckCommand:
return @"kCMIOStreamPropertyCanProcessDeckCommand";
case kCMIOStreamPropertyDeck:
return @"kCMIOStreamPropertyDeck";
case kCMIOStreamPropertyDeckFrameNumber:
return @"kCMIOStreamPropertyDeckFrameNumber";
case kCMIOStreamPropertyDeckDropness:
return @"kCMIOStreamPropertyDeckDropness";
case kCMIOStreamPropertyDeckThreaded:
return @"kCMIOStreamPropertyDeckThreaded";
case kCMIOStreamPropertyDeckLocal:
return @"kCMIOStreamPropertyDeckLocal";
case kCMIOStreamPropertyDeckCueing:
return @"kCMIOStreamPropertyDeckCueing";
case kCMIOStreamPropertyInitialPresentationTimeStampForLinkedAndSyncedAudio:
return @"kCMIOStreamPropertyInitialPresentationTimeStampForLinkedAndSyncedAudio";
case kCMIOStreamPropertyScheduledOutputNotificationProc:
return @"kCMIOStreamPropertyScheduledOutputNotificationProc";
case kCMIOStreamPropertyPreferredFormatDescription:
return @"kCMIOStreamPropertyPreferredFormatDescription";
case kCMIOStreamPropertyPreferredFrameRate:
return @"kCMIOStreamPropertyPreferredFrameRate";
case kCMIOControlPropertyScope:
return @"kCMIOControlPropertyScope";
case kCMIOControlPropertyElement:
return @"kCMIOControlPropertyElement";
case kCMIOControlPropertyVariant:
return @"kCMIOControlPropertyVariant";
case kCMIOHardwarePropertyProcessIsMaster:
return @"kCMIOHardwarePropertyProcessIsMaster";
case kCMIOHardwarePropertyIsInitingOrExiting:
return @"kCMIOHardwarePropertyIsInitingOrExiting";
case kCMIOHardwarePropertyDevices:
return @"kCMIOHardwarePropertyDevices";
case kCMIOHardwarePropertyDefaultInputDevice:
return @"kCMIOHardwarePropertyDefaultInputDevice";
case kCMIOHardwarePropertyDefaultOutputDevice:
return @"kCMIOHardwarePropertyDefaultOutputDevice";
case kCMIOHardwarePropertyDeviceForUID:
return @"kCMIOHardwarePropertyDeviceForUID";
case kCMIOHardwarePropertySleepingIsAllowed:
return @"kCMIOHardwarePropertySleepingIsAllowed";
case kCMIOHardwarePropertyUnloadingIsAllowed:
return @"kCMIOHardwarePropertyUnloadingIsAllowed";
case kCMIOHardwarePropertyPlugInForBundleID:
return @"kCMIOHardwarePropertyPlugInForBundleID";
case kCMIOHardwarePropertyUserSessionIsActiveOrHeadless:
return @"kCMIOHardwarePropertyUserSessionIsActiveOrHeadless";
case kCMIOHardwarePropertySuspendedBySystem:
return @"kCMIOHardwarePropertySuspendedBySystem";
case kCMIOHardwarePropertyAllowScreenCaptureDevices:
return @"kCMIOHardwarePropertyAllowScreenCaptureDevices";
case kCMIOHardwarePropertyAllowWirelessScreenCaptureDevices:
return @"kCMIOHardwarePropertyAllowWirelessScreenCaptureDevices";
default:
uint8_t *chars = (uint8_t *) &selector;
return [NSString stringWithFormat:@"Unknown selector: %c%c%c%c", chars[0], chars[1], chars[2], chars[3]];
}
}
+ (BOOL)IsBridgedTypeForSelector:(CMIOObjectPropertySelector)selector
{
switch (selector) {
case kCMIOObjectPropertyName:
case kCMIOObjectPropertyManufacturer:
case kCMIOObjectPropertyElementName:
case kCMIOObjectPropertyElementCategoryName:
case kCMIOObjectPropertyElementNumberName:
case kCMIODevicePropertyDeviceUID:
case kCMIODevicePropertyModelUID:
case kCMIOStreamPropertyFormatDescriptions:
case kCMIOStreamPropertyFormatDescription:
case kCMIOStreamPropertyClock:
return YES;
default:
return NO;
}
switch (selector) {
case kCMIOObjectPropertyName:
case kCMIOObjectPropertyManufacturer:
case kCMIOObjectPropertyElementName:
case kCMIOObjectPropertyElementCategoryName:
case kCMIOObjectPropertyElementNumberName:
case kCMIODevicePropertyDeviceUID:
case kCMIODevicePropertyModelUID:
case kCMIOStreamPropertyFormatDescriptions:
case kCMIOStreamPropertyFormatDescription:
case kCMIOStreamPropertyClock:
return YES;
default:
return NO;
}
}
+ (OBSDALObjectStore *)SharedObjectStore
{
static OBSDALObjectStore *sObjectStore = nil;
static dispatch_once_t sOnceToken;
dispatch_once(&sOnceToken, ^{
sObjectStore = [[self alloc] init];
});
return sObjectStore;
static OBSDALObjectStore *sObjectStore = nil;
static dispatch_once_t sOnceToken;
dispatch_once(&sOnceToken, ^{
sObjectStore = [[self alloc] init];
});
return sObjectStore;
}
+ (NSObject<CMIOObject> *)GetObjectWithId:(CMIOObjectID)objectId
{
return [[OBSDALObjectStore SharedObjectStore] getObject:objectId];
return [[OBSDALObjectStore SharedObjectStore] getObject:objectId];
}
- (id)init
{
if (self = [super init]) {
self.objectMap = [[NSMutableDictionary alloc] init];
}
return self;
if (self = [super init]) {
self.objectMap = [[NSMutableDictionary alloc] init];
}
return self;
}
- (NSObject<CMIOObject> *)getObject:(CMIOObjectID)objectID
{
return self.objectMap[@(objectID)];
return self.objectMap[@(objectID)];
}
- (void)setObject:(id<CMIOObject>)object forObjectId:(CMIOObjectID)objectId
{
self.objectMap[@(objectId)] = object;
self.objectMap[@(objectId)] = object;
}
@end
@@ -21,9 +21,9 @@
#import "OBSDALMachClient.h"
#import "OBSDALStream.h"
#define kTestCardWidthKey @"obs-mac-virtualcam-test-card-width"
#define kTestCardWidthKey @"obs-mac-virtualcam-test-card-width"
#define kTestCardHeightKey @"obs-mac-virtualcam-test-card-height"
#define kTestCardFPSKey @"obs-mac-virtualcam-test-card-fps"
#define kTestCardFPSKey @"obs-mac-virtualcam-test-card-fps"
NS_ASSUME_NONNULL_BEGIN
@@ -22,20 +22,20 @@
#import "Logging.h"
typedef enum {
PlugInStateNotStarted = 0,
PlugInStateWaitingForServer,
PlugInStateReceivingFrames,
PlugInStateNotStarted = 0,
PlugInStateWaitingForServer,
PlugInStateReceivingFrames,
} OBSDALPlugInState;
@interface OBSDALPlugin () <MachClientDelegate> {
//! Serial queue for all state changes that need to be concerned with thread safety
dispatch_queue_t _stateQueue;
//! Serial queue for all state changes that need to be concerned with thread safety
dispatch_queue_t _stateQueue;
//! Repeated timer for driving the mach server re-connection
dispatch_source_t _machConnectTimer;
//! Repeated timer for driving the mach server re-connection
dispatch_source_t _machConnectTimer;
//! Timeout timer when we haven't received frames for 5s
dispatch_source_t _timeoutTimer;
//! Timeout timer when we haven't received frames for 5s
dispatch_source_t _timeoutTimer;
}
@property OBSDALPlugInState state;
@property OBSDALMachClient *machClient;
@@ -46,209 +46,186 @@ typedef enum {
+ (OBSDALPlugin *)SharedPlugIn
{
static OBSDALPlugin *sPlugIn = nil;
static dispatch_once_t sOnceToken;
dispatch_once(&sOnceToken, ^{
sPlugIn = [[self alloc] init];
});
return sPlugIn;
static OBSDALPlugin *sPlugIn = nil;
static dispatch_once_t sOnceToken;
dispatch_once(&sOnceToken, ^{
sPlugIn = [[self alloc] init];
});
return sPlugIn;
}
- (instancetype)init
{
if (self = [super init]) {
_stateQueue = dispatch_queue_create(
"com.obsproject.obs-mac-virtualcam.dal.state",
DISPATCH_QUEUE_SERIAL);
if (self = [super init]) {
_stateQueue = dispatch_queue_create("com.obsproject.obs-mac-virtualcam.dal.state", DISPATCH_QUEUE_SERIAL);
_timeoutTimer = dispatch_source_create(
DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _stateQueue);
__weak __typeof(self) weakSelf = self;
dispatch_source_set_event_handler(_timeoutTimer, ^{
if (weakSelf.state == PlugInStateReceivingFrames) {
DLog(@"No frames received for 5s, restarting connection");
[self stopStream];
[self startStream];
}
});
_timeoutTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _stateQueue);
__weak __typeof(self) weakSelf = self;
dispatch_source_set_event_handler(_timeoutTimer, ^{
if (weakSelf.state == PlugInStateReceivingFrames) {
DLog(@"No frames received for 5s, restarting connection");
[self stopStream];
[self startStream];
}
});
_machClient = [[OBSDALMachClient alloc] init];
_machClient.delegate = self;
_machClient = [[OBSDALMachClient alloc] init];
_machClient.delegate = self;
_machConnectTimer = dispatch_source_create(
DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _stateQueue);
dispatch_time_t startTime = dispatch_time(DISPATCH_TIME_NOW, 0);
uint64_t intervalTime = (int64_t)(1 * NSEC_PER_SEC);
dispatch_source_set_timer(_machConnectTimer, startTime,
intervalTime, 0);
_machConnectTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _stateQueue);
dispatch_time_t startTime = dispatch_time(DISPATCH_TIME_NOW, 0);
uint64_t intervalTime = (int64_t) (1 * NSEC_PER_SEC);
dispatch_source_set_timer(_machConnectTimer, startTime, intervalTime, 0);
dispatch_source_set_event_handler(_machConnectTimer, ^{
__strong __typeof(weakSelf) strongSelf = weakSelf;
if (![[strongSelf machClient] isServerAvailable]) {
DLog(@"Server is not available");
} else if (strongSelf.state ==
PlugInStateWaitingForServer) {
DLog(@"Attempting connection");
[[strongSelf machClient] connectToServer];
}
});
}
return self;
dispatch_source_set_event_handler(_machConnectTimer, ^{
__strong __typeof(weakSelf) strongSelf = weakSelf;
if (![[strongSelf machClient] isServerAvailable]) {
DLog(@"Server is not available");
} else if (strongSelf.state == PlugInStateWaitingForServer) {
DLog(@"Attempting connection");
[[strongSelf machClient] connectToServer];
}
});
}
return self;
}
- (void)startStream
{
dispatch_async(_stateQueue, ^{
if (_state == PlugInStateNotStarted) {
dispatch_resume(_machConnectTimer);
[self.stream startServingDefaultFrames];
_state = PlugInStateWaitingForServer;
}
});
dispatch_async(_stateQueue, ^{
if (_state == PlugInStateNotStarted) {
dispatch_resume(_machConnectTimer);
[self.stream startServingDefaultFrames];
_state = PlugInStateWaitingForServer;
}
});
}
- (void)stopStream
{
dispatch_async(_stateQueue, ^{
if (_state == PlugInStateWaitingForServer) {
dispatch_suspend(_machConnectTimer);
[self.stream stopServingDefaultFrames];
} else if (_state == PlugInStateReceivingFrames) {
// TODO: Disconnect from the mach server?
dispatch_suspend(_timeoutTimer);
}
_state = PlugInStateNotStarted;
});
dispatch_async(_stateQueue, ^{
if (_state == PlugInStateWaitingForServer) {
dispatch_suspend(_machConnectTimer);
[self.stream stopServingDefaultFrames];
} else if (_state == PlugInStateReceivingFrames) {
// TODO: Disconnect from the mach server?
dispatch_suspend(_timeoutTimer);
}
_state = PlugInStateNotStarted;
});
}
- (void)initialize
{
}
{}
- (void)teardown
{
}
{}
#pragma mark - CMIOObject
- (BOOL)hasPropertyWithAddress:(CMIOObjectPropertyAddress)address
{
switch (address.mSelector) {
case kCMIOObjectPropertyName:
return true;
default:
DLog(@"PlugIn unhandled hasPropertyWithAddress for %@",
[OBSDALObjectStore
StringFromPropertySelector:address.mSelector]);
return false;
};
switch (address.mSelector) {
case kCMIOObjectPropertyName:
return true;
default:
DLog(@"PlugIn unhandled hasPropertyWithAddress for %@",
[OBSDALObjectStore StringFromPropertySelector:address.mSelector]);
return false;
};
}
- (BOOL)isPropertySettableWithAddress:(CMIOObjectPropertyAddress)address
{
switch (address.mSelector) {
case kCMIOObjectPropertyName:
return false;
default:
DLog(@"PlugIn unhandled isPropertySettableWithAddress for %@",
[OBSDALObjectStore
StringFromPropertySelector:address.mSelector]);
return false;
};
switch (address.mSelector) {
case kCMIOObjectPropertyName:
return false;
default:
DLog(@"PlugIn unhandled isPropertySettableWithAddress for %@",
[OBSDALObjectStore StringFromPropertySelector:address.mSelector]);
return false;
};
}
- (UInt32)getPropertyDataSizeWithAddress:(CMIOObjectPropertyAddress)address
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(const void *)qualifierData
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(const void *)qualifierData
{
switch (address.mSelector) {
case kCMIOObjectPropertyName:
return sizeof(CFStringRef);
default:
DLog(@"PlugIn unhandled getPropertyDataSizeWithAddress for %@",
[OBSDALObjectStore
StringFromPropertySelector:address.mSelector]);
return 0;
};
switch (address.mSelector) {
case kCMIOObjectPropertyName:
return sizeof(CFStringRef);
default:
DLog(@"PlugIn unhandled getPropertyDataSizeWithAddress for %@",
[OBSDALObjectStore StringFromPropertySelector:address.mSelector]);
return 0;
};
}
- (void)getPropertyDataWithAddress:(CMIOObjectPropertyAddress)address
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(nonnull const void *)qualifierData
dataSize:(UInt32)dataSize
dataUsed:(nonnull UInt32 *)dataUsed
data:(nonnull void *)data
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(nonnull const void *)qualifierData
dataSize:(UInt32)dataSize
dataUsed:(nonnull UInt32 *)dataUsed
data:(nonnull void *)data
{
switch (address.mSelector) {
case kCMIOObjectPropertyName:
*static_cast<CFStringRef *>(data) =
CFSTR("OBS Virtual Camera Plugin");
*dataUsed = sizeof(CFStringRef);
return;
default:
DLog(@"PlugIn unhandled getPropertyDataWithAddress for %@",
[OBSDALObjectStore
StringFromPropertySelector:address.mSelector]);
return;
};
switch (address.mSelector) {
case kCMIOObjectPropertyName:
*static_cast<CFStringRef *>(data) = CFSTR("OBS Virtual Camera Plugin");
*dataUsed = sizeof(CFStringRef);
return;
default:
DLog(@"PlugIn unhandled getPropertyDataWithAddress for %@",
[OBSDALObjectStore StringFromPropertySelector:address.mSelector]);
return;
};
}
- (void)setPropertyDataWithAddress:(CMIOObjectPropertyAddress)address
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(nonnull const void *)qualifierData
dataSize:(UInt32)dataSize
data:(nonnull const void *)data
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(nonnull const void *)qualifierData
dataSize:(UInt32)dataSize
data:(nonnull const void *)data
{
DLog(@"PlugIn unhandled setPropertyDataWithAddress for %@",
[OBSDALObjectStore StringFromPropertySelector:address.mSelector]);
DLog(@"PlugIn unhandled setPropertyDataWithAddress for %@",
[OBSDALObjectStore StringFromPropertySelector:address.mSelector]);
}
#pragma mark - MachClientDelegate
- (void)receivedPixelBuffer:(CVPixelBufferRef)frame
timestamp:(uint64_t)timestamp
fpsNumerator:(uint32_t)fpsNumerator
fpsDenominator:(uint32_t)fpsDenominator
timestamp:(uint64_t)timestamp
fpsNumerator:(uint32_t)fpsNumerator
fpsDenominator:(uint32_t)fpsDenominator
{
size_t width = CVPixelBufferGetWidth(frame);
size_t height = CVPixelBufferGetHeight(frame);
size_t width = CVPixelBufferGetWidth(frame);
size_t height = CVPixelBufferGetHeight(frame);
dispatch_sync(_stateQueue, ^{
if (_state == PlugInStateWaitingForServer) {
NSUserDefaults *defaults =
[NSUserDefaults standardUserDefaults];
[defaults setInteger:(long)width
forKey:kTestCardWidthKey];
[defaults setInteger:(long)height
forKey:kTestCardHeightKey];
[defaults setDouble:(double)fpsNumerator /
(double)fpsDenominator
forKey:kTestCardFPSKey];
dispatch_sync(_stateQueue, ^{
if (_state == PlugInStateWaitingForServer) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setInteger:(long) width forKey:kTestCardWidthKey];
[defaults setInteger:(long) height forKey:kTestCardHeightKey];
[defaults setDouble:(double) fpsNumerator / (double) fpsDenominator forKey:kTestCardFPSKey];
dispatch_suspend(_machConnectTimer);
[self.stream stopServingDefaultFrames];
dispatch_resume(_timeoutTimer);
_state = PlugInStateReceivingFrames;
}
});
dispatch_suspend(_machConnectTimer);
[self.stream stopServingDefaultFrames];
dispatch_resume(_timeoutTimer);
_state = PlugInStateReceivingFrames;
}
});
// Add 5 more seconds onto the timeout timer
dispatch_source_set_timer(
_timeoutTimer,
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)),
(uint64_t)(5.0 * NSEC_PER_SEC), (1ull * NSEC_PER_SEC) / 10);
// Add 5 more seconds onto the timeout timer
dispatch_source_set_timer(_timeoutTimer, dispatch_time(DISPATCH_TIME_NOW, (int64_t) (5.0 * NSEC_PER_SEC)),
(uint64_t) (5.0 * NSEC_PER_SEC), (1ull * NSEC_PER_SEC) / 10);
[self.stream queuePixelBuffer:frame
timestamp:timestamp
fpsNumerator:fpsNumerator
fpsDenominator:fpsDenominator];
[self.stream queuePixelBuffer:frame timestamp:timestamp fpsNumerator:fpsNumerator fpsDenominator:fpsDenominator];
}
- (void)receivedStop
{
DLogFunc(@"Restarting connection");
[self stopStream];
[self startStream];
DLogFunc(@"Restarting connection");
[self stopStream];
[self startStream];
}
@end
@@ -32,399 +32,337 @@ static UInt32 sRefCount = 0;
ULONG HardwarePlugIn_AddRef(CMIOHardwarePlugInRef)
{
sRefCount += 1;
DLogFunc(@"sRefCount now = %d", sRefCount);
return sRefCount;
sRefCount += 1;
DLogFunc(@"sRefCount now = %d", sRefCount);
return sRefCount;
}
ULONG HardwarePlugIn_Release(CMIOHardwarePlugInRef)
{
sRefCount -= 1;
DLogFunc(@"sRefCount now = %d", sRefCount);
return sRefCount;
sRefCount -= 1;
DLogFunc(@"sRefCount now = %d", sRefCount);
return sRefCount;
}
HRESULT HardwarePlugIn_QueryInterface(CMIOHardwarePlugInRef, REFIID uuid,
LPVOID *interface)
HRESULT HardwarePlugIn_QueryInterface(CMIOHardwarePlugInRef, REFIID uuid, LPVOID *interface)
{
if (!interface) {
DLogFunc(@"Received an empty interface");
return E_POINTER;
}
if (!interface) {
DLogFunc(@"Received an empty interface");
return E_POINTER;
}
// Set the returned interface to NULL in case the UUIDs don't match
*interface = NULL;
// Set the returned interface to NULL in case the UUIDs don't match
*interface = NULL;
// Create a CoreFoundation UUIDRef for the requested interface.
CFUUIDRef cfUuid = CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, uuid);
CFStringRef uuidString = CFUUIDCreateString(NULL, cfUuid);
CFStringRef hardwarePluginUuid =
CFUUIDCreateString(NULL, kCMIOHardwarePlugInInterfaceID);
CFRelease(cfUuid);
// Create a CoreFoundation UUIDRef for the requested interface.
CFUUIDRef cfUuid = CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, uuid);
CFStringRef uuidString = CFUUIDCreateString(NULL, cfUuid);
CFStringRef hardwarePluginUuid = CFUUIDCreateString(NULL, kCMIOHardwarePlugInInterfaceID);
CFRelease(cfUuid);
if (CFEqual(uuidString, hardwarePluginUuid)) {
// Return the interface;
sRefCount += 1;
*interface = OBSDALPlugInRef();
CFRelease(hardwarePluginUuid);
CFRelease(uuidString);
return kCMIOHardwareNoError;
} else {
DLogFunc(@"ERR Queried for some weird UUID %@", uuidString);
}
CFRelease(hardwarePluginUuid);
CFRelease(uuidString);
return E_NOINTERFACE;
if (CFEqual(uuidString, hardwarePluginUuid)) {
// Return the interface;
sRefCount += 1;
*interface = OBSDALPlugInRef();
CFRelease(hardwarePluginUuid);
CFRelease(uuidString);
return kCMIOHardwareNoError;
} else {
DLogFunc(@"ERR Queried for some weird UUID %@", uuidString);
}
CFRelease(hardwarePluginUuid);
CFRelease(uuidString);
return E_NOINTERFACE;
}
// I think this is deprecated, seems that HardwarePlugIn_InitializeWithObjectID gets called instead
OSStatus HardwarePlugIn_Initialize(CMIOHardwarePlugInRef self
__attribute__((unused)))
OSStatus HardwarePlugIn_Initialize(CMIOHardwarePlugInRef self __attribute__((unused)))
{
DLogFunc(@"ERR self=%p", self);
return kCMIOHardwareUnspecifiedError;
DLogFunc(@"ERR self=%p", self);
return kCMIOHardwareUnspecifiedError;
}
OSStatus HardwarePlugIn_InitializeWithObjectID(CMIOHardwarePlugInRef self,
CMIOObjectID objectID)
OSStatus HardwarePlugIn_InitializeWithObjectID(CMIOHardwarePlugInRef self, CMIOObjectID objectID)
{
DLogFunc(@"self=%p", self);
DLogFunc(@"self=%p", self);
OSStatus error = kCMIOHardwareNoError;
OSStatus error = kCMIOHardwareNoError;
OBSDALPlugin *plugIn = [OBSDALPlugin SharedPlugIn];
plugIn.objectId = objectID;
[[OBSDALObjectStore SharedObjectStore] setObject:plugIn
forObjectId:objectID];
OBSDALPlugin *plugIn = [OBSDALPlugin SharedPlugIn];
plugIn.objectId = objectID;
[[OBSDALObjectStore SharedObjectStore] setObject:plugIn forObjectId:objectID];
OBSDALDevice *device = [[OBSDALDevice alloc] init];
CMIOObjectID deviceId;
error = CMIOObjectCreate(OBSDALPlugInRef(), kCMIOObjectSystemObject,
kCMIODeviceClassID, &deviceId);
if (error != noErr) {
DLog(@"CMIOObjectCreate Error %d", error);
return error;
}
device.objectId = deviceId;
device.pluginId = objectID;
[[OBSDALObjectStore SharedObjectStore] setObject:device
forObjectId:deviceId];
OBSDALDevice *device = [[OBSDALDevice alloc] init];
CMIOObjectID deviceId;
error = CMIOObjectCreate(OBSDALPlugInRef(), kCMIOObjectSystemObject, kCMIODeviceClassID, &deviceId);
if (error != noErr) {
DLog(@"CMIOObjectCreate Error %d", error);
return error;
}
device.objectId = deviceId;
device.pluginId = objectID;
[[OBSDALObjectStore SharedObjectStore] setObject:device forObjectId:deviceId];
OBSDALStream *stream = [[OBSDALStream alloc] init];
CMIOObjectID streamId;
error = CMIOObjectCreate(OBSDALPlugInRef(), deviceId,
kCMIOStreamClassID, &streamId);
if (error != noErr) {
DLog(@"CMIOObjectCreate Error %d", error);
return error;
}
stream.objectId = streamId;
[[OBSDALObjectStore SharedObjectStore] setObject:stream
forObjectId:streamId];
device.streamId = streamId;
plugIn.stream = stream;
OBSDALStream *stream = [[OBSDALStream alloc] init];
CMIOObjectID streamId;
error = CMIOObjectCreate(OBSDALPlugInRef(), deviceId, kCMIOStreamClassID, &streamId);
if (error != noErr) {
DLog(@"CMIOObjectCreate Error %d", error);
return error;
}
stream.objectId = streamId;
[[OBSDALObjectStore SharedObjectStore] setObject:stream forObjectId:streamId];
device.streamId = streamId;
plugIn.stream = stream;
// Tell the system about the Device
error = CMIOObjectsPublishedAndDied(
OBSDALPlugInRef(), kCMIOObjectSystemObject, 1, &deviceId, 0, 0);
if (error != kCMIOHardwareNoError) {
DLog(@"CMIOObjectsPublishedAndDied plugin/device Error %d",
error);
return error;
}
// Tell the system about the Device
error = CMIOObjectsPublishedAndDied(OBSDALPlugInRef(), kCMIOObjectSystemObject, 1, &deviceId, 0, 0);
if (error != kCMIOHardwareNoError) {
DLog(@"CMIOObjectsPublishedAndDied plugin/device Error %d", error);
return error;
}
// Tell the system about the Stream
error = CMIOObjectsPublishedAndDied(OBSDALPlugInRef(), deviceId, 1,
&streamId, 0, 0);
if (error != kCMIOHardwareNoError) {
DLog(@"CMIOObjectsPublishedAndDied device/stream Error %d",
error);
return error;
}
// Tell the system about the Stream
error = CMIOObjectsPublishedAndDied(OBSDALPlugInRef(), deviceId, 1, &streamId, 0, 0);
if (error != kCMIOHardwareNoError) {
DLog(@"CMIOObjectsPublishedAndDied device/stream Error %d", error);
return error;
}
return error;
return error;
}
OSStatus HardwarePlugIn_Teardown(CMIOHardwarePlugInRef self)
{
DLogFunc(@"self=%p", self);
DLogFunc(@"self=%p", self);
OSStatus error = kCMIOHardwareNoError;
OSStatus error = kCMIOHardwareNoError;
OBSDALPlugin *plugIn = [OBSDALPlugin SharedPlugIn];
[plugIn teardown];
OBSDALPlugin *plugIn = [OBSDALPlugin SharedPlugIn];
[plugIn teardown];
return error;
return error;
}
#pragma mark CMIOObject Operations
void HardwarePlugIn_ObjectShow(CMIOHardwarePlugInRef self, CMIOObjectID)
{
DLogFunc(@"self=%p", self);
DLogFunc(@"self=%p", self);
}
Boolean
HardwarePlugIn_ObjectHasProperty(CMIOHardwarePlugInRef, CMIOObjectID objectID,
const CMIOObjectPropertyAddress *address)
Boolean HardwarePlugIn_ObjectHasProperty(CMIOHardwarePlugInRef, CMIOObjectID objectID,
const CMIOObjectPropertyAddress *address)
{
NSObject<CMIOObject> *object =
[OBSDALObjectStore GetObjectWithId:objectID];
NSObject<CMIOObject> *object = [OBSDALObjectStore GetObjectWithId:objectID];
if (object == nil) {
DLogFunc(@"ERR nil object");
return false;
}
if (object == nil) {
DLogFunc(@"ERR nil object");
return false;
}
Boolean answer = [object hasPropertyWithAddress:*address];
Boolean answer = [object hasPropertyWithAddress:*address];
return answer;
return answer;
}
OSStatus HardwarePlugIn_ObjectIsPropertySettable(
CMIOHardwarePlugInRef self, CMIOObjectID objectID,
const CMIOObjectPropertyAddress *address, Boolean *isSettable)
OSStatus HardwarePlugIn_ObjectIsPropertySettable(CMIOHardwarePlugInRef self, CMIOObjectID objectID,
const CMIOObjectPropertyAddress *address, Boolean *isSettable)
{
NSObject<CMIOObject> *object = [OBSDALObjectStore GetObjectWithId:objectID];
NSObject<CMIOObject> *object =
[OBSDALObjectStore GetObjectWithId:objectID];
if (object == nil) {
DLogFunc(@"ERR nil object");
return kCMIOHardwareBadObjectError;
}
if (object == nil) {
DLogFunc(@"ERR nil object");
return kCMIOHardwareBadObjectError;
}
*isSettable = [object isPropertySettableWithAddress:*address];
*isSettable = [object isPropertySettableWithAddress:*address];
DLogFunc(@"%@(%d) %@ self=%p settable=%d", NSStringFromClass([object class]), objectID,
[OBSDALObjectStore StringFromPropertySelector:address->mSelector], self, *isSettable);
DLogFunc(@"%@(%d) %@ self=%p settable=%d",
NSStringFromClass([object class]), objectID,
[OBSDALObjectStore
StringFromPropertySelector:address->mSelector],
self, *isSettable);
return kCMIOHardwareNoError;
return kCMIOHardwareNoError;
}
OSStatus HardwarePlugIn_ObjectGetPropertyDataSize(
CMIOHardwarePlugInRef, CMIOObjectID objectID,
const CMIOObjectPropertyAddress *address, UInt32 qualifierDataSize,
const void *qualifierData, UInt32 *dataSize)
OSStatus HardwarePlugIn_ObjectGetPropertyDataSize(CMIOHardwarePlugInRef, CMIOObjectID objectID,
const CMIOObjectPropertyAddress *address, UInt32 qualifierDataSize,
const void *qualifierData, UInt32 *dataSize)
{
NSObject<CMIOObject> *object =
[OBSDALObjectStore GetObjectWithId:objectID];
NSObject<CMIOObject> *object = [OBSDALObjectStore GetObjectWithId:objectID];
if (object == nil) {
DLogFunc(@"ERR nil object");
return kCMIOHardwareBadObjectError;
}
if (object == nil) {
DLogFunc(@"ERR nil object");
return kCMIOHardwareBadObjectError;
}
*dataSize = [object getPropertyDataSizeWithAddress:*address
qualifierDataSize:qualifierDataSize
qualifierData:qualifierData];
*dataSize = [object getPropertyDataSizeWithAddress:*address qualifierDataSize:qualifierDataSize
qualifierData:qualifierData];
return kCMIOHardwareNoError;
return kCMIOHardwareNoError;
}
OSStatus HardwarePlugIn_ObjectGetPropertyData(
CMIOHardwarePlugInRef, CMIOObjectID objectID,
const CMIOObjectPropertyAddress *address, UInt32 qualifierDataSize,
const void *qualifierData, UInt32 dataSize, UInt32 *dataUsed,
void *data)
OSStatus HardwarePlugIn_ObjectGetPropertyData(CMIOHardwarePlugInRef, CMIOObjectID objectID,
const CMIOObjectPropertyAddress *address, UInt32 qualifierDataSize,
const void *qualifierData, UInt32 dataSize, UInt32 *dataUsed, void *data)
{
NSObject<CMIOObject> *object =
[OBSDALObjectStore GetObjectWithId:objectID];
NSObject<CMIOObject> *object = [OBSDALObjectStore GetObjectWithId:objectID];
if (object == nil) {
DLogFunc(@"ERR nil object");
return kCMIOHardwareBadObjectError;
}
if (object == nil) {
DLogFunc(@"ERR nil object");
return kCMIOHardwareBadObjectError;
}
[object getPropertyDataWithAddress:*address
qualifierDataSize:qualifierDataSize
qualifierData:qualifierData
dataSize:dataSize
dataUsed:dataUsed
data:data];
[object getPropertyDataWithAddress:*address qualifierDataSize:qualifierDataSize qualifierData:qualifierData
dataSize:dataSize
dataUsed:dataUsed
data:data];
return kCMIOHardwareNoError;
return kCMIOHardwareNoError;
}
OSStatus HardwarePlugIn_ObjectSetPropertyData(
CMIOHardwarePlugInRef self, CMIOObjectID objectID,
const CMIOObjectPropertyAddress *address, UInt32 qualifierDataSize,
const void *qualifierData, UInt32 dataSize, const void *data)
OSStatus HardwarePlugIn_ObjectSetPropertyData(CMIOHardwarePlugInRef self, CMIOObjectID objectID,
const CMIOObjectPropertyAddress *address, UInt32 qualifierDataSize,
const void *qualifierData, UInt32 dataSize, const void *data)
{
NSObject<CMIOObject> *object = [OBSDALObjectStore GetObjectWithId:objectID];
NSObject<CMIOObject> *object =
[OBSDALObjectStore GetObjectWithId:objectID];
if (object == nil) {
DLogFunc(@"ERR nil object");
return kCMIOHardwareBadObjectError;
}
if (object == nil) {
DLogFunc(@"ERR nil object");
return kCMIOHardwareBadObjectError;
}
UInt32 *dataInt = (UInt32 *) data;
DLogFunc(@"%@(%d) %@ self=%p data(int)=%d", NSStringFromClass([object class]), objectID,
[OBSDALObjectStore StringFromPropertySelector:address->mSelector], self, *dataInt);
UInt32 *dataInt = (UInt32 *)data;
DLogFunc(@"%@(%d) %@ self=%p data(int)=%d",
NSStringFromClass([object class]), objectID,
[OBSDALObjectStore
StringFromPropertySelector:address->mSelector],
self, *dataInt);
[object setPropertyDataWithAddress:*address qualifierDataSize:qualifierDataSize qualifierData:qualifierData
dataSize:dataSize
data:data];
[object setPropertyDataWithAddress:*address
qualifierDataSize:qualifierDataSize
qualifierData:qualifierData
dataSize:dataSize
data:data];
return kCMIOHardwareNoError;
return kCMIOHardwareNoError;
}
#pragma mark CMIOStream Operations
OSStatus HardwarePlugIn_StreamCopyBufferQueue(
CMIOHardwarePlugInRef self, CMIOStreamID streamID,
CMIODeviceStreamQueueAlteredProc queueAlteredProc,
void *queueAlteredRefCon, CMSimpleQueueRef *queue)
OSStatus HardwarePlugIn_StreamCopyBufferQueue(CMIOHardwarePlugInRef self, CMIOStreamID streamID,
CMIODeviceStreamQueueAlteredProc queueAlteredProc,
void *queueAlteredRefCon, CMSimpleQueueRef *queue)
{
OBSDALStream *stream = (OBSDALStream *) [OBSDALObjectStore GetObjectWithId:streamID];
OBSDALStream *stream =
(OBSDALStream *)[OBSDALObjectStore GetObjectWithId:streamID];
if (stream == nil) {
DLogFunc(@"ERR nil object");
return kCMIOHardwareBadObjectError;
}
if (stream == nil) {
DLogFunc(@"ERR nil object");
return kCMIOHardwareBadObjectError;
}
*queue = [stream copyBufferQueueWithAlteredProc:queueAlteredProc alteredRefCon:queueAlteredRefCon];
*queue = [stream copyBufferQueueWithAlteredProc:queueAlteredProc
alteredRefCon:queueAlteredRefCon];
DLogFunc(@"%@ (id=%d) self=%p queue=%@", stream, streamID, self, (__bridge NSObject *) *queue);
DLogFunc(@"%@ (id=%d) self=%p queue=%@", stream, streamID, self,
(__bridge NSObject *)*queue);
return kCMIOHardwareNoError;
return kCMIOHardwareNoError;
}
#pragma mark CMIODevice Operations
OSStatus HardwarePlugIn_DeviceStartStream(CMIOHardwarePlugInRef self,
CMIODeviceID deviceID,
CMIOStreamID streamID)
OSStatus HardwarePlugIn_DeviceStartStream(CMIOHardwarePlugInRef self, CMIODeviceID deviceID, CMIOStreamID streamID)
{
DLogFunc(@"self=%p device=%d stream=%d", self, deviceID, streamID);
DLogFunc(@"self=%p device=%d stream=%d", self, deviceID, streamID);
OBSDALStream *stream =
(OBSDALStream *)[OBSDALObjectStore GetObjectWithId:streamID];
OBSDALStream *stream = (OBSDALStream *) [OBSDALObjectStore GetObjectWithId:streamID];
if (stream == nil) {
DLogFunc(@"ERR nil object");
return kCMIOHardwareBadObjectError;
}
if (stream == nil) {
DLogFunc(@"ERR nil object");
return kCMIOHardwareBadObjectError;
}
[[OBSDALPlugin SharedPlugIn] startStream];
[[OBSDALPlugin SharedPlugIn] startStream];
return kCMIOHardwareNoError;
return kCMIOHardwareNoError;
}
OSStatus HardwarePlugIn_DeviceSuspend(CMIOHardwarePlugInRef self, CMIODeviceID)
{
DLogFunc(@"self=%p", self);
return kCMIOHardwareNoError;
DLogFunc(@"self=%p", self);
return kCMIOHardwareNoError;
}
OSStatus HardwarePlugIn_DeviceResume(CMIOHardwarePlugInRef self, CMIODeviceID)
{
DLogFunc(@"self=%p", self);
return kCMIOHardwareNoError;
DLogFunc(@"self=%p", self);
return kCMIOHardwareNoError;
}
OSStatus HardwarePlugIn_DeviceStopStream(CMIOHardwarePlugInRef self,
CMIODeviceID deviceID,
CMIOStreamID streamID)
OSStatus HardwarePlugIn_DeviceStopStream(CMIOHardwarePlugInRef self, CMIODeviceID deviceID, CMIOStreamID streamID)
{
DLogFunc(@"self=%p device=%d stream=%d", self, deviceID, streamID);
DLogFunc(@"self=%p device=%d stream=%d", self, deviceID, streamID);
OBSDALStream *stream =
(OBSDALStream *)[OBSDALObjectStore GetObjectWithId:streamID];
OBSDALStream *stream = (OBSDALStream *) [OBSDALObjectStore GetObjectWithId:streamID];
if (stream == nil) {
DLogFunc(@"ERR nil object");
return kCMIOHardwareBadObjectError;
}
if (stream == nil) {
DLogFunc(@"ERR nil object");
return kCMIOHardwareBadObjectError;
}
[[OBSDALPlugin SharedPlugIn] stopStream];
[[OBSDALPlugin SharedPlugIn] stopStream];
return kCMIOHardwareNoError;
return kCMIOHardwareNoError;
}
OSStatus HardwarePlugIn_DeviceProcessAVCCommand(CMIOHardwarePlugInRef self,
CMIODeviceID,
CMIODeviceAVCCommand *)
OSStatus HardwarePlugIn_DeviceProcessAVCCommand(CMIOHardwarePlugInRef self, CMIODeviceID, CMIODeviceAVCCommand *)
{
DLogFunc(@"self=%p", self);
return kCMIOHardwareNoError;
DLogFunc(@"self=%p", self);
return kCMIOHardwareNoError;
}
OSStatus HardwarePlugIn_DeviceProcessRS422Command(CMIOHardwarePlugInRef self,
CMIODeviceID,
CMIODeviceRS422Command *)
OSStatus HardwarePlugIn_DeviceProcessRS422Command(CMIOHardwarePlugInRef self, CMIODeviceID, CMIODeviceRS422Command *)
{
DLogFunc(@"self=%p", self);
return kCMIOHardwareNoError;
DLogFunc(@"self=%p", self);
return kCMIOHardwareNoError;
}
OSStatus HardwarePlugIn_StreamDeckPlay(CMIOHardwarePlugInRef self, CMIOStreamID)
{
DLogFunc(@"self=%p", self);
return kCMIOHardwareIllegalOperationError;
DLogFunc(@"self=%p", self);
return kCMIOHardwareIllegalOperationError;
}
OSStatus HardwarePlugIn_StreamDeckStop(CMIOHardwarePlugInRef self, CMIOStreamID)
{
DLogFunc(@"self=%p", self);
return kCMIOHardwareIllegalOperationError;
DLogFunc(@"self=%p", self);
return kCMIOHardwareIllegalOperationError;
}
OSStatus HardwarePlugIn_StreamDeckJog(CMIOHardwarePlugInRef self, CMIOStreamID,
SInt32)
OSStatus HardwarePlugIn_StreamDeckJog(CMIOHardwarePlugInRef self, CMIOStreamID, SInt32)
{
DLogFunc(@"self=%p", self);
return kCMIOHardwareIllegalOperationError;
DLogFunc(@"self=%p", self);
return kCMIOHardwareIllegalOperationError;
}
OSStatus HardwarePlugIn_StreamDeckCueTo(CMIOHardwarePlugInRef self,
CMIOStreamID, Float64, Boolean)
OSStatus HardwarePlugIn_StreamDeckCueTo(CMIOHardwarePlugInRef self, CMIOStreamID, Float64, Boolean)
{
DLogFunc(@"self=%p", self);
return kCMIOHardwareIllegalOperationError;
DLogFunc(@"self=%p", self);
return kCMIOHardwareIllegalOperationError;
}
static CMIOHardwarePlugInInterface sInterface = {
// Padding for COM
NULL,
// Padding for COM
NULL,
// IUnknown Routines
(HRESULT (*)(void *, CFUUIDBytes,
void **))HardwarePlugIn_QueryInterface,
(ULONG(*)(void *))HardwarePlugIn_AddRef,
(ULONG(*)(void *))HardwarePlugIn_Release,
// IUnknown Routines
(HRESULT (*)(void *, CFUUIDBytes, void **)) HardwarePlugIn_QueryInterface, (ULONG(*)(void *)) HardwarePlugIn_AddRef,
(ULONG(*)(void *)) HardwarePlugIn_Release,
// DAL Plug-In Routines
HardwarePlugIn_Initialize, HardwarePlugIn_InitializeWithObjectID,
HardwarePlugIn_Teardown, HardwarePlugIn_ObjectShow,
HardwarePlugIn_ObjectHasProperty,
HardwarePlugIn_ObjectIsPropertySettable,
HardwarePlugIn_ObjectGetPropertyDataSize,
HardwarePlugIn_ObjectGetPropertyData,
HardwarePlugIn_ObjectSetPropertyData, HardwarePlugIn_DeviceSuspend,
HardwarePlugIn_DeviceResume, HardwarePlugIn_DeviceStartStream,
HardwarePlugIn_DeviceStopStream, HardwarePlugIn_DeviceProcessAVCCommand,
HardwarePlugIn_DeviceProcessRS422Command,
HardwarePlugIn_StreamCopyBufferQueue, HardwarePlugIn_StreamDeckPlay,
HardwarePlugIn_StreamDeckStop, HardwarePlugIn_StreamDeckJog,
HardwarePlugIn_StreamDeckCueTo};
// DAL Plug-In Routines
HardwarePlugIn_Initialize, HardwarePlugIn_InitializeWithObjectID, HardwarePlugIn_Teardown,
HardwarePlugIn_ObjectShow, HardwarePlugIn_ObjectHasProperty, HardwarePlugIn_ObjectIsPropertySettable,
HardwarePlugIn_ObjectGetPropertyDataSize, HardwarePlugIn_ObjectGetPropertyData,
HardwarePlugIn_ObjectSetPropertyData, HardwarePlugIn_DeviceSuspend, HardwarePlugIn_DeviceResume,
HardwarePlugIn_DeviceStartStream, HardwarePlugIn_DeviceStopStream, HardwarePlugIn_DeviceProcessAVCCommand,
HardwarePlugIn_DeviceProcessRS422Command, HardwarePlugIn_StreamCopyBufferQueue, HardwarePlugIn_StreamDeckPlay,
HardwarePlugIn_StreamDeckStop, HardwarePlugIn_StreamDeckJog, HardwarePlugIn_StreamDeckCueTo};
static CMIOHardwarePlugInInterface *sInterfacePtr = &sInterface;
static CMIOHardwarePlugInRef sPlugInRef = &sInterfacePtr;
CMIOHardwarePlugInRef OBSDALPlugInRef()
{
return sPlugInRef;
return sPlugInRef;
}
@@ -23,13 +23,13 @@
//! PlugInMain is the entrypoint for the plugin
extern "C" {
__exported void *PlugInMain(CFAllocatorRef, CFUUIDRef requestedTypeUUID)
{
DLogFunc(@"version=%@", PLUGIN_VERSION);
if (!CFEqual(requestedTypeUUID, kCMIOHardwarePlugInTypeID)) {
return 0;
}
__exported void *PlugInMain(CFAllocatorRef, CFUUIDRef requestedTypeUUID)
{
DLogFunc(@"version=%@", PLUGIN_VERSION);
if (!CFEqual(requestedTypeUUID, kCMIOHardwarePlugInTypeID)) {
return 0;
}
return OBSDALPlugInRef();
}
return OBSDALPlugInRef();
}
}
@@ -28,18 +28,17 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype _Nonnull)init;
- (CMSimpleQueueRef)copyBufferQueueWithAlteredProc:
(CMIODeviceStreamQueueAlteredProc)alteredProc
alteredRefCon:(void *)alteredRefCon;
- (CMSimpleQueueRef)copyBufferQueueWithAlteredProc:(CMIODeviceStreamQueueAlteredProc)alteredProc
alteredRefCon:(void *)alteredRefCon;
- (void)startServingDefaultFrames;
- (void)stopServingDefaultFrames;
- (void)queuePixelBuffer:(CVPixelBufferRef)frame
timestamp:(uint64_t)timestamp
fpsNumerator:(uint32_t)fpsNumerator
fpsDenominator:(uint32_t)fpsDenominator;
timestamp:(uint64_t)timestamp
fpsNumerator:(uint32_t)fpsNumerator
fpsDenominator:(uint32_t)fpsDenominator;
@end
@@ -27,12 +27,12 @@
#import "OBSDALPlugIn.h"
@interface OBSDALStream () {
CMSimpleQueueRef _queue;
CFTypeRef _clock;
NSImage *_testCardImage;
dispatch_source_t _frameDispatchSource;
NSSize _testCardSize;
Float64 _fps;
CMSimpleQueueRef _queue;
CFTypeRef _clock;
NSImage *_testCardImage;
dispatch_source_t _frameDispatchSource;
NSSize _testCardSize;
Float64 _fps;
}
@property CMIODeviceStreamQueueAlteredProc alteredProc;
@@ -48,555 +48,489 @@
@implementation OBSDALStream
#define DEFAULT_FPS 30.0
#define DEFAULT_WIDTH 1280
#define DEFAULT_FPS 30.0
#define DEFAULT_WIDTH 1280
#define DEFAULT_HEIGHT 720
- (instancetype _Nonnull)init
{
self = [super init];
if (self) {
_frameDispatchSource = dispatch_source_create(
DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
__weak __typeof(self) wself = self;
dispatch_source_set_event_handler(_frameDispatchSource, ^{
[wself fillFrame];
});
}
return self;
self = [super init];
if (self) {
_frameDispatchSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
__weak __typeof(self) wself = self;
dispatch_source_set_event_handler(_frameDispatchSource, ^{
[wself fillFrame];
});
}
return self;
}
- (void)dealloc
{
DLog(@"Stream Dealloc");
CMIOStreamClockInvalidate(_clock);
CFRelease(_clock);
_clock = NULL;
CFRelease(_queue);
_queue = NULL;
dispatch_suspend(_frameDispatchSource);
DLog(@"Stream Dealloc");
CMIOStreamClockInvalidate(_clock);
CFRelease(_clock);
_clock = NULL;
CFRelease(_queue);
_queue = NULL;
dispatch_suspend(_frameDispatchSource);
}
- (void)startServingDefaultFrames
{
DLogFunc(@"");
_testCardImage = nil;
_testCardSize = NSZeroSize;
_fps = 0;
dispatch_time_t startTime = dispatch_time(DISPATCH_TIME_NOW, 0);
uint64_t intervalTime = (int64_t)(NSEC_PER_SEC / self.fps);
dispatch_source_set_timer(_frameDispatchSource, startTime, intervalTime,
0);
dispatch_resume(_frameDispatchSource);
DLogFunc(@"");
_testCardImage = nil;
_testCardSize = NSZeroSize;
_fps = 0;
dispatch_time_t startTime = dispatch_time(DISPATCH_TIME_NOW, 0);
uint64_t intervalTime = (int64_t) (NSEC_PER_SEC / self.fps);
dispatch_source_set_timer(_frameDispatchSource, startTime, intervalTime, 0);
dispatch_resume(_frameDispatchSource);
}
- (void)stopServingDefaultFrames
{
DLogFunc(@"");
dispatch_suspend(_frameDispatchSource);
DLogFunc(@"");
dispatch_suspend(_frameDispatchSource);
}
- (CMSimpleQueueRef)queue
{
if (_queue == NULL) {
// Allocate a one-second long queue, which we can use our FPS constant for.
OSStatus err = CMSimpleQueueCreate(kCFAllocatorDefault,
(int32_t)self.fps, &_queue);
if (err != noErr) {
DLog(@"Err %d in CMSimpleQueueCreate", err);
}
}
return _queue;
if (_queue == NULL) {
// Allocate a one-second long queue, which we can use our FPS constant for.
OSStatus err = CMSimpleQueueCreate(kCFAllocatorDefault, (int32_t) self.fps, &_queue);
if (err != noErr) {
DLog(@"Err %d in CMSimpleQueueCreate", err);
}
}
return _queue;
}
- (CFTypeRef)clock
{
if (_clock == NULL) {
OSStatus err = CMIOStreamClockCreate(
kCFAllocatorDefault,
CFSTR("obs-mac-virtualcam::Stream::clock"),
(__bridge void *)self, CMTimeMake(1, 10), 100, 10,
&_clock);
if (err != noErr) {
DLog(@"Error %d from CMIOStreamClockCreate", err);
}
}
return _clock;
if (_clock == NULL) {
OSStatus err = CMIOStreamClockCreate(kCFAllocatorDefault, CFSTR("obs-mac-virtualcam::Stream::clock"),
(__bridge void *) self, CMTimeMake(1, 10), 100, 10, &_clock);
if (err != noErr) {
DLog(@"Error %d from CMIOStreamClockCreate", err);
}
}
return _clock;
}
- (NSSize)testCardSize
{
if (NSEqualSizes(_testCardSize, NSZeroSize)) {
NSUserDefaults *defaults =
[NSUserDefaults standardUserDefaults];
NSInteger width = [[defaults objectForKey:kTestCardWidthKey]
integerValue];
NSInteger height = [[defaults objectForKey:kTestCardHeightKey]
integerValue];
if (width == 0 || height == 0) {
_testCardSize =
NSMakeSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
} else {
_testCardSize = NSMakeSize(width, height);
}
}
return _testCardSize;
if (NSEqualSizes(_testCardSize, NSZeroSize)) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSInteger width = [[defaults objectForKey:kTestCardWidthKey] integerValue];
NSInteger height = [[defaults objectForKey:kTestCardHeightKey] integerValue];
if (width == 0 || height == 0) {
_testCardSize = NSMakeSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
} else {
_testCardSize = NSMakeSize(width, height);
}
}
return _testCardSize;
}
- (Float64)fps
{
if (_fps == 0) {
NSUserDefaults *defaults =
[NSUserDefaults standardUserDefaults];
double fps =
[[defaults objectForKey:kTestCardFPSKey] doubleValue];
if (fps == 0) {
_fps = DEFAULT_FPS;
} else {
_fps = fps;
}
}
return _fps;
if (_fps == 0) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
double fps = [[defaults objectForKey:kTestCardFPSKey] doubleValue];
if (fps == 0) {
_fps = DEFAULT_FPS;
} else {
_fps = fps;
}
}
return _fps;
}
- (NSImage *)testCardImage
{
if (_testCardImage == nil) {
NSString *bundlePath = [[NSBundle
bundleForClass:[OBSDALStream class]] bundlePath];
NSString *placeHolderPath = [bundlePath
stringByAppendingString:
@"/Contents/Resources/placeholder.png"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *homeUrl = [fileManager homeDirectoryForCurrentUser];
NSURL *customUrl = [homeUrl
URLByAppendingPathComponent:
@"Library/Application Support/obs-studio/plugin_config/mac-virtualcam/placeholder.png"];
NSString *customPlaceHolder = customUrl.path;
if ([fileManager isReadableFileAtPath:customPlaceHolder])
placeHolderPath = customPlaceHolder;
DLog(@"PlaceHolder:%@", placeHolderPath);
NSImage *placeholderImage = [[NSImage alloc]
initWithContentsOfFile:placeHolderPath];
if (_testCardImage == nil) {
NSString *bundlePath = [[NSBundle bundleForClass:[OBSDALStream class]] bundlePath];
NSString *placeHolderPath = [bundlePath stringByAppendingString:@"/Contents/Resources/placeholder.png"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *homeUrl = [fileManager homeDirectoryForCurrentUser];
NSURL *customUrl =
[homeUrl URLByAppendingPathComponent:
@"Library/Application Support/obs-studio/plugin_config/mac-virtualcam/placeholder.png"];
NSString *customPlaceHolder = customUrl.path;
if ([fileManager isReadableFileAtPath:customPlaceHolder])
placeHolderPath = customPlaceHolder;
DLog(@"PlaceHolder:%@", placeHolderPath);
NSImage *placeholderImage = [[NSImage alloc] initWithContentsOfFile:placeHolderPath];
NSBitmapImageRep *rep = [[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:NULL
pixelsWide:(NSInteger)
self.testCardSize.width
pixelsHigh:(NSInteger)self.testCardSize
.height
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSCalibratedRGBColorSpace
bytesPerRow:0
bitsPerPixel:0];
rep.size = self.testCardSize;
NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
pixelsWide:(NSInteger) self.testCardSize.width
pixelsHigh:(NSInteger) self.testCardSize.height
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSCalibratedRGBColorSpace
bytesPerRow:0
bitsPerPixel:0];
rep.size = self.testCardSize;
double hScale =
placeholderImage.size.width / self.testCardSize.width;
double vScale =
placeholderImage.size.height / self.testCardSize.height;
double hScale = placeholderImage.size.width / self.testCardSize.width;
double vScale = placeholderImage.size.height / self.testCardSize.height;
double scaling = fmax(hScale, vScale);
double scaling = fmax(hScale, vScale);
double newWidth = placeholderImage.size.width / scaling;
double newHeight = placeholderImage.size.height / scaling;
double newWidth = placeholderImage.size.width / scaling;
double newHeight = placeholderImage.size.height / scaling;
double leftOffset = (self.testCardSize.width - newWidth) / 2;
double topOffset = (self.testCardSize.height - newHeight) / 2;
double leftOffset = (self.testCardSize.width - newWidth) / 2;
double topOffset = (self.testCardSize.height - newHeight) / 2;
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext
setCurrentContext:
[NSGraphicsContext
graphicsContextWithBitmapImageRep:rep]];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:rep]];
NSColor *backgroundColor = [NSColor blackColor];
[backgroundColor set];
NSRectFill(NSMakeRect(0, 0, self.testCardSize.width,
self.testCardSize.height));
NSColor *backgroundColor = [NSColor blackColor];
[backgroundColor set];
NSRectFill(NSMakeRect(0, 0, self.testCardSize.width, self.testCardSize.height));
[placeholderImage drawInRect:NSMakeRect(leftOffset, topOffset,
newWidth, newHeight)
fromRect:NSZeroRect
operation:NSCompositingOperationCopy
fraction:1.0];
[NSGraphicsContext restoreGraphicsState];
[placeholderImage drawInRect:NSMakeRect(leftOffset, topOffset, newWidth, newHeight) fromRect:NSZeroRect
operation:NSCompositingOperationCopy
fraction:1.0];
[NSGraphicsContext restoreGraphicsState];
NSImage *testCardImage =
[[NSImage alloc] initWithSize:self.testCardSize];
[testCardImage addRepresentation:rep];
NSImage *testCardImage = [[NSImage alloc] initWithSize:self.testCardSize];
[testCardImage addRepresentation:rep];
_testCardImage = testCardImage;
}
return _testCardImage;
_testCardImage = testCardImage;
}
return _testCardImage;
}
- (CMSimpleQueueRef)copyBufferQueueWithAlteredProc:
(CMIODeviceStreamQueueAlteredProc)alteredProc
alteredRefCon:(void *)alteredRefCon
- (CMSimpleQueueRef)copyBufferQueueWithAlteredProc:(CMIODeviceStreamQueueAlteredProc)alteredProc
alteredRefCon:(void *)alteredRefCon
{
self.alteredProc = alteredProc;
self.alteredRefCon = alteredRefCon;
self.alteredProc = alteredProc;
self.alteredRefCon = alteredRefCon;
// Retain this since it's a copy operation
CFRetain(self.queue);
// Retain this since it's a copy operation
CFRetain(self.queue);
return self.queue;
return self.queue;
}
- (CVPixelBufferRef)createPixelBufferWithTestAnimation
{
int width = (int)self.testCardSize.width;
int height = (int)self.testCardSize.height;
int width = (int) self.testCardSize.width;
int height = (int) self.testCardSize.height;
NSDictionary *options = [NSDictionary
dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES],
kCVPixelBufferCGImageCompatibilityKey,
[NSNumber numberWithBool:YES],
kCVPixelBufferCGBitmapContextCompatibilityKey, nil];
CVPixelBufferRef pxbuffer = NULL;
CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, width,
height, kCVPixelFormatType_32ARGB,
(__bridge CFDictionaryRef)options,
&pxbuffer);
NSDictionary *options = [NSDictionary
dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
[NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey, nil];
CVPixelBufferRef pxbuffer = NULL;
CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, width, height, kCVPixelFormatType_32ARGB,
(__bridge CFDictionaryRef) options, &pxbuffer);
NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);
NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);
CVPixelBufferLockBaseAddress(pxbuffer, 0);
void *pxdata = CVPixelBufferGetBaseAddressOfPlane(pxbuffer, 0);
NSParameterAssert(pxdata != NULL);
CVPixelBufferLockBaseAddress(pxbuffer, 0);
void *pxdata = CVPixelBufferGetBaseAddressOfPlane(pxbuffer, 0);
NSParameterAssert(pxdata != NULL);
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(
pxdata, width, height, 8,
CVPixelBufferGetBytesPerRowOfPlane(pxbuffer, 0), rgbColorSpace,
kCGImageAlphaPremultipliedFirst | kCGImageByteOrder32Big);
CFRelease(rgbColorSpace);
NSParameterAssert(context);
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(pxdata, width, height, 8,
CVPixelBufferGetBytesPerRowOfPlane(pxbuffer, 0), rgbColorSpace,
kCGImageAlphaPremultipliedFirst | kCGImageByteOrder32Big);
CFRelease(rgbColorSpace);
NSParameterAssert(context);
NSGraphicsContext *nsContext = [NSGraphicsContext
graphicsContextWithCGContext:context
flipped:NO];
[NSGraphicsContext setCurrentContext:nsContext];
NSGraphicsContext *nsContext = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO];
[NSGraphicsContext setCurrentContext:nsContext];
NSRect rect = NSMakeRect(0, 0, self.testCardImage.size.width,
self.testCardImage.size.height);
CGImageRef image = [self.testCardImage CGImageForProposedRect:&rect
context:nsContext
hints:nil];
CGContextDrawImage(context,
CGRectMake(0, 0, CGImageGetWidth(image),
CGImageGetHeight(image)),
image);
NSRect rect = NSMakeRect(0, 0, self.testCardImage.size.width, self.testCardImage.size.height);
CGImageRef image = [self.testCardImage CGImageForProposedRect:&rect context:nsContext hints:nil];
CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image);
// DrawDialWithFrame(
// NSMakeRect(0, 0, width, height),
// (int(self.fps) - self.sequenceNumber % int(self.fps)) * 360 /
// int(self.fps));
// DrawDialWithFrame(
// NSMakeRect(0, 0, width, height),
// (int(self.fps) - self.sequenceNumber % int(self.fps)) * 360 /
// int(self.fps));
CGContextRelease(context);
CGContextRelease(context);
CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
return pxbuffer;
return pxbuffer;
}
- (void)fillFrame
{
if (CMSimpleQueueGetFullness(self.queue) >= 1.0) {
return;
}
if (CMSimpleQueueGetFullness(self.queue) >= 1.0) {
return;
}
CVPixelBufferRef pixelBuffer =
[self createPixelBufferWithTestAnimation];
CVPixelBufferRef pixelBuffer = [self createPixelBufferWithTestAnimation];
uint64_t hostTime = clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
CMSampleTimingInfo timingInfo =
CMSampleTimingInfoForTimestamp(hostTime, (uint32_t)self.fps, 1);
uint64_t hostTime = clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
CMSampleTimingInfo timingInfo = CMSampleTimingInfoForTimestamp(hostTime, (uint32_t) self.fps, 1);
OSStatus err = CMIOStreamClockPostTimingEvent(
timingInfo.presentationTimeStamp, hostTime, true, self.clock);
if (err != noErr) {
DLog(@"CMIOStreamClockPostTimingEvent err %d", err);
}
OSStatus err = CMIOStreamClockPostTimingEvent(timingInfo.presentationTimeStamp, hostTime, true, self.clock);
if (err != noErr) {
DLog(@"CMIOStreamClockPostTimingEvent err %d", err);
}
CMFormatDescriptionRef format;
CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault,
pixelBuffer, &format);
CMFormatDescriptionRef format;
CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault, pixelBuffer, &format);
self.sequenceNumber = CMIOGetNextSequenceNumber(self.sequenceNumber);
self.sequenceNumber = CMIOGetNextSequenceNumber(self.sequenceNumber);
CMSampleBufferRef buffer;
err = CMIOSampleBufferCreateForImageBuffer(
kCFAllocatorDefault, pixelBuffer, format, &timingInfo,
self.sequenceNumber, kCMIOSampleBufferNoDiscontinuities,
&buffer);
CFRelease(pixelBuffer);
CFRelease(format);
if (err != noErr) {
DLog(@"CMIOSampleBufferCreateForImageBuffer err %d", err);
}
CMSampleBufferRef buffer;
err = CMIOSampleBufferCreateForImageBuffer(kCFAllocatorDefault, pixelBuffer, format, &timingInfo,
self.sequenceNumber, kCMIOSampleBufferNoDiscontinuities, &buffer);
CFRelease(pixelBuffer);
CFRelease(format);
if (err != noErr) {
DLog(@"CMIOSampleBufferCreateForImageBuffer err %d", err);
}
CMSimpleQueueEnqueue(self.queue, buffer);
CMSimpleQueueEnqueue(self.queue, buffer);
// Inform the clients that the queue has been altered
if (self.alteredProc != NULL) {
(self.alteredProc)(self.objectId, buffer, self.alteredRefCon);
}
// Inform the clients that the queue has been altered
if (self.alteredProc != NULL) {
(self.alteredProc)(self.objectId, buffer, self.alteredRefCon);
}
}
- (void)queuePixelBuffer:(CVPixelBufferRef)frame
timestamp:(uint64_t)timestamp
fpsNumerator:(uint32_t)fpsNumerator
fpsDenominator:(uint32_t)fpsDenominator
timestamp:(uint64_t)timestamp
fpsNumerator:(uint32_t)fpsNumerator
fpsDenominator:(uint32_t)fpsDenominator
{
if (CMSimpleQueueGetFullness(self.queue) >= 1.0) {
DLog(@"Queue is full, bailing out");
return;
}
OSStatus err = noErr;
if (CMSimpleQueueGetFullness(self.queue) >= 1.0) {
DLog(@"Queue is full, bailing out");
return;
}
OSStatus err = noErr;
CMSampleTimingInfo timingInfo = CMSampleTimingInfoForTimestamp(
timestamp, fpsNumerator, fpsDenominator);
CMSampleTimingInfo timingInfo = CMSampleTimingInfoForTimestamp(timestamp, fpsNumerator, fpsDenominator);
err = CMIOStreamClockPostTimingEvent(
timingInfo.presentationTimeStamp,
clock_gettime_nsec_np(CLOCK_UPTIME_RAW), true, self.clock);
if (err != noErr) {
DLog(@"CMIOStreamClockPostTimingEvent err %d", err);
}
err = CMIOStreamClockPostTimingEvent(timingInfo.presentationTimeStamp, clock_gettime_nsec_np(CLOCK_UPTIME_RAW),
true, self.clock);
if (err != noErr) {
DLog(@"CMIOStreamClockPostTimingEvent err %d", err);
}
self.sequenceNumber = CMIOGetNextSequenceNumber(self.sequenceNumber);
self.sequenceNumber = CMIOGetNextSequenceNumber(self.sequenceNumber);
CMSampleBufferRef sampleBuffer;
CMSampleBufferRef sampleBuffer;
// Generate the video format description from that pixel buffer
CMVideoFormatDescriptionRef format;
err = CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault,
frame, &format);
if (err != noErr) {
DLog(@"CMVideoFormatDescriptionCreateForImageBuffer err %d",
err);
return;
}
// Generate the video format description from that pixel buffer
CMVideoFormatDescriptionRef format;
err = CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault, frame, &format);
if (err != noErr) {
DLog(@"CMVideoFormatDescriptionCreateForImageBuffer err %d", err);
return;
}
err = CMIOSampleBufferCreateForImageBuffer(
kCFAllocatorDefault, frame, format, &timingInfo,
self.sequenceNumber, kCMIOSampleBufferNoDiscontinuities,
&sampleBuffer);
err = CMIOSampleBufferCreateForImageBuffer(kCFAllocatorDefault, frame, format, &timingInfo, self.sequenceNumber,
kCMIOSampleBufferNoDiscontinuities, &sampleBuffer);
CFRelease(format);
CFRelease(format);
if (err != noErr) {
DLog(@"CMIOSampleBufferCreateForImageBuffer err %d", err);
return;
}
if (err != noErr) {
DLog(@"CMIOSampleBufferCreateForImageBuffer err %d", err);
return;
}
err = CMSimpleQueueEnqueue(self.queue, sampleBuffer);
err = CMSimpleQueueEnqueue(self.queue, sampleBuffer);
if (err != noErr) {
CFRelease(sampleBuffer);
if (err != noErr) {
CFRelease(sampleBuffer);
DLog(@"CMSimpleQueueEnqueue err %d", err);
return;
}
DLog(@"CMSimpleQueueEnqueue err %d", err);
return;
}
// Inform the clients that the queue has been altered
if (self.alteredProc != NULL) {
(self.alteredProc)(self.objectId, sampleBuffer,
self.alteredRefCon);
}
// Inform the clients that the queue has been altered
if (self.alteredProc != NULL) {
(self.alteredProc)(self.objectId, sampleBuffer, self.alteredRefCon);
}
}
- (CMVideoFormatDescriptionRef)getFormatDescription
{
CMVideoFormatDescriptionRef formatDescription;
OSStatus err = CMVideoFormatDescriptionCreate(
kCFAllocatorDefault,
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,
(int32_t)self.testCardSize.width,
(int32_t)self.testCardSize.height, NULL, &formatDescription);
if (err != noErr) {
DLog(@"Error %d from CMVideoFormatDescriptionCreate", err);
}
return formatDescription;
CMVideoFormatDescriptionRef formatDescription;
OSStatus err = CMVideoFormatDescriptionCreate(kCFAllocatorDefault, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,
(int32_t) self.testCardSize.width, (int32_t) self.testCardSize.height,
NULL, &formatDescription);
if (err != noErr) {
DLog(@"Error %d from CMVideoFormatDescriptionCreate", err);
}
return formatDescription;
}
#pragma mark - CMIOObject
- (UInt32)getPropertyDataSizeWithAddress:(CMIOObjectPropertyAddress)address
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(nonnull const void *)qualifierData
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(nonnull const void *)qualifierData
{
switch (address.mSelector) {
case kCMIOStreamPropertyInitialPresentationTimeStampForLinkedAndSyncedAudio:
return sizeof(CMTime);
case kCMIOStreamPropertyOutputBuffersNeededForThrottledPlayback:
return sizeof(UInt32);
case kCMIOObjectPropertyName:
return sizeof(CFStringRef);
case kCMIOObjectPropertyManufacturer:
return sizeof(CFStringRef);
case kCMIOObjectPropertyElementName:
return sizeof(CFStringRef);
case kCMIOObjectPropertyElementCategoryName:
return sizeof(CFStringRef);
case kCMIOObjectPropertyElementNumberName:
return sizeof(CFStringRef);
case kCMIOStreamPropertyDirection:
return sizeof(UInt32);
case kCMIOStreamPropertyTerminalType:
return sizeof(UInt32);
case kCMIOStreamPropertyStartingChannel:
return sizeof(UInt32);
case kCMIOStreamPropertyLatency:
return sizeof(UInt32);
case kCMIOStreamPropertyFormatDescriptions:
return sizeof(CFArrayRef);
case kCMIOStreamPropertyFormatDescription:
return sizeof(CMFormatDescriptionRef);
case kCMIOStreamPropertyFrameRateRanges:
return sizeof(AudioValueRange);
case kCMIOStreamPropertyFrameRate:
case kCMIOStreamPropertyFrameRates:
return sizeof(Float64);
case kCMIOStreamPropertyMinimumFrameRate:
return sizeof(Float64);
case kCMIOStreamPropertyClock:
return sizeof(CFTypeRef);
default:
return 0;
};
switch (address.mSelector) {
case kCMIOStreamPropertyInitialPresentationTimeStampForLinkedAndSyncedAudio:
return sizeof(CMTime);
case kCMIOStreamPropertyOutputBuffersNeededForThrottledPlayback:
return sizeof(UInt32);
case kCMIOObjectPropertyName:
return sizeof(CFStringRef);
case kCMIOObjectPropertyManufacturer:
return sizeof(CFStringRef);
case kCMIOObjectPropertyElementName:
return sizeof(CFStringRef);
case kCMIOObjectPropertyElementCategoryName:
return sizeof(CFStringRef);
case kCMIOObjectPropertyElementNumberName:
return sizeof(CFStringRef);
case kCMIOStreamPropertyDirection:
return sizeof(UInt32);
case kCMIOStreamPropertyTerminalType:
return sizeof(UInt32);
case kCMIOStreamPropertyStartingChannel:
return sizeof(UInt32);
case kCMIOStreamPropertyLatency:
return sizeof(UInt32);
case kCMIOStreamPropertyFormatDescriptions:
return sizeof(CFArrayRef);
case kCMIOStreamPropertyFormatDescription:
return sizeof(CMFormatDescriptionRef);
case kCMIOStreamPropertyFrameRateRanges:
return sizeof(AudioValueRange);
case kCMIOStreamPropertyFrameRate:
case kCMIOStreamPropertyFrameRates:
return sizeof(Float64);
case kCMIOStreamPropertyMinimumFrameRate:
return sizeof(Float64);
case kCMIOStreamPropertyClock:
return sizeof(CFTypeRef);
default:
return 0;
};
}
- (void)getPropertyDataWithAddress:(CMIOObjectPropertyAddress)address
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(nonnull const void *)qualifierData
dataSize:(UInt32)dataSize
dataUsed:(nonnull UInt32 *)dataUsed
data:(nonnull void *)data
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(nonnull const void *)qualifierData
dataSize:(UInt32)dataSize
dataUsed:(nonnull UInt32 *)dataUsed
data:(nonnull void *)data
{
switch (address.mSelector) {
case kCMIOObjectPropertyName:
*static_cast<CFStringRef *>(data) = CFSTR("OBS Virtual Camera");
*dataUsed = sizeof(CFStringRef);
break;
case kCMIOObjectPropertyElementName:
*static_cast<CFStringRef *>(data) =
CFSTR("OBS Virtual Camera Stream Element");
*dataUsed = sizeof(CFStringRef);
break;
case kCMIOObjectPropertyManufacturer:
case kCMIOObjectPropertyElementCategoryName:
case kCMIOObjectPropertyElementNumberName:
case kCMIOStreamPropertyTerminalType:
case kCMIOStreamPropertyStartingChannel:
case kCMIOStreamPropertyLatency:
case kCMIOStreamPropertyInitialPresentationTimeStampForLinkedAndSyncedAudio:
case kCMIOStreamPropertyOutputBuffersNeededForThrottledPlayback:
break;
case kCMIOStreamPropertyDirection:
*static_cast<UInt32 *>(data) = 1;
*dataUsed = sizeof(UInt32);
break;
case kCMIOStreamPropertyFormatDescriptions:
*static_cast<CFArrayRef *>(
data) = (__bridge_retained CFArrayRef)[NSArray
arrayWithObject:(__bridge_transfer NSObject *)
[self getFormatDescription]];
*dataUsed = sizeof(CFArrayRef);
break;
case kCMIOStreamPropertyFormatDescription:
*static_cast<CMVideoFormatDescriptionRef *>(data) =
[self getFormatDescription];
*dataUsed = sizeof(CMVideoFormatDescriptionRef);
break;
case kCMIOStreamPropertyFrameRateRanges:
AudioValueRange range;
range.mMinimum = self.fps;
range.mMaximum = self.fps;
*static_cast<AudioValueRange *>(data) = range;
*dataUsed = sizeof(AudioValueRange);
break;
case kCMIOStreamPropertyFrameRate:
case kCMIOStreamPropertyFrameRates:
*static_cast<Float64 *>(data) = self.fps;
*dataUsed = sizeof(Float64);
break;
case kCMIOStreamPropertyMinimumFrameRate:
*static_cast<Float64 *>(data) = self.fps;
*dataUsed = sizeof(Float64);
break;
case kCMIOStreamPropertyClock:
*static_cast<CFTypeRef *>(data) = self.clock;
// This one was incredibly tricky and cost me many hours to find. It seems that DAL expects
// the clock to be retained when returned. It's unclear why, and that seems inconsistent
// with other properties that don't have the same behavior. But this is what Apple's sample
// code does.
// https://github.com/lvsti/CoreMediaIO-DAL-Example/blob/0392cb/Sources/Extras/CoreMediaIO/DeviceAbstractionLayer/Devices/DP/Properties/CMIO_DP_Property_Clock.cpp#L75
CFRetain(*static_cast<CFTypeRef *>(data));
*dataUsed = sizeof(CFTypeRef);
break;
default:
*dataUsed = 0;
};
switch (address.mSelector) {
case kCMIOObjectPropertyName:
*static_cast<CFStringRef *>(data) = CFSTR("OBS Virtual Camera");
*dataUsed = sizeof(CFStringRef);
break;
case kCMIOObjectPropertyElementName:
*static_cast<CFStringRef *>(data) = CFSTR("OBS Virtual Camera Stream Element");
*dataUsed = sizeof(CFStringRef);
break;
case kCMIOObjectPropertyManufacturer:
case kCMIOObjectPropertyElementCategoryName:
case kCMIOObjectPropertyElementNumberName:
case kCMIOStreamPropertyTerminalType:
case kCMIOStreamPropertyStartingChannel:
case kCMIOStreamPropertyLatency:
case kCMIOStreamPropertyInitialPresentationTimeStampForLinkedAndSyncedAudio:
case kCMIOStreamPropertyOutputBuffersNeededForThrottledPlayback:
break;
case kCMIOStreamPropertyDirection:
*static_cast<UInt32 *>(data) = 1;
*dataUsed = sizeof(UInt32);
break;
case kCMIOStreamPropertyFormatDescriptions:
*static_cast<CFArrayRef *>(data) = (__bridge_retained CFArrayRef)
[NSArray arrayWithObject:(__bridge_transfer NSObject *) [self getFormatDescription]];
*dataUsed = sizeof(CFArrayRef);
break;
case kCMIOStreamPropertyFormatDescription:
*static_cast<CMVideoFormatDescriptionRef *>(data) = [self getFormatDescription];
*dataUsed = sizeof(CMVideoFormatDescriptionRef);
break;
case kCMIOStreamPropertyFrameRateRanges:
AudioValueRange range;
range.mMinimum = self.fps;
range.mMaximum = self.fps;
*static_cast<AudioValueRange *>(data) = range;
*dataUsed = sizeof(AudioValueRange);
break;
case kCMIOStreamPropertyFrameRate:
case kCMIOStreamPropertyFrameRates:
*static_cast<Float64 *>(data) = self.fps;
*dataUsed = sizeof(Float64);
break;
case kCMIOStreamPropertyMinimumFrameRate:
*static_cast<Float64 *>(data) = self.fps;
*dataUsed = sizeof(Float64);
break;
case kCMIOStreamPropertyClock:
*static_cast<CFTypeRef *>(data) = self.clock;
// This one was incredibly tricky and cost me many hours to find. It seems that DAL expects
// the clock to be retained when returned. It's unclear why, and that seems inconsistent
// with other properties that don't have the same behavior. But this is what Apple's sample
// code does.
// https://github.com/lvsti/CoreMediaIO-DAL-Example/blob/0392cb/Sources/Extras/CoreMediaIO/DeviceAbstractionLayer/Devices/DP/Properties/CMIO_DP_Property_Clock.cpp#L75
CFRetain(*static_cast<CFTypeRef *>(data));
*dataUsed = sizeof(CFTypeRef);
break;
default:
*dataUsed = 0;
};
}
- (BOOL)hasPropertyWithAddress:(CMIOObjectPropertyAddress)address
{
switch (address.mSelector) {
case kCMIOObjectPropertyName:
case kCMIOObjectPropertyElementName:
case kCMIOStreamPropertyFormatDescriptions:
case kCMIOStreamPropertyFormatDescription:
case kCMIOStreamPropertyFrameRateRanges:
case kCMIOStreamPropertyFrameRate:
case kCMIOStreamPropertyFrameRates:
case kCMIOStreamPropertyMinimumFrameRate:
case kCMIOStreamPropertyClock:
return true;
case kCMIOObjectPropertyManufacturer:
case kCMIOObjectPropertyElementCategoryName:
case kCMIOObjectPropertyElementNumberName:
case kCMIOStreamPropertyDirection:
case kCMIOStreamPropertyTerminalType:
case kCMIOStreamPropertyStartingChannel:
case kCMIOStreamPropertyLatency:
case kCMIOStreamPropertyInitialPresentationTimeStampForLinkedAndSyncedAudio:
case kCMIOStreamPropertyOutputBuffersNeededForThrottledPlayback:
DLog(@"TODO: %@",
[OBSDALObjectStore
StringFromPropertySelector:address.mSelector]);
return false;
default:
return false;
};
switch (address.mSelector) {
case kCMIOObjectPropertyName:
case kCMIOObjectPropertyElementName:
case kCMIOStreamPropertyFormatDescriptions:
case kCMIOStreamPropertyFormatDescription:
case kCMIOStreamPropertyFrameRateRanges:
case kCMIOStreamPropertyFrameRate:
case kCMIOStreamPropertyFrameRates:
case kCMIOStreamPropertyMinimumFrameRate:
case kCMIOStreamPropertyClock:
return true;
case kCMIOObjectPropertyManufacturer:
case kCMIOObjectPropertyElementCategoryName:
case kCMIOObjectPropertyElementNumberName:
case kCMIOStreamPropertyDirection:
case kCMIOStreamPropertyTerminalType:
case kCMIOStreamPropertyStartingChannel:
case kCMIOStreamPropertyLatency:
case kCMIOStreamPropertyInitialPresentationTimeStampForLinkedAndSyncedAudio:
case kCMIOStreamPropertyOutputBuffersNeededForThrottledPlayback:
DLog(@"TODO: %@", [OBSDALObjectStore StringFromPropertySelector:address.mSelector]);
return false;
default:
return false;
};
}
- (BOOL)isPropertySettableWithAddress:(CMIOObjectPropertyAddress)address
{
switch (address.mSelector) {
case kCMIOStreamPropertyFormatDescription:
case kCMIOStreamPropertyFrameRate:
// Suppress error logs complaining about the application not being able to set the desired format or frame rate.
return true;
default:
return false;
}
switch (address.mSelector) {
case kCMIOStreamPropertyFormatDescription:
case kCMIOStreamPropertyFrameRate:
// Suppress error logs complaining about the application not being able to set the desired format or frame rate.
return true;
default:
return false;
}
}
- (void)setPropertyDataWithAddress:(CMIOObjectPropertyAddress)address
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(nonnull const void *)qualifierData
dataSize:(UInt32)dataSize
data:(nonnull const void *)data
{
}
qualifierDataSize:(UInt32)qualifierDataSize
qualifierData:(nonnull const void *)qualifierData
dataSize:(UInt32)dataSize
data:(nonnull const void *)data
{}
@end
@@ -18,9 +18,9 @@ NS_ASSUME_NONNULL_BEGIN
Will eventually be used for sending frames to all connected clients
*/
- (void)sendPixelBuffer:(CVPixelBufferRef)frame
timestamp:(uint64_t)timestamp
fpsNumerator:(uint32_t)fpsNumerator
fpsDenominator:(uint32_t)fpsDenominator;
timestamp:(uint64_t)timestamp
fpsNumerator:(uint32_t)fpsNumerator
fpsDenominator:(uint32_t)fpsDenominator;
- (void)stop;
@@ -20,26 +20,26 @@
- (id)init
{
if (self = [super init]) {
self.clientPorts = [[NSMutableSet alloc] init];
}
return self;
if (self = [super init]) {
self.clientPorts = [[NSMutableSet alloc] init];
}
return self;
}
- (void)dealloc
{
blog(LOG_DEBUG, "tearing down MachServer");
[self.runLoop removePort:self.port forMode:NSDefaultRunLoopMode];
[self.port invalidate];
self.port.delegate = nil;
blog(LOG_DEBUG, "tearing down MachServer");
[self.runLoop removePort:self.port forMode:NSDefaultRunLoopMode];
[self.port invalidate];
self.port.delegate = nil;
}
- (void)run
{
if (self.port != nil) {
blog(LOG_DEBUG, "mach server already running!");
return;
}
if (self.port != nil) {
blog(LOG_DEBUG, "mach server already running!");
return;
}
// It's a bummer this is deprecated. The replacement, NSXPCConnection, seems to require
// an assistant process that lives inside the .app bundle. This would be more modern, but adds
@@ -49,145 +49,119 @@
// these APIs (which are, interestingly, not deprecated)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
self.port = [[NSMachBootstrapServer sharedInstance]
servicePortWithName:@MACH_SERVICE_NAME];
self.port = [[NSMachBootstrapServer sharedInstance] servicePortWithName:@MACH_SERVICE_NAME];
#pragma clang diagnostic pop
if (self.port == nil) {
// This probably means another instance is running.
blog(LOG_ERROR, "Unable to open mach server port.");
return;
}
if (self.port == nil) {
// This probably means another instance is running.
blog(LOG_ERROR, "Unable to open mach server port.");
return;
}
self.port.delegate = self;
self.port.delegate = self;
self.runLoop = [NSRunLoop currentRunLoop];
[self.runLoop addPort:self.port forMode:NSDefaultRunLoopMode];
self.runLoop = [NSRunLoop currentRunLoop];
[self.runLoop addPort:self.port forMode:NSDefaultRunLoopMode];
blog(LOG_DEBUG, "mach server running!");
blog(LOG_DEBUG, "mach server running!");
}
- (void)handlePortMessage:(NSPortMessage *)message
{
switch (message.msgid) {
case MachMsgIdConnect:
if (message.sendPort != nil) {
blog(LOG_DEBUG,
"mach server received connect message from port %d!",
((NSMachPort *)message.sendPort).machPort);
[self.clientPorts addObject:message.sendPort];
}
break;
default:
blog(LOG_ERROR, "Unexpected mach message ID %u",
(unsigned)message.msgid);
break;
}
switch (message.msgid) {
case MachMsgIdConnect:
if (message.sendPort != nil) {
blog(LOG_DEBUG, "mach server received connect message from port %d!",
((NSMachPort *) message.sendPort).machPort);
[self.clientPorts addObject:message.sendPort];
}
break;
default:
blog(LOG_ERROR, "Unexpected mach message ID %u", (unsigned) message.msgid);
break;
}
}
- (void)sendMessageToClientsWithMsgId:(uint32_t)msgId
components:(nullable NSArray *)components
- (void)sendMessageToClientsWithMsgId:(uint32_t)msgId components:(nullable NSArray *)components
{
if ([self.clientPorts count] <= 0) {
return;
}
if ([self.clientPorts count] <= 0) {
return;
}
NSMutableSet *removedPorts = [NSMutableSet set];
NSMutableSet *removedPorts = [NSMutableSet set];
for (NSPort *port in self.clientPorts) {
@try {
NSPortMessage *message = [[NSPortMessage alloc]
initWithSendPort:port
receivePort:nil
components:components];
message.msgid = msgId;
if (![port isValid] ||
![message
sendBeforeDate:
[NSDate dateWithTimeIntervalSinceNow:
1.0]]) {
blog(LOG_DEBUG,
"failed to send message to %d, removing it from the clients!",
((NSMachPort *)port).machPort);
for (NSPort *port in self.clientPorts) {
@try {
NSPortMessage *message = [[NSPortMessage alloc] initWithSendPort:port receivePort:nil
components:components];
message.msgid = msgId;
if (![port isValid] || ![message sendBeforeDate:[NSDate dateWithTimeIntervalSinceNow:1.0]]) {
blog(LOG_DEBUG, "failed to send message to %d, removing it from the clients!",
((NSMachPort *) port).machPort);
[port invalidate];
[removedPorts addObject:port];
}
} @catch (NSException *exception) {
blog(LOG_DEBUG,
"failed to send message (exception) to %d, removing it from the clients!",
((NSMachPort *)port).machPort);
[port invalidate];
[removedPorts addObject:port];
}
} @catch (NSException *exception) {
blog(LOG_DEBUG, "failed to send message (exception) to %d, removing it from the clients!",
((NSMachPort *) port).machPort);
[port invalidate];
[removedPorts addObject:port];
}
}
[port invalidate];
[removedPorts addObject:port];
}
}
// Remove dead ports if necessary
[self.clientPorts minusSet:removedPorts];
// Remove dead ports if necessary
[self.clientPorts minusSet:removedPorts];
}
- (void)sendPixelBuffer:(CVPixelBufferRef)frame
timestamp:(uint64_t)timestamp
fpsNumerator:(uint32_t)fpsNumerator
fpsDenominator:(uint32_t)fpsDenominator
timestamp:(uint64_t)timestamp
fpsNumerator:(uint32_t)fpsNumerator
fpsDenominator:(uint32_t)fpsDenominator
{
if ([self.clientPorts count] <= 0) {
return;
}
if ([self.clientPorts count] <= 0) {
return;
}
@autoreleasepool {
NSData *timestampData = [NSData
dataWithBytes:&timestamp
length:sizeof(timestamp)];
NSData *fpsNumeratorData = [NSData
dataWithBytes:&fpsNumerator
length:sizeof(fpsNumerator)];
NSData *fpsDenominatorData = [NSData
dataWithBytes:&fpsDenominator
length:sizeof(fpsDenominator)];
@autoreleasepool {
NSData *timestampData = [NSData dataWithBytes:&timestamp length:sizeof(timestamp)];
NSData *fpsNumeratorData = [NSData dataWithBytes:&fpsNumerator length:sizeof(fpsNumerator)];
NSData *fpsDenominatorData = [NSData dataWithBytes:&fpsDenominator length:sizeof(fpsDenominator)];
IOSurfaceRef surface = CVPixelBufferGetIOSurface(frame);
IOSurfaceRef surface = CVPixelBufferGetIOSurface(frame);
#ifndef __aarch64__
IOSurfaceLock(surface, 0, NULL);
IOSurfaceLock(surface, 0, NULL);
#endif
if (!surface) {
blog(LOG_ERROR,
"unable to access IOSurface associated with CVPixelBuffer");
return;
}
if (!surface) {
blog(LOG_ERROR, "unable to access IOSurface associated with CVPixelBuffer");
return;
}
mach_port_t framePort = IOSurfaceCreateMachPort(surface);
mach_port_t framePort = IOSurfaceCreateMachPort(surface);
if (!framePort) {
blog(LOG_ERROR,
"unable to allocate mach port for IOSurface");
return;
}
if (!framePort) {
blog(LOG_ERROR, "unable to allocate mach port for IOSurface");
return;
}
[self sendMessageToClientsWithMsgId:MachMsgIdFrame
components:@[
[NSMachPort
portWithMachPort:framePort
options:NSMachPortDeallocateNone],
timestampData,
fpsNumeratorData,
fpsDenominatorData
]];
[self sendMessageToClientsWithMsgId:MachMsgIdFrame components:@[
[NSMachPort portWithMachPort:framePort options:NSMachPortDeallocateNone], timestampData, fpsNumeratorData,
fpsDenominatorData
]];
mach_port_deallocate(mach_task_self(), framePort);
mach_port_deallocate(mach_task_self(), framePort);
#ifndef __aarch64__
IOSurfaceUnlock(surface, 0, NULL);
IOSurfaceUnlock(surface, 0, NULL);
#endif
}
}
}
- (void)stop
{
blog(LOG_DEBUG, "sending stop message to %lu clients",
self.clientPorts.count);
[self sendMessageToClientsWithMsgId:MachMsgIdStop components:nil];
blog(LOG_DEBUG, "sending stop message to %lu clients", self.clientPorts.count);
[self sendMessageToClientsWithMsgId:MachMsgIdStop components:nil];
}
@end
File diff suppressed because it is too large Load Diff
+20 -25
View File
@@ -23,40 +23,35 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
void EditorWidget::buildEffectContainer(AEffect *effect)
{
NSView *view =
[[NSView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300)];
cocoaViewContainer =
QWidget::createWindowContainer(QWindow::fromWinId(WId(view)));
cocoaViewContainer->move(0, 0);
cocoaViewContainer->resize(300, 300);
cocoaViewContainer->show();
NSView *view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300)];
cocoaViewContainer = QWidget::createWindowContainer(QWindow::fromWinId(WId(view)));
cocoaViewContainer->move(0, 0);
cocoaViewContainer->resize(300, 300);
cocoaViewContainer->show();
QGridLayout *hblParams = new QGridLayout();
hblParams->setContentsMargins(0, 0, 0, 0);
hblParams->setSpacing(0);
hblParams->addWidget(cocoaViewContainer);
QGridLayout *hblParams = new QGridLayout();
hblParams->setContentsMargins(0, 0, 0, 0);
hblParams->setSpacing(0);
hblParams->addWidget(cocoaViewContainer);
VstRect *vstRect = nullptr;
effect->dispatcher(effect, effEditGetRect, 0, 0, &vstRect, 0);
if (vstRect) {
NSRect frame = NSMakeRect(vstRect->left, vstRect->top,
vstRect->right, vstRect->bottom);
VstRect *vstRect = nullptr;
effect->dispatcher(effect, effEditGetRect, 0, 0, &vstRect, 0);
if (vstRect) {
NSRect frame = NSMakeRect(vstRect->left, vstRect->top, vstRect->right, vstRect->bottom);
[view setFrame:frame];
[view setFrame:frame];
cocoaViewContainer->resize(vstRect->right - vstRect->left,
vstRect->bottom - vstRect->top);
cocoaViewContainer->resize(vstRect->right - vstRect->left, vstRect->bottom - vstRect->top);
setFixedSize(vstRect->right - vstRect->left,
vstRect->bottom - vstRect->top);
}
setFixedSize(vstRect->right - vstRect->left, vstRect->bottom - vstRect->top);
}
effect->dispatcher(effect, effEditOpen, 0, 0, view, 0);
effect->dispatcher(effect, effEditOpen, 0, 0, view, 0);
setLayout(hblParams);
setLayout(hblParams);
}
void EditorWidget::handleResizeRequest(int width, int height)
{
setFixedSize(width, height);
setFixedSize(width, height);
}
+52 -59
View File
@@ -19,81 +19,74 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
AEffect *VSTPlugin::loadEffect()
{
AEffect *newEffect = NULL;
AEffect *newEffect = NULL;
// Create a path to the bundle
CFStringRef pluginPathStringRef = CFStringCreateWithCString(
NULL, pluginPath.c_str(), kCFStringEncodingUTF8);
CFURLRef bundleUrl = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
pluginPathStringRef,
kCFURLPOSIXPathStyle,
true);
// Create a path to the bundle
CFStringRef pluginPathStringRef = CFStringCreateWithCString(NULL, pluginPath.c_str(), kCFStringEncodingUTF8);
CFURLRef bundleUrl =
CFURLCreateWithFileSystemPath(kCFAllocatorDefault, pluginPathStringRef, kCFURLPOSIXPathStyle, true);
if (bundleUrl == NULL) {
CFRelease(pluginPathStringRef);
blog(LOG_WARNING,
"Couldn't make URL reference for VST plug-in");
return NULL;
}
if (bundleUrl == NULL) {
CFRelease(pluginPathStringRef);
blog(LOG_WARNING, "Couldn't make URL reference for VST plug-in");
return NULL;
}
// Open the bundle
bundle = CFBundleCreate(kCFAllocatorDefault, bundleUrl);
if (bundle == NULL) {
blog(LOG_WARNING, "Couldn't create VST bundle reference.");
CFRelease(pluginPathStringRef);
CFRelease(bundleUrl);
return NULL;
}
// Open the bundle
bundle = CFBundleCreate(kCFAllocatorDefault, bundleUrl);
if (bundle == NULL) {
blog(LOG_WARNING, "Couldn't create VST bundle reference.");
CFRelease(pluginPathStringRef);
CFRelease(bundleUrl);
return NULL;
}
vstPluginMain mainEntryPoint = NULL;
mainEntryPoint = (vstPluginMain)CFBundleGetFunctionPointerForName(
bundle, CFSTR("VSTPluginMain"));
vstPluginMain mainEntryPoint = NULL;
mainEntryPoint = (vstPluginMain) CFBundleGetFunctionPointerForName(bundle, CFSTR("VSTPluginMain"));
// VST plugins previous to the 2.4 SDK used main_macho for the
// entry point name.
if (mainEntryPoint == NULL) {
mainEntryPoint =
(vstPluginMain)CFBundleGetFunctionPointerForName(
bundle, CFSTR("main_macho"));
}
// VST plugins previous to the 2.4 SDK used main_macho for the
// entry point name.
if (mainEntryPoint == NULL) {
mainEntryPoint = (vstPluginMain) CFBundleGetFunctionPointerForName(bundle, CFSTR("main_macho"));
}
if (mainEntryPoint == NULL) {
blog(LOG_WARNING, "Couldn't get a pointer to plug-in's main()");
CFRelease(pluginPathStringRef);
CFRelease(bundleUrl);
CFRelease(bundle);
bundle = NULL;
return NULL;
}
if (mainEntryPoint == NULL) {
blog(LOG_WARNING, "Couldn't get a pointer to plug-in's main()");
CFRelease(pluginPathStringRef);
CFRelease(bundleUrl);
CFRelease(bundle);
bundle = NULL;
return NULL;
}
newEffect = mainEntryPoint(hostCallback_static);
if (newEffect == NULL) {
blog(LOG_WARNING, "VST Plug-in's main() returns null.");
CFRelease(pluginPathStringRef);
CFRelease(bundleUrl);
CFRelease(bundle);
bundle = NULL;
return NULL;
}
newEffect = mainEntryPoint(hostCallback_static);
if (newEffect == NULL) {
blog(LOG_WARNING, "VST Plug-in's main() returns null.");
CFRelease(pluginPathStringRef);
CFRelease(bundleUrl);
CFRelease(bundle);
bundle = NULL;
return NULL;
}
newEffect->user = this;
newEffect->user = this;
// Clean up
CFRelease(pluginPathStringRef);
CFRelease(bundleUrl);
// Clean up
CFRelease(pluginPathStringRef);
CFRelease(bundleUrl);
return newEffect;
return newEffect;
}
void VSTPlugin::unloadLibrary()
{
if (bundle) {
CFRelease(bundle);
bundle = NULL;
}
if (bundle) {
CFRelease(bundle);
bundle = NULL;
}
}
bool VSTPlugin::vstLoaded()
{
return (bundle != NULL);
return (bundle != NULL);
}
+58 -75
View File
@@ -9,117 +9,100 @@ extern void save_font_list(void);
static inline void add_path_font(const char *path)
{
FT_Face face;
FT_Long idx = 0;
FT_Long max_faces = 1;
FT_Face face;
FT_Long idx = 0;
FT_Long max_faces = 1;
while (idx < max_faces) {
if (FT_New_Face(ft2_lib, path, idx, &face) != 0)
break;
while (idx < max_faces) {
if (FT_New_Face(ft2_lib, path, idx, &face) != 0)
break;
build_font_path_info(face, idx++, path);
max_faces = face->num_faces;
FT_Done_Face(face);
}
build_font_path_info(face, idx++, path);
max_faces = face->num_faces;
FT_Done_Face(face);
}
}
static void add_path_fonts(NSFileManager *file_manager, NSString *path)
{
NSArray *files = NULL;
NSArray *files = NULL;
files = [file_manager contentsOfDirectoryAtPath:path error:nil];
files = [file_manager contentsOfDirectoryAtPath:path error:nil];
for (NSString *file in files) {
NSString *full_path =
[path stringByAppendingPathComponent:file];
for (NSString *file in files) {
NSString *full_path = [path stringByAppendingPathComponent:file];
BOOL is_dir = FALSE;
bool folder_exists = [file_manager fileExistsAtPath:full_path
isDirectory:&is_dir];
BOOL is_dir = FALSE;
bool folder_exists = [file_manager fileExistsAtPath:full_path isDirectory:&is_dir];
if (folder_exists && is_dir) {
add_path_fonts(file_manager, full_path);
} else {
add_path_font(full_path.fileSystemRepresentation);
}
}
if (folder_exists && is_dir) {
add_path_fonts(file_manager, full_path);
} else {
add_path_font(full_path.fileSystemRepresentation);
}
}
}
void load_os_font_list(void)
{
@autoreleasepool {
BOOL is_dir;
NSArray *paths = NSSearchPathForDirectoriesInDomains(
NSLibraryDirectory, NSAllDomainsMask, true);
@autoreleasepool {
BOOL is_dir;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask, true);
for (NSString *path in paths) {
NSFileManager *file_manager =
[NSFileManager defaultManager];
NSString *font_path =
[path stringByAppendingPathComponent:@"Fonts"];
for (NSString *path in paths) {
NSFileManager *file_manager = [NSFileManager defaultManager];
NSString *font_path = [path stringByAppendingPathComponent:@"Fonts"];
bool folder_exists = [file_manager
fileExistsAtPath:font_path
isDirectory:&is_dir];
bool folder_exists = [file_manager fileExistsAtPath:font_path isDirectory:&is_dir];
if (folder_exists && is_dir)
add_path_fonts(file_manager, font_path);
}
if (folder_exists && is_dir)
add_path_fonts(file_manager, font_path);
}
save_font_list();
}
save_font_list();
}
}
static uint32_t add_font_checksum(uint32_t checksum, const char *path)
{
if (path && *path)
checksum = calc_crc32(checksum, path, strlen(path));
return checksum;
if (path && *path)
checksum = calc_crc32(checksum, path, strlen(path));
return checksum;
}
static uint32_t add_font_checksum_path(uint32_t checksum,
NSFileManager *file_manager,
NSString *path)
static uint32_t add_font_checksum_path(uint32_t checksum, NSFileManager *file_manager, NSString *path)
{
NSArray *files = NULL;
NSArray *files = NULL;
files = [file_manager contentsOfDirectoryAtPath:path error:nil];
files = [file_manager contentsOfDirectoryAtPath:path error:nil];
for (NSString *file in files) {
NSString *full_path =
[path stringByAppendingPathComponent:file];
for (NSString *file in files) {
NSString *full_path = [path stringByAppendingPathComponent:file];
checksum = add_font_checksum(
checksum, full_path.fileSystemRepresentation);
}
checksum = add_font_checksum(checksum, full_path.fileSystemRepresentation);
}
return checksum;
return checksum;
}
uint32_t get_font_checksum(void)
{
uint32_t checksum = 0;
uint32_t checksum = 0;
@autoreleasepool {
BOOL is_dir;
NSArray *paths = NSSearchPathForDirectoriesInDomains(
NSLibraryDirectory, NSAllDomainsMask, true);
@autoreleasepool {
BOOL is_dir;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask, true);
for (NSString *path in paths) {
NSFileManager *file_manager =
[NSFileManager defaultManager];
NSString *font_path =
[path stringByAppendingPathComponent:@"Fonts"];
for (NSString *path in paths) {
NSFileManager *file_manager = [NSFileManager defaultManager];
NSString *font_path = [path stringByAppendingPathComponent:@"Fonts"];
bool folder_exists = [file_manager
fileExistsAtPath:font_path
isDirectory:&is_dir];
bool folder_exists = [file_manager fileExistsAtPath:font_path isDirectory:&is_dir];
if (folder_exists && is_dir)
checksum = add_font_checksum_path(
checksum, file_manager, font_path);
}
}
if (folder_exists && is_dir)
checksum = add_font_checksum_path(checksum, file_manager, font_path);
}
}
return checksum;
return checksum;
}