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

This commit is contained in:
PatTheMav 2023-06-04 03:26:09 +02:00 committed by Patrick Heyer
parent db7491083c
commit f8e00d6071
36 changed files with 6822 additions and 7956 deletions

View file

@ -6,40 +6,41 @@ using namespace std;
void GetWindowList(vector<string> &windows)
{
windows.resize(0);
windows.resize(0);
@autoreleasepool {
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
NSArray *array = [ws runningApplications];
for (NSRunningApplication *app in array) {
NSString *name = app.localizedName;
if (!name)
continue;
@autoreleasepool {
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
NSArray *array = [ws runningApplications];
for (NSRunningApplication *app in array) {
NSString *name = app.localizedName;
if (!name)
continue;
const char *str = name.UTF8String;
if (str && *str)
windows.emplace_back(str);
}
}
const char *str = name.UTF8String;
if (str && *str)
windows.emplace_back(str);
}
}
}
void GetCurrentWindowTitle(string &title)
{
title.resize(0);
title.resize(0);
@autoreleasepool {
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
NSRunningApplication *app = [ws frontmostApplication];
if (app) {
NSString *name = app.localizedName;
if (!name)
return;
@autoreleasepool {
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
NSRunningApplication *app = [ws frontmostApplication];
if (app) {
NSString *name = app.localizedName;
if (!name)
return;
const char *str = name.UTF8String;
if (str && *str)
title = str;
}
}
const char *str = name.UTF8String;
if (str && *str)
title = str;
}
}
}
void CleanupSceneSwitcher() {}
void CleanupSceneSwitcher()
{}

View file

@ -34,341 +34,289 @@ using namespace std;
bool isInBundle()
{
NSRunningApplication *app = [NSRunningApplication currentApplication];
return [app bundleIdentifier] != nil;
NSRunningApplication *app = [NSRunningApplication currentApplication];
return [app bundleIdentifier] != nil;
}
bool GetDataFilePath(const char *data, string &output)
{
NSURL *bundleUrl = [[NSBundle mainBundle] bundleURL];
NSString *path = [[bundleUrl path]
stringByAppendingFormat:@"/%@/%s", @"Contents/Resources", data];
output = path.UTF8String;
NSURL *bundleUrl = [[NSBundle mainBundle] bundleURL];
NSString *path = [[bundleUrl path] stringByAppendingFormat:@"/%@/%s", @"Contents/Resources", data];
output = path.UTF8String;
return !access(output.c_str(), R_OK);
return !access(output.c_str(), R_OK);
}
void CheckIfAlreadyRunning(bool &already_running)
{
NSString *bundleId = [[NSBundle mainBundle] bundleIdentifier];
NSString *bundleId = [[NSBundle mainBundle] bundleIdentifier];
NSUInteger appCount = [[NSRunningApplication
runningApplicationsWithBundleIdentifier:bundleId] count];
NSUInteger appCount = [[NSRunningApplication runningApplicationsWithBundleIdentifier:bundleId] count];
already_running = appCount > 1;
already_running = appCount > 1;
}
string GetDefaultVideoSavePath()
{
NSFileManager *fm = [NSFileManager defaultManager];
NSURL *url = [fm URLForDirectory:NSMoviesDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:true
error:nil];
NSFileManager *fm = [NSFileManager defaultManager];
NSURL *url = [fm URLForDirectory:NSMoviesDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:true
error:nil];
if (!url)
return getenv("HOME");
if (!url)
return getenv("HOME");
return url.path.fileSystemRepresentation;
return url.path.fileSystemRepresentation;
}
vector<string> GetPreferredLocales()
{
NSArray *preferred = [NSLocale preferredLanguages];
NSArray *preferred = [NSLocale preferredLanguages];
auto locales = GetLocaleNames();
auto lang_to_locale = [&locales](string lang) -> string {
string lang_match = "";
auto locales = GetLocaleNames();
auto lang_to_locale = [&locales](string lang) -> string {
string lang_match = "";
for (const auto &locale : locales) {
if (locale.first == lang.substr(0, locale.first.size()))
return locale.first;
for (const auto &locale : locales) {
if (locale.first == lang.substr(0, locale.first.size()))
return locale.first;
if (!lang_match.size() &&
locale.first.substr(0, 2) == lang.substr(0, 2))
lang_match = locale.first;
}
if (!lang_match.size() && locale.first.substr(0, 2) == lang.substr(0, 2))
lang_match = locale.first;
}
return lang_match;
};
return lang_match;
};
vector<string> result;
result.reserve(preferred.count);
vector<string> result;
result.reserve(preferred.count);
for (NSString *lang in preferred) {
string locale = lang_to_locale(lang.UTF8String);
if (!locale.size())
continue;
for (NSString *lang in preferred) {
string locale = lang_to_locale(lang.UTF8String);
if (!locale.size())
continue;
if (find(begin(result), end(result), locale) != end(result))
continue;
if (find(begin(result), end(result), locale) != end(result))
continue;
result.emplace_back(locale);
}
result.emplace_back(locale);
}
return result;
return result;
}
bool IsAlwaysOnTop(QWidget *window)
{
return (window->windowFlags() & Qt::WindowStaysOnTopHint) != 0;
return (window->windowFlags() & Qt::WindowStaysOnTopHint) != 0;
}
void disableColorSpaceConversion(QWidget *window)
{
NSView *view =
(__bridge NSView *)reinterpret_cast<void *>(window->winId());
view.window.colorSpace = NSColorSpace.sRGBColorSpace;
NSView *view = (__bridge NSView *) reinterpret_cast<void *>(window->winId());
view.window.colorSpace = NSColorSpace.sRGBColorSpace;
}
void SetAlwaysOnTop(QWidget *window, bool enable)
{
Qt::WindowFlags flags = window->windowFlags();
Qt::WindowFlags flags = window->windowFlags();
if (enable) {
NSView *view = (__bridge NSView *)reinterpret_cast<void *>(
window->winId());
if (enable) {
NSView *view = (__bridge NSView *) reinterpret_cast<void *>(window->winId());
[[view window] setLevel:NSScreenSaverWindowLevel];
[[view window] setLevel:NSScreenSaverWindowLevel];
flags |= Qt::WindowStaysOnTopHint;
} else {
flags &= ~Qt::WindowStaysOnTopHint;
}
flags |= Qt::WindowStaysOnTopHint;
} else {
flags &= ~Qt::WindowStaysOnTopHint;
}
window->setWindowFlags(flags);
window->show();
window->setWindowFlags(flags);
window->show();
}
bool SetDisplayAffinitySupported(void)
{
// Not implemented yet
return false;
// Not implemented yet
return false;
}
typedef void (*set_int_t)(int);
void EnableOSXVSync(bool enable)
{
static bool initialized = false;
static bool valid = false;
static set_int_t set_debug_options = nullptr;
static set_int_t deferred_updates = nullptr;
static bool initialized = false;
static bool valid = false;
static set_int_t set_debug_options = nullptr;
static set_int_t deferred_updates = nullptr;
if (!initialized) {
void *quartzCore = dlopen("/System/Library/Frameworks/"
"QuartzCore.framework/QuartzCore",
RTLD_LAZY);
if (quartzCore) {
set_debug_options = (set_int_t)dlsym(
quartzCore, "CGSSetDebugOptions");
deferred_updates = (set_int_t)dlsym(
quartzCore, "CGSDeferredUpdates");
if (!initialized) {
void *quartzCore = dlopen("/System/Library/Frameworks/"
"QuartzCore.framework/QuartzCore",
RTLD_LAZY);
if (quartzCore) {
set_debug_options = (set_int_t) dlsym(quartzCore, "CGSSetDebugOptions");
deferred_updates = (set_int_t) dlsym(quartzCore, "CGSDeferredUpdates");
valid = set_debug_options && deferred_updates;
}
valid = set_debug_options && deferred_updates;
}
initialized = true;
}
initialized = true;
}
if (valid) {
set_debug_options(enable ? 0 : 0x08000000);
deferred_updates(enable ? 1 : 0);
}
if (valid) {
set_debug_options(enable ? 0 : 0x08000000);
deferred_updates(enable ? 1 : 0);
}
}
void EnableOSXDockIcon(bool enable)
{
if (enable)
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
else
[NSApp setActivationPolicy:
NSApplicationActivationPolicyProhibited];
if (enable)
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
else
[NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];
}
@interface DockView : NSView {
@private
QIcon _icon;
@private
QIcon _icon;
}
@end
@implementation DockView
- (id)initWithIcon:(QIcon)icon
{
self = [super init];
_icon = icon;
return self;
self = [super init];
_icon = icon;
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
CGSize size = dirtyRect.size;
CGSize size = dirtyRect.size;
/* Draw regular app icon */
NSImage *appIcon = [[NSWorkspace sharedWorkspace]
iconForFile:[[NSBundle mainBundle] bundlePath]];
[appIcon drawInRect:CGRectMake(0, 0, size.width, size.height)];
/* Draw regular app icon */
NSImage *appIcon = [[NSWorkspace sharedWorkspace] iconForFile:[[NSBundle mainBundle] bundlePath]];
[appIcon drawInRect:CGRectMake(0, 0, size.width, size.height)];
/* Draw small icon on top */
float iconSize = 0.45;
CGImageRef image =
_icon.pixmap(size.width, size.height).toImage().toCGImage();
CGContextRef context = [[NSGraphicsContext currentContext] CGContext];
CGContextDrawImage(context,
CGRectMake(size.width * (1 - iconSize), 0,
size.width * iconSize,
size.height * iconSize),
image);
CGImageRelease(image);
/* Draw small icon on top */
float iconSize = 0.45;
CGImageRef image = _icon.pixmap(size.width, size.height).toImage().toCGImage();
CGContextRef context = [[NSGraphicsContext currentContext] CGContext];
CGContextDrawImage(
context, CGRectMake(size.width * (1 - iconSize), 0, size.width * iconSize, size.height * iconSize), image);
CGImageRelease(image);
}
@end
MacPermissionStatus CheckPermissionWithPrompt(MacPermissionType type,
bool prompt_for_permission)
MacPermissionStatus CheckPermissionWithPrompt(MacPermissionType type, bool prompt_for_permission)
{
__block MacPermissionStatus permissionResponse =
kPermissionNotDetermined;
__block MacPermissionStatus permissionResponse = kPermissionNotDetermined;
switch (type) {
case kAudioDeviceAccess: {
AVAuthorizationStatus audioStatus = [AVCaptureDevice
authorizationStatusForMediaType:AVMediaTypeAudio];
switch (type) {
case kAudioDeviceAccess: {
AVAuthorizationStatus audioStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
if (audioStatus == AVAuthorizationStatusNotDetermined &&
prompt_for_permission) {
os_event_t *block_finished;
os_event_init(&block_finished, OS_EVENT_TYPE_MANUAL);
[AVCaptureDevice
requestAccessForMediaType:AVMediaTypeAudio
completionHandler:^(
BOOL granted
__attribute((unused))) {
os_event_signal(block_finished);
}];
os_event_wait(block_finished);
os_event_destroy(block_finished);
audioStatus = [AVCaptureDevice
authorizationStatusForMediaType:AVMediaTypeAudio];
}
if (audioStatus == AVAuthorizationStatusNotDetermined && prompt_for_permission) {
os_event_t *block_finished;
os_event_init(&block_finished, OS_EVENT_TYPE_MANUAL);
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio
completionHandler:^(BOOL granted __attribute((unused))) {
os_event_signal(block_finished);
}];
os_event_wait(block_finished);
os_event_destroy(block_finished);
audioStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
}
permissionResponse = (MacPermissionStatus)audioStatus;
permissionResponse = (MacPermissionStatus) audioStatus;
blog(LOG_INFO, "[macOS] Permission for audio device access %s.",
permissionResponse == kPermissionAuthorized ? "granted"
: "denied");
blog(LOG_INFO, "[macOS] Permission for audio device access %s.",
permissionResponse == kPermissionAuthorized ? "granted" : "denied");
break;
}
case kVideoDeviceAccess: {
AVAuthorizationStatus videoStatus = [AVCaptureDevice
authorizationStatusForMediaType:AVMediaTypeVideo];
break;
}
case kVideoDeviceAccess: {
AVAuthorizationStatus videoStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (videoStatus == AVAuthorizationStatusNotDetermined &&
prompt_for_permission) {
os_event_t *block_finished;
os_event_init(&block_finished, OS_EVENT_TYPE_MANUAL);
[AVCaptureDevice
requestAccessForMediaType:AVMediaTypeVideo
completionHandler:^(
BOOL granted
__attribute((unused))) {
os_event_signal(block_finished);
}];
if (videoStatus == AVAuthorizationStatusNotDetermined && prompt_for_permission) {
os_event_t *block_finished;
os_event_init(&block_finished, OS_EVENT_TYPE_MANUAL);
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo
completionHandler:^(BOOL granted __attribute((unused))) {
os_event_signal(block_finished);
}];
os_event_wait(block_finished);
os_event_destroy(block_finished);
videoStatus = [AVCaptureDevice
authorizationStatusForMediaType:AVMediaTypeVideo];
}
os_event_wait(block_finished);
os_event_destroy(block_finished);
videoStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
}
permissionResponse = (MacPermissionStatus)videoStatus;
permissionResponse = (MacPermissionStatus) videoStatus;
blog(LOG_INFO, "[macOS] Permission for video device access %s.",
permissionResponse == kPermissionAuthorized ? "granted"
: "denied");
blog(LOG_INFO, "[macOS] Permission for video device access %s.",
permissionResponse == kPermissionAuthorized ? "granted" : "denied");
break;
}
case kScreenCapture: {
permissionResponse = (CGPreflightScreenCaptureAccess()
? kPermissionAuthorized
: kPermissionDenied);
break;
}
case kScreenCapture: {
permissionResponse = (CGPreflightScreenCaptureAccess() ? kPermissionAuthorized : kPermissionDenied);
if (permissionResponse != kPermissionAuthorized &&
prompt_for_permission) {
permissionResponse = (CGRequestScreenCaptureAccess()
? kPermissionAuthorized
: kPermissionDenied);
}
if (permissionResponse != kPermissionAuthorized && prompt_for_permission) {
permissionResponse = (CGRequestScreenCaptureAccess() ? kPermissionAuthorized : kPermissionDenied);
}
blog(LOG_INFO, "[macOS] Permission for screen capture %s.",
permissionResponse == kPermissionAuthorized ? "granted"
: "denied");
blog(LOG_INFO, "[macOS] Permission for screen capture %s.",
permissionResponse == kPermissionAuthorized ? "granted" : "denied");
break;
}
case kAccessibility: {
permissionResponse = (AXIsProcessTrusted()
? kPermissionAuthorized
: kPermissionDenied);
break;
}
case kAccessibility: {
permissionResponse = (AXIsProcessTrusted() ? kPermissionAuthorized : kPermissionDenied);
if (permissionResponse != kPermissionAuthorized &&
prompt_for_permission) {
NSDictionary *options = @{
(__bridge id)kAXTrustedCheckOptionPrompt: @YES
};
permissionResponse = (AXIsProcessTrustedWithOptions(
(CFDictionaryRef)options)
? kPermissionAuthorized
: kPermissionDenied);
}
if (permissionResponse != kPermissionAuthorized && prompt_for_permission) {
NSDictionary *options = @{(__bridge id) kAXTrustedCheckOptionPrompt: @YES};
permissionResponse = (AXIsProcessTrustedWithOptions((CFDictionaryRef) options) ? kPermissionAuthorized
: kPermissionDenied);
}
blog(LOG_INFO, "[macOS] Permission for accessibility %s.",
permissionResponse == kPermissionAuthorized ? "granted"
: "denied");
break;
}
}
blog(LOG_INFO, "[macOS] Permission for accessibility %s.",
permissionResponse == kPermissionAuthorized ? "granted" : "denied");
break;
}
}
return permissionResponse;
return permissionResponse;
}
void OpenMacOSPrivacyPreferences(const char *tab)
{
NSURL *url = [NSURL
URLWithString:
[NSString
stringWithFormat:
@"x-apple.systempreferences:com.apple.preference.security?Privacy_%s",
tab]];
[[NSWorkspace sharedWorkspace] openURL:url];
NSURL *url = [NSURL
URLWithString:[NSString
stringWithFormat:@"x-apple.systempreferences:com.apple.preference.security?Privacy_%s", tab]];
[[NSWorkspace sharedWorkspace] openURL:url];
}
void SetMacOSDarkMode(bool dark)
{
if (dark) {
NSApp.appearance =
[NSAppearance appearanceNamed:NSAppearanceNameDarkAqua];
} else {
NSApp.appearance =
[NSAppearance appearanceNamed:NSAppearanceNameAqua];
}
if (dark) {
NSApp.appearance = [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua];
} else {
NSApp.appearance = [NSAppearance appearanceNamed:NSAppearanceNameAqua];
}
}
void TaskbarOverlayInit() {}
void TaskbarOverlayInit()
{}
void TaskbarOverlaySetStatus(TaskbarOverlayStatus status)
{
QIcon icon;
if (status == TaskbarOverlayStatusActive)
icon = QIcon::fromTheme("obs-active",
QIcon(":/res/images/active_mac.png"));
else if (status == TaskbarOverlayStatusPaused)
icon = QIcon::fromTheme("obs-paused",
QIcon(":/res/images/paused_mac.png"));
QIcon icon;
if (status == TaskbarOverlayStatusActive)
icon = QIcon::fromTheme("obs-active", QIcon(":/res/images/active_mac.png"));
else if (status == TaskbarOverlayStatusPaused)
icon = QIcon::fromTheme("obs-paused", QIcon(":/res/images/paused_mac.png"));
NSDockTile *tile = [NSApp dockTile];
[tile setContentView:[[DockView alloc] initWithIcon:icon]];
[tile display];
NSDockTile *tile = [NSApp dockTile];
[tile setContentView:[[DockView alloc] initWithIcon:icon]];
[tile display];
}
/*
@ -389,22 +337,22 @@ void TaskbarOverlaySetStatus(TaskbarOverlayStatus status)
@implementation OBSApplication
- (void)sendEvent:(NSEvent *)event
{
_handlingSendEvent = YES;
[super sendEvent:event];
_handlingSendEvent = NO;
_handlingSendEvent = YES;
[super sendEvent:event];
_handlingSendEvent = NO;
}
@end
void InstallNSThreadLocks()
{
[[NSThread new] start];
[[NSThread new] start];
if ([NSThread isMultiThreaded] != 1) {
abort();
}
if ([NSThread isMultiThreaded] != 1) {
abort();
}
}
void InstallNSApplicationSubclass()
{
[OBSApplication sharedApplication];
[OBSApplication sharedApplication];
}

View file

@ -4,29 +4,25 @@
#import <Security/Security.h>
#import <Security/SecKey.h>
bool VerifySignature(const uint8_t *pubKey, const size_t pubKeyLen,
const uint8_t *buf, const size_t len, const uint8_t *sig,
const size_t sigLen)
bool VerifySignature(const uint8_t *pubKey, const size_t pubKeyLen, const uint8_t *buf, const size_t len,
const uint8_t *sig, const size_t sigLen)
{
NSData *pubKeyData = [NSData dataWithBytes:pubKey length:pubKeyLen];
CFArrayRef items = nullptr;
NSData *pubKeyData = [NSData dataWithBytes:pubKey length:pubKeyLen];
CFArrayRef items = nullptr;
OSStatus res = SecItemImport((CFDataRef)pubKeyData, nullptr, nullptr,
nullptr, (SecItemImportExportFlags)0,
nullptr, nullptr, &items);
if (res != errSecSuccess)
return false;
OSStatus res = SecItemImport((CFDataRef) pubKeyData, nullptr, nullptr, nullptr, (SecItemImportExportFlags) 0,
nullptr, nullptr, &items);
if (res != errSecSuccess)
return false;
SecKeyRef pubKeyRef = (SecKeyRef)CFArrayGetValueAtIndex(items, 0);
NSData *signedData = [NSData dataWithBytes:buf length:len];
NSData *signature = [NSData dataWithBytes:sig length:sigLen];
SecKeyRef pubKeyRef = (SecKeyRef) CFArrayGetValueAtIndex(items, 0);
NSData *signedData = [NSData dataWithBytes:buf length:len];
NSData *signature = [NSData dataWithBytes:sig length:sigLen];
CFErrorRef errRef;
bool result = SecKeyVerifySignature(
pubKeyRef, kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA512,
(__bridge CFDataRef)signedData, (__bridge CFDataRef)signature,
&errRef);
CFErrorRef errRef;
bool result = SecKeyVerifySignature(pubKeyRef, kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA512,
(__bridge CFDataRef) signedData, (__bridge CFDataRef) signature, &errRef);
CFRelease(items);
return result;
CFRelease(items);
return result;
};

View file

@ -16,80 +16,64 @@
@synthesize branch;
- (nonnull NSSet<NSString *> *)allowedChannelsForUpdater:
(nonnull SPUUpdater *)updater
- (nonnull NSSet<NSString *> *)allowedChannelsForUpdater:(nonnull SPUUpdater *)updater
{
return [NSSet setWithObject:branch];
return [NSSet setWithObject:branch];
}
- (void)observeCanCheckForUpdatesWithAction:(QAction *)action
{
[_updaterController.updater
addObserver:self
forKeyPath:NSStringFromSelector(@selector(canCheckForUpdates))
options:(NSKeyValueObservingOptionInitial |
NSKeyValueObservingOptionNew)
context:(void *)action];
[_updaterController.updater addObserver:self forKeyPath:NSStringFromSelector(@selector(canCheckForUpdates))
options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew)
context:(void *) action];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey, id> *)change
context:(void *)context
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey, id> *)change
context:(void *)context
{
if ([keyPath isEqualToString:NSStringFromSelector(
@selector(canCheckForUpdates))]) {
QAction *menuAction = (QAction *)context;
menuAction->setEnabled(
_updaterController.updater.canCheckForUpdates);
} else {
[super observeValueForKeyPath:keyPath
ofObject:object
change:change
context:context];
}
if ([keyPath isEqualToString:NSStringFromSelector(@selector(canCheckForUpdates))]) {
QAction *menuAction = (QAction *) context;
menuAction->setEnabled(_updaterController.updater.canCheckForUpdates);
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)dealloc
{
@autoreleasepool {
[_updaterController.updater
removeObserver:self
forKeyPath:NSStringFromSelector(
@selector(canCheckForUpdates))];
}
@autoreleasepool {
[_updaterController.updater removeObserver:self forKeyPath:NSStringFromSelector(@selector(canCheckForUpdates))];
}
}
@end
OBSSparkle::OBSSparkle(const char *branch, QAction *checkForUpdatesAction)
{
@autoreleasepool {
updaterDelegate = [[OBSUpdateDelegate alloc] init];
updaterDelegate.branch = [NSString stringWithUTF8String:branch];
updaterDelegate.updaterController =
[[SPUStandardUpdaterController alloc]
initWithStartingUpdater:YES
updaterDelegate:updaterDelegate
userDriverDelegate:nil];
[updaterDelegate observeCanCheckForUpdatesWithAction:
checkForUpdatesAction];
}
@autoreleasepool {
updaterDelegate = [[OBSUpdateDelegate alloc] init];
updaterDelegate.branch = [NSString stringWithUTF8String:branch];
updaterDelegate.updaterController =
[[SPUStandardUpdaterController alloc] initWithStartingUpdater:YES updaterDelegate:updaterDelegate
userDriverDelegate:nil];
[updaterDelegate observeCanCheckForUpdatesWithAction:checkForUpdatesAction];
}
}
void OBSSparkle::setBranch(const char *branch)
{
updaterDelegate.branch = [NSString stringWithUTF8String:branch];
updaterDelegate.branch = [NSString stringWithUTF8String:branch];
}
void OBSSparkle::checkForUpdates(bool manualCheck)
{
@autoreleasepool {
if (manualCheck) {
[updaterDelegate.updaterController checkForUpdates:nil];
} else {
[updaterDelegate.updaterController
.updater checkForUpdatesInBackground];
}
}
@autoreleasepool {
if (manualCheck) {
[updaterDelegate.updaterController checkForUpdates:nil];
} else {
[updaterDelegate.updaterController.updater checkForUpdatesInBackground];
}
}
}

View file

@ -22,443 +22,421 @@
#import <AppKit/AppKit.h>
struct gl_windowinfo {
NSView *view;
NSOpenGLContext *context;
gs_texture_t *texture;
GLuint fbo;
NSView *view;
NSOpenGLContext *context;
gs_texture_t *texture;
GLuint fbo;
};
struct gl_platform {
NSOpenGLContext *context;
NSOpenGLContext *context;
};
static NSOpenGLContext *gl_context_create(NSOpenGLContext *share)
{
NSOpenGLPixelFormatAttribute attributes[] = {
NSOpenGLPFADoubleBuffer, NSOpenGLPFAOpenGLProfile,
NSOpenGLProfileVersion3_2Core, 0};
NSOpenGLPixelFormatAttribute attributes[] = {NSOpenGLPFADoubleBuffer, NSOpenGLPFAOpenGLProfile,
NSOpenGLProfileVersion3_2Core, 0};
NSOpenGLPixelFormat *pf;
pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
if (!pf) {
blog(LOG_ERROR, "Failed to create pixel format");
return NULL;
}
NSOpenGLPixelFormat *pf;
pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
if (!pf) {
blog(LOG_ERROR, "Failed to create pixel format");
return NULL;
}
NSOpenGLContext *context;
context = [[NSOpenGLContext alloc] initWithFormat:pf
shareContext:share];
[pf release];
if (!context) {
blog(LOG_ERROR, "Failed to create context");
return NULL;
}
NSOpenGLContext *context;
context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:share];
[pf release];
if (!context) {
blog(LOG_ERROR, "Failed to create context");
return NULL;
}
[context clearDrawable];
[context clearDrawable];
return context;
return context;
}
struct gl_platform *gl_platform_create(gs_device_t *device __unused,
uint32_t adapter __unused)
struct gl_platform *gl_platform_create(gs_device_t *device __unused, uint32_t adapter __unused)
{
NSOpenGLContext *context = gl_context_create(nil);
if (!context) {
blog(LOG_ERROR, "gl_context_create failed");
return NULL;
}
NSOpenGLContext *context = gl_context_create(nil);
if (!context) {
blog(LOG_ERROR, "gl_context_create failed");
return NULL;
}
[context makeCurrentContext];
GLint interval = 0;
[context setValues:&interval
forParameter:NSOpenGLContextParameterSwapInterval];
const bool success = gladLoadGL() != 0;
[context makeCurrentContext];
GLint interval = 0;
[context setValues:&interval forParameter:NSOpenGLContextParameterSwapInterval];
const bool success = gladLoadGL() != 0;
if (!success) {
blog(LOG_ERROR, "gladLoadGL failed");
[context release];
return NULL;
}
if (!success) {
blog(LOG_ERROR, "gladLoadGL failed");
[context release];
return NULL;
}
struct gl_platform *plat = bzalloc(sizeof(struct gl_platform));
plat->context = context;
return plat;
struct gl_platform *plat = bzalloc(sizeof(struct gl_platform));
plat->context = context;
return plat;
}
void gl_platform_destroy(struct gl_platform *platform)
{
if (!platform)
return;
if (!platform)
return;
[platform->context release];
platform->context = nil;
[platform->context release];
platform->context = nil;
bfree(platform);
bfree(platform);
}
bool gl_platform_init_swapchain(struct gs_swap_chain *swap)
{
NSOpenGLContext *parent = swap->device->plat->context;
NSOpenGLContext *context = gl_context_create(parent);
bool success = context != nil;
if (success) {
CGLContextObj parent_obj = [parent CGLContextObj];
CGLLockContext(parent_obj);
NSOpenGLContext *parent = swap->device->plat->context;
NSOpenGLContext *context = gl_context_create(parent);
bool success = context != nil;
if (success) {
CGLContextObj parent_obj = [parent CGLContextObj];
CGLLockContext(parent_obj);
[parent makeCurrentContext];
struct gs_init_data *init_data = &swap->info;
swap->wi->texture = device_texture_create(
swap->device, init_data->cx, init_data->cy,
init_data->format, 1, NULL, GS_RENDER_TARGET);
glFlush();
[NSOpenGLContext clearCurrentContext];
[parent makeCurrentContext];
struct gs_init_data *init_data = &swap->info;
swap->wi->texture = device_texture_create(swap->device, init_data->cx, init_data->cy, init_data->format, 1,
NULL, GS_RENDER_TARGET);
glFlush();
[NSOpenGLContext clearCurrentContext];
CGLContextObj context_obj = [context CGLContextObj];
CGLLockContext(context_obj);
CGLContextObj context_obj = [context CGLContextObj];
CGLLockContext(context_obj);
[context makeCurrentContext];
[context makeCurrentContext];
#pragma clang diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
[context setView:swap->wi->view];
[context setView:swap->wi->view];
#pragma clang diagnostic pop
GLint interval = 0;
[context setValues:&interval
forParameter:NSOpenGLContextParameterSwapInterval];
gl_gen_framebuffers(1, &swap->wi->fbo);
gl_bind_framebuffer(GL_FRAMEBUFFER, swap->wi->fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
swap->wi->texture->texture, 0);
gl_success("glFrameBufferTexture2D");
glFlush();
[NSOpenGLContext clearCurrentContext];
GLint interval = 0;
[context setValues:&interval forParameter:NSOpenGLContextParameterSwapInterval];
gl_gen_framebuffers(1, &swap->wi->fbo);
gl_bind_framebuffer(GL_FRAMEBUFFER, swap->wi->fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, swap->wi->texture->texture, 0);
gl_success("glFrameBufferTexture2D");
glFlush();
[NSOpenGLContext clearCurrentContext];
CGLUnlockContext(context_obj);
CGLUnlockContext(context_obj);
CGLUnlockContext(parent_obj);
CGLUnlockContext(parent_obj);
swap->wi->context = context;
}
swap->wi->context = context;
}
return success;
return success;
}
void gl_platform_cleanup_swapchain(struct gs_swap_chain *swap)
{
NSOpenGLContext *parent = swap->device->plat->context;
CGLContextObj parent_obj = [parent CGLContextObj];
CGLLockContext(parent_obj);
NSOpenGLContext *parent = swap->device->plat->context;
CGLContextObj parent_obj = [parent CGLContextObj];
CGLLockContext(parent_obj);
NSOpenGLContext *context = swap->wi->context;
CGLContextObj context_obj = [context CGLContextObj];
CGLLockContext(context_obj);
NSOpenGLContext *context = swap->wi->context;
CGLContextObj context_obj = [context CGLContextObj];
CGLLockContext(context_obj);
[context makeCurrentContext];
gl_delete_framebuffers(1, &swap->wi->fbo);
glFlush();
[NSOpenGLContext clearCurrentContext];
[context makeCurrentContext];
gl_delete_framebuffers(1, &swap->wi->fbo);
glFlush();
[NSOpenGLContext clearCurrentContext];
CGLUnlockContext(context_obj);
CGLUnlockContext(context_obj);
[parent makeCurrentContext];
gs_texture_destroy(swap->wi->texture);
glFlush();
[NSOpenGLContext clearCurrentContext];
swap->wi->context = nil;
[parent makeCurrentContext];
gs_texture_destroy(swap->wi->texture);
glFlush();
[NSOpenGLContext clearCurrentContext];
swap->wi->context = nil;
CGLUnlockContext(parent_obj);
CGLUnlockContext(parent_obj);
}
struct gl_windowinfo *gl_windowinfo_create(const struct gs_init_data *info)
{
if (!info)
return NULL;
if (!info)
return NULL;
if (!info->window.view)
return NULL;
if (!info->window.view)
return NULL;
struct gl_windowinfo *wi = bzalloc(sizeof(struct gl_windowinfo));
struct gl_windowinfo *wi = bzalloc(sizeof(struct gl_windowinfo));
wi->view = info->window.view;
wi->view.window.colorSpace = NSColorSpace.sRGBColorSpace;
wi->view = info->window.view;
wi->view.window.colorSpace = NSColorSpace.sRGBColorSpace;
#pragma clang diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
wi->view.wantsBestResolutionOpenGLSurface = YES;
wi->view.wantsBestResolutionOpenGLSurface = YES;
#pragma clang diagnostic pop
return wi;
return wi;
}
void gl_windowinfo_destroy(struct gl_windowinfo *wi)
{
if (!wi)
return;
if (!wi)
return;
wi->view = nil;
bfree(wi);
wi->view = nil;
bfree(wi);
}
void gl_update(gs_device_t *device)
{
gs_swapchain_t *swap = device->cur_swap;
NSOpenGLContext *parent = device->plat->context;
NSOpenGLContext *context = swap->wi->context;
dispatch_async(dispatch_get_main_queue(), ^() {
if (!swap || !swap->wi) {
return;
}
gs_swapchain_t *swap = device->cur_swap;
NSOpenGLContext *parent = device->plat->context;
NSOpenGLContext *context = swap->wi->context;
dispatch_async(dispatch_get_main_queue(), ^() {
if (!swap || !swap->wi) {
return;
}
CGLContextObj parent_obj = [parent CGLContextObj];
CGLLockContext(parent_obj);
CGLContextObj parent_obj = [parent CGLContextObj];
CGLLockContext(parent_obj);
CGLContextObj context_obj = [context CGLContextObj];
CGLLockContext(context_obj);
CGLContextObj context_obj = [context CGLContextObj];
CGLLockContext(context_obj);
[context makeCurrentContext];
[context update];
struct gs_init_data *info = &swap->info;
gs_texture_t *previous = swap->wi->texture;
swap->wi->texture = device_texture_create(device, info->cx,
info->cy,
info->format, 1, NULL,
GS_RENDER_TARGET);
gl_bind_framebuffer(GL_FRAMEBUFFER, swap->wi->fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
swap->wi->texture->texture, 0);
gl_success("glFrameBufferTexture2D");
gs_texture_destroy(previous);
glFlush();
[NSOpenGLContext clearCurrentContext];
[context makeCurrentContext];
[context update];
struct gs_init_data *info = &swap->info;
gs_texture_t *previous = swap->wi->texture;
swap->wi->texture = device_texture_create(device, info->cx, info->cy, info->format, 1, NULL, GS_RENDER_TARGET);
gl_bind_framebuffer(GL_FRAMEBUFFER, swap->wi->fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, swap->wi->texture->texture, 0);
gl_success("glFrameBufferTexture2D");
gs_texture_destroy(previous);
glFlush();
[NSOpenGLContext clearCurrentContext];
CGLUnlockContext(context_obj);
CGLUnlockContext(context_obj);
CGLUnlockContext(parent_obj);
});
CGLUnlockContext(parent_obj);
});
}
void gl_clear_context(gs_device_t *device)
{
UNUSED_PARAMETER(device);
[NSOpenGLContext clearCurrentContext];
UNUSED_PARAMETER(device);
[NSOpenGLContext clearCurrentContext];
}
void device_enter_context(gs_device_t *device)
{
CGLLockContext([device->plat->context CGLContextObj]);
CGLLockContext([device->plat->context CGLContextObj]);
[device->plat->context makeCurrentContext];
[device->plat->context makeCurrentContext];
}
void device_leave_context(gs_device_t *device)
{
glFlush();
[NSOpenGLContext clearCurrentContext];
device->cur_vertex_buffer = NULL;
device->cur_index_buffer = NULL;
device->cur_render_target = NULL;
device->cur_zstencil_buffer = NULL;
device->cur_swap = NULL;
device->cur_fbo = NULL;
glFlush();
[NSOpenGLContext clearCurrentContext];
device->cur_vertex_buffer = NULL;
device->cur_index_buffer = NULL;
device->cur_render_target = NULL;
device->cur_zstencil_buffer = NULL;
device->cur_swap = NULL;
device->cur_fbo = NULL;
CGLUnlockContext([device->plat->context CGLContextObj]);
CGLUnlockContext([device->plat->context CGLContextObj]);
}
void *device_get_device_obj(gs_device_t *device)
{
return device->plat->context;
return device->plat->context;
}
void device_load_swapchain(gs_device_t *device, gs_swapchain_t *swap)
{
if (device->cur_swap == swap)
return;
if (device->cur_swap == swap)
return;
device->cur_swap = swap;
if (swap) {
device_set_render_target(device, swap->wi->texture, NULL);
}
device->cur_swap = swap;
if (swap) {
device_set_render_target(device, swap->wi->texture, NULL);
}
}
bool device_is_present_ready(gs_device_t *device __unused)
{
return true;
return true;
}
void device_present(gs_device_t *device)
{
glFlush();
[NSOpenGLContext clearCurrentContext];
glFlush();
[NSOpenGLContext clearCurrentContext];
CGLLockContext([device->cur_swap->wi->context CGLContextObj]);
CGLLockContext([device->cur_swap->wi->context CGLContextObj]);
[device->cur_swap->wi->context makeCurrentContext];
gl_bind_framebuffer(GL_READ_FRAMEBUFFER, device->cur_swap->wi->fbo);
gl_bind_framebuffer(GL_DRAW_FRAMEBUFFER, 0);
const uint32_t width = device->cur_swap->info.cx;
const uint32_t height = device->cur_swap->info.cy;
glBlitFramebuffer(0, 0, width, height, 0, height, width, 0,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
[device->cur_swap->wi->context flushBuffer];
glFlush();
[NSOpenGLContext clearCurrentContext];
[device->cur_swap->wi->context makeCurrentContext];
gl_bind_framebuffer(GL_READ_FRAMEBUFFER, device->cur_swap->wi->fbo);
gl_bind_framebuffer(GL_DRAW_FRAMEBUFFER, 0);
const uint32_t width = device->cur_swap->info.cx;
const uint32_t height = device->cur_swap->info.cy;
glBlitFramebuffer(0, 0, width, height, 0, height, width, 0, GL_COLOR_BUFFER_BIT, GL_NEAREST);
[device->cur_swap->wi->context flushBuffer];
glFlush();
[NSOpenGLContext clearCurrentContext];
CGLUnlockContext([device->cur_swap->wi->context CGLContextObj]);
CGLUnlockContext([device->cur_swap->wi->context CGLContextObj]);
[device->plat->context makeCurrentContext];
[device->plat->context makeCurrentContext];
}
bool device_is_monitor_hdr(gs_device_t *device __unused, void *monitor __unused)
{
return false;
return false;
}
void gl_getclientsize(const struct gs_swap_chain *swap, uint32_t *width,
uint32_t *height)
void gl_getclientsize(const struct gs_swap_chain *swap, uint32_t *width, uint32_t *height)
{
if (width)
*width = swap->info.cx;
if (height)
*height = swap->info.cy;
if (width)
*width = swap->info.cx;
if (height)
*height = swap->info.cy;
}
gs_texture_t *device_texture_create_from_iosurface(gs_device_t *device,
void *iosurf)
gs_texture_t *device_texture_create_from_iosurface(gs_device_t *device, void *iosurf)
{
IOSurfaceRef ref = (IOSurfaceRef)iosurf;
struct gs_texture_2d *tex = bzalloc(sizeof(struct gs_texture_2d));
IOSurfaceRef ref = (IOSurfaceRef) iosurf;
struct gs_texture_2d *tex = bzalloc(sizeof(struct gs_texture_2d));
OSType pf = IOSurfaceGetPixelFormat(ref);
OSType pf = IOSurfaceGetPixelFormat(ref);
FourCharCode l10r_code = 0;
l10r_code = ('l' << 24) | ('1' << 16) | ('0' << 8) | 'r';
FourCharCode l10r_code = 0;
l10r_code = ('l' << 24) | ('1' << 16) | ('0' << 8) | 'r';
FourCharCode bgra_code = 0;
bgra_code = ('B' << 24) | ('G' << 16) | ('R' << 8) | 'A';
FourCharCode bgra_code = 0;
bgra_code = ('B' << 24) | ('G' << 16) | ('R' << 8) | 'A';
const bool l10r = pf == l10r_code;
if (pf == 0)
blog(LOG_ERROR, "Invalid IOSurface Buffer");
else if ((pf != bgra_code) && !l10r)
blog(LOG_ERROR, "Unexpected pixel format: %d (%c%c%c%c)", pf,
pf >> 24, pf >> 16, pf >> 8, pf);
const bool l10r = pf == l10r_code;
if (pf == 0)
blog(LOG_ERROR, "Invalid IOSurface Buffer");
else if ((pf != bgra_code) && !l10r)
blog(LOG_ERROR, "Unexpected pixel format: %d (%c%c%c%c)", pf, pf >> 24, pf >> 16, pf >> 8, pf);
const enum gs_color_format color_format = l10r ? GS_R10G10B10A2
: GS_BGRA;
const enum gs_color_format color_format = l10r ? GS_R10G10B10A2 : GS_BGRA;
tex->base.device = device;
tex->base.type = GS_TEXTURE_2D;
tex->base.format = color_format;
tex->base.levels = 1;
tex->base.gl_format = l10r ? GL_BGRA : convert_gs_format(color_format);
tex->base.gl_internal_format = convert_gs_internal_format(color_format);
tex->base.gl_type = l10r ? GL_UNSIGNED_INT_2_10_10_10_REV
: GL_UNSIGNED_INT_8_8_8_8_REV;
tex->base.gl_target = GL_TEXTURE_RECTANGLE_ARB;
tex->base.is_dynamic = false;
tex->base.is_render_target = false;
tex->base.gen_mipmaps = false;
tex->width = (uint32_t)IOSurfaceGetWidth(ref);
tex->height = (uint32_t)IOSurfaceGetHeight(ref);
tex->base.device = device;
tex->base.type = GS_TEXTURE_2D;
tex->base.format = color_format;
tex->base.levels = 1;
tex->base.gl_format = l10r ? GL_BGRA : convert_gs_format(color_format);
tex->base.gl_internal_format = convert_gs_internal_format(color_format);
tex->base.gl_type = l10r ? GL_UNSIGNED_INT_2_10_10_10_REV : GL_UNSIGNED_INT_8_8_8_8_REV;
tex->base.gl_target = GL_TEXTURE_RECTANGLE_ARB;
tex->base.is_dynamic = false;
tex->base.is_render_target = false;
tex->base.gen_mipmaps = false;
tex->width = (uint32_t) IOSurfaceGetWidth(ref);
tex->height = (uint32_t) IOSurfaceGetHeight(ref);
if (!gl_gen_textures(1, &tex->base.texture))
goto fail;
if (!gl_gen_textures(1, &tex->base.texture))
goto fail;
if (!gl_bind_texture(tex->base.gl_target, tex->base.texture))
goto fail;
if (!gl_bind_texture(tex->base.gl_target, tex->base.texture))
goto fail;
CGLError err = CGLTexImageIOSurface2D(
[[NSOpenGLContext currentContext] CGLContextObj],
tex->base.gl_target, tex->base.gl_internal_format, tex->width,
tex->height, tex->base.gl_format, tex->base.gl_type, ref, 0);
CGLError err = CGLTexImageIOSurface2D([[NSOpenGLContext currentContext] CGLContextObj], tex->base.gl_target,
tex->base.gl_internal_format, tex->width, tex->height, tex->base.gl_format,
tex->base.gl_type, ref, 0);
if (err != kCGLNoError) {
blog(LOG_ERROR,
"CGLTexImageIOSurface2D: %u, %s"
" (device_texture_create_from_iosurface)",
err, CGLErrorString(err));
if (err != kCGLNoError) {
blog(LOG_ERROR,
"CGLTexImageIOSurface2D: %u, %s"
" (device_texture_create_from_iosurface)",
err, CGLErrorString(err));
gl_success("CGLTexImageIOSurface2D");
goto fail;
}
gl_success("CGLTexImageIOSurface2D");
goto fail;
}
if (!gl_tex_param_i(tex->base.gl_target, GL_TEXTURE_MAX_LEVEL, 0))
goto fail;
if (!gl_tex_param_i(tex->base.gl_target, GL_TEXTURE_MAX_LEVEL, 0))
goto fail;
if (!gl_bind_texture(tex->base.gl_target, 0))
goto fail;
if (!gl_bind_texture(tex->base.gl_target, 0))
goto fail;
return (gs_texture_t *)tex;
return (gs_texture_t *) tex;
fail:
gs_texture_destroy((gs_texture_t *)tex);
blog(LOG_ERROR, "device_texture_create_from_iosurface (GL) failed");
return NULL;
gs_texture_destroy((gs_texture_t *) tex);
blog(LOG_ERROR, "device_texture_create_from_iosurface (GL) failed");
return NULL;
}
gs_texture_t *device_texture_open_shared(gs_device_t *device, uint32_t handle)
{
gs_texture_t *texture = NULL;
IOSurfaceRef ref = IOSurfaceLookupFromMachPort((mach_port_t)handle);
texture = device_texture_create_from_iosurface(device, ref);
CFRelease(ref);
return texture;
gs_texture_t *texture = NULL;
IOSurfaceRef ref = IOSurfaceLookupFromMachPort((mach_port_t) handle);
texture = device_texture_create_from_iosurface(device, ref);
CFRelease(ref);
return texture;
}
bool device_shared_texture_available(void)
{
return true;
return true;
}
bool gs_texture_rebind_iosurface(gs_texture_t *texture, void *iosurf)
{
if (!texture)
return false;
if (!texture)
return false;
if (!iosurf)
return false;
if (!iosurf)
return false;
FourCharCode l10r_code = 0;
l10r_code = ('l' << 24) | ('1' << 16) | ('0' << 8) | 'r';
FourCharCode l10r_code = 0;
l10r_code = ('l' << 24) | ('1' << 16) | ('0' << 8) | 'r';
FourCharCode bgra_code = 0;
bgra_code = ('B' << 24) | ('G' << 16) | ('R' << 8) | 'A';
FourCharCode bgra_code = 0;
bgra_code = ('B' << 24) | ('G' << 16) | ('R' << 8) | 'A';
struct gs_texture_2d *tex = (struct gs_texture_2d *)texture;
IOSurfaceRef ref = (IOSurfaceRef)iosurf;
struct gs_texture_2d *tex = (struct gs_texture_2d *) texture;
IOSurfaceRef ref = (IOSurfaceRef) iosurf;
OSType pf = IOSurfaceGetPixelFormat(ref);
if (pf == 0) {
blog(LOG_ERROR, "Invalid IOSurface buffer");
} else if ((pf != bgra_code) && (pf != l10r_code)) {
blog(LOG_ERROR, "Unexpected pixel format: %d (%c%c%c%c)", pf,
pf >> 24, pf >> 16, pf >> 8, pf);
}
OSType pf = IOSurfaceGetPixelFormat(ref);
if (pf == 0) {
blog(LOG_ERROR, "Invalid IOSurface buffer");
} else if ((pf != bgra_code) && (pf != l10r_code)) {
blog(LOG_ERROR, "Unexpected pixel format: %d (%c%c%c%c)", pf, pf >> 24, pf >> 16, pf >> 8, pf);
}
tex->width = (uint32_t)IOSurfaceGetWidth(ref);
tex->height = (uint32_t)IOSurfaceGetHeight(ref);
tex->width = (uint32_t) IOSurfaceGetWidth(ref);
tex->height = (uint32_t) IOSurfaceGetHeight(ref);
if (!gl_bind_texture(tex->base.gl_target, tex->base.texture))
return false;
if (!gl_bind_texture(tex->base.gl_target, tex->base.texture))
return false;
CGLError err = CGLTexImageIOSurface2D(
[[NSOpenGLContext currentContext] CGLContextObj],
tex->base.gl_target, tex->base.gl_internal_format, tex->width,
tex->height, tex->base.gl_format, tex->base.gl_type, ref, 0);
CGLError err = CGLTexImageIOSurface2D([[NSOpenGLContext currentContext] CGLContextObj], tex->base.gl_target,
tex->base.gl_internal_format, tex->width, tex->height, tex->base.gl_format,
tex->base.gl_type, ref, 0);
if (err != kCGLNoError) {
blog(LOG_ERROR,
"CGLTexImageIOSurface2D: %u, %s"
" (gs_texture_rebind_iosurface)",
err, CGLErrorString(err));
if (err != kCGLNoError) {
blog(LOG_ERROR,
"CGLTexImageIOSurface2D: %u, %s"
" (gs_texture_rebind_iosurface)",
err, CGLErrorString(err));
gl_success("CGLTexImageIOSurface2D");
return false;
}
gl_success("CGLTexImageIOSurface2D");
return false;
}
if (!gl_bind_texture(tex->base.gl_target, 0))
return false;
if (!gl_bind_texture(tex->base.gl_target, 0))
return false;
return true;
return true;
}

File diff suppressed because it is too large Load diff

View file

@ -40,279 +40,264 @@
uint64_t os_gettime_ns(void)
{
return clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
return clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
}
/* gets the location [domain mask]/Library/Application Support/[name] */
static int os_get_path_internal(char *dst, size_t size, const char *name,
NSSearchPathDomainMask domainMask)
static int os_get_path_internal(char *dst, size_t size, const char *name, NSSearchPathDomainMask domainMask)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(
NSApplicationSupportDirectory, domainMask, YES);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, domainMask, YES);
if ([paths count] == 0)
bcrash("Could not get home directory (platform-cocoa)");
if ([paths count] == 0)
bcrash("Could not get home directory (platform-cocoa)");
NSString *application_support = paths[0];
const char *base_path = [application_support UTF8String];
NSString *application_support = paths[0];
const char *base_path = [application_support UTF8String];
if (!name || !*name)
return snprintf(dst, size, "%s", base_path);
else
return snprintf(dst, size, "%s/%s", base_path, name);
if (!name || !*name)
return snprintf(dst, size, "%s", base_path);
else
return snprintf(dst, size, "%s/%s", base_path, name);
}
static char *os_get_path_ptr_internal(const char *name,
NSSearchPathDomainMask domainMask)
static char *os_get_path_ptr_internal(const char *name, NSSearchPathDomainMask domainMask)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(
NSApplicationSupportDirectory, domainMask, YES);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, domainMask, YES);
if ([paths count] == 0)
bcrash("Could not get home directory (platform-cocoa)");
if ([paths count] == 0)
bcrash("Could not get home directory (platform-cocoa)");
NSString *application_support = paths[0];
NSString *application_support = paths[0];
NSUInteger len = [application_support
lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
NSUInteger len = [application_support lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
char *path_ptr = bmalloc(len + 1);
char *path_ptr = bmalloc(len + 1);
path_ptr[len] = 0;
path_ptr[len] = 0;
memcpy(path_ptr, [application_support UTF8String], len);
memcpy(path_ptr, [application_support UTF8String], len);
struct dstr path;
dstr_init_move_array(&path, path_ptr);
dstr_cat(&path, "/");
dstr_cat(&path, name);
return path.array;
struct dstr path;
dstr_init_move_array(&path, path_ptr);
dstr_cat(&path, "/");
dstr_cat(&path, name);
return path.array;
}
int os_get_config_path(char *dst, size_t size, const char *name)
{
return os_get_path_internal(dst, size, name, NSUserDomainMask);
return os_get_path_internal(dst, size, name, NSUserDomainMask);
}
char *os_get_config_path_ptr(const char *name)
{
return os_get_path_ptr_internal(name, NSUserDomainMask);
return os_get_path_ptr_internal(name, NSUserDomainMask);
}
int os_get_program_data_path(char *dst, size_t size, const char *name)
{
return os_get_path_internal(dst, size, name, NSLocalDomainMask);
return os_get_path_internal(dst, size, name, NSLocalDomainMask);
}
char *os_get_program_data_path_ptr(const char *name)
{
return os_get_path_ptr_internal(name, NSLocalDomainMask);
return os_get_path_ptr_internal(name, NSLocalDomainMask);
}
char *os_get_executable_path_ptr(const char *name)
{
char exe[PATH_MAX];
char abs_path[PATH_MAX];
uint32_t size = sizeof(exe);
struct dstr path;
char *slash;
char exe[PATH_MAX];
char abs_path[PATH_MAX];
uint32_t size = sizeof(exe);
struct dstr path;
char *slash;
if (_NSGetExecutablePath(exe, &size) != 0) {
return NULL;
}
if (_NSGetExecutablePath(exe, &size) != 0) {
return NULL;
}
if (!realpath(exe, abs_path)) {
return NULL;
}
if (!realpath(exe, abs_path)) {
return NULL;
}
dstr_init_copy(&path, abs_path);
slash = strrchr(path.array, '/');
if (slash) {
size_t len = slash - path.array + 1;
dstr_resize(&path, len);
}
dstr_init_copy(&path, abs_path);
slash = strrchr(path.array, '/');
if (slash) {
size_t len = slash - path.array + 1;
dstr_resize(&path, len);
}
if (name && *name) {
dstr_cat(&path, name);
}
return path.array;
if (name && *name) {
dstr_cat(&path, name);
}
return path.array;
}
struct os_cpu_usage_info {
int64_t last_cpu_time;
int64_t last_sys_time;
int core_count;
int64_t last_cpu_time;
int64_t last_sys_time;
int core_count;
};
static inline void add_time_value(time_value_t *dst, time_value_t *a,
time_value_t *b)
static inline void add_time_value(time_value_t *dst, time_value_t *a, time_value_t *b)
{
dst->microseconds = a->microseconds + b->microseconds;
dst->seconds = a->seconds + b->seconds;
dst->microseconds = a->microseconds + b->microseconds;
dst->seconds = a->seconds + b->seconds;
if (dst->microseconds >= 1000000) {
dst->seconds += dst->microseconds / 1000000;
dst->microseconds %= 1000000;
}
if (dst->microseconds >= 1000000) {
dst->seconds += dst->microseconds / 1000000;
dst->microseconds %= 1000000;
}
}
static bool get_time_info(int64_t *cpu_time, int64_t *sys_time)
{
mach_port_t task = mach_task_self();
struct task_thread_times_info thread_data;
struct task_basic_info_64 task_data;
mach_msg_type_number_t count;
kern_return_t kern_ret;
time_value_t cur_time;
mach_port_t task = mach_task_self();
struct task_thread_times_info thread_data;
struct task_basic_info_64 task_data;
mach_msg_type_number_t count;
kern_return_t kern_ret;
time_value_t cur_time;
*cpu_time = 0;
*sys_time = 0;
*cpu_time = 0;
*sys_time = 0;
count = TASK_THREAD_TIMES_INFO_COUNT;
kern_ret = task_info(task, TASK_THREAD_TIMES_INFO,
(task_info_t)&thread_data, &count);
if (kern_ret != KERN_SUCCESS)
return false;
count = TASK_THREAD_TIMES_INFO_COUNT;
kern_ret = task_info(task, TASK_THREAD_TIMES_INFO, (task_info_t) &thread_data, &count);
if (kern_ret != KERN_SUCCESS)
return false;
count = TASK_BASIC_INFO_64_COUNT;
kern_ret = task_info(task, TASK_BASIC_INFO_64, (task_info_t)&task_data,
&count);
if (kern_ret != KERN_SUCCESS)
return false;
count = TASK_BASIC_INFO_64_COUNT;
kern_ret = task_info(task, TASK_BASIC_INFO_64, (task_info_t) &task_data, &count);
if (kern_ret != KERN_SUCCESS)
return false;
add_time_value(&cur_time, &thread_data.user_time,
&thread_data.system_time);
add_time_value(&cur_time, &cur_time, &task_data.user_time);
add_time_value(&cur_time, &cur_time, &task_data.system_time);
add_time_value(&cur_time, &thread_data.user_time, &thread_data.system_time);
add_time_value(&cur_time, &cur_time, &task_data.user_time);
add_time_value(&cur_time, &cur_time, &task_data.system_time);
*cpu_time = os_gettime_ns() / 1000;
*sys_time = cur_time.seconds * 1000000 + cur_time.microseconds;
return true;
*cpu_time = os_gettime_ns() / 1000;
*sys_time = cur_time.seconds * 1000000 + cur_time.microseconds;
return true;
}
os_cpu_usage_info_t *os_cpu_usage_info_start(void)
{
struct os_cpu_usage_info *info = bmalloc(sizeof(*info));
struct os_cpu_usage_info *info = bmalloc(sizeof(*info));
if (!get_time_info(&info->last_cpu_time, &info->last_sys_time)) {
bfree(info);
return NULL;
}
if (!get_time_info(&info->last_cpu_time, &info->last_sys_time)) {
bfree(info);
return NULL;
}
info->core_count = sysconf(_SC_NPROCESSORS_ONLN);
return info;
info->core_count = sysconf(_SC_NPROCESSORS_ONLN);
return info;
}
double os_cpu_usage_info_query(os_cpu_usage_info_t *info)
{
int64_t sys_time, cpu_time;
int64_t sys_time_delta, cpu_time_delta;
int64_t sys_time, cpu_time;
int64_t sys_time_delta, cpu_time_delta;
if (!info || !get_time_info(&cpu_time, &sys_time))
return 0.0;
if (!info || !get_time_info(&cpu_time, &sys_time))
return 0.0;
sys_time_delta = sys_time - info->last_sys_time;
cpu_time_delta = cpu_time - info->last_cpu_time;
sys_time_delta = sys_time - info->last_sys_time;
cpu_time_delta = cpu_time - info->last_cpu_time;
if (cpu_time_delta == 0)
return 0.0;
if (cpu_time_delta == 0)
return 0.0;
info->last_sys_time = sys_time;
info->last_cpu_time = cpu_time;
info->last_sys_time = sys_time;
info->last_cpu_time = cpu_time;
return (double)sys_time_delta * 100.0 / (double)cpu_time_delta /
(double)info->core_count;
return (double) sys_time_delta * 100.0 / (double) cpu_time_delta / (double) info->core_count;
}
void os_cpu_usage_info_destroy(os_cpu_usage_info_t *info)
{
if (info)
bfree(info);
if (info)
bfree(info);
}
os_performance_token_t *os_request_high_performance(const char *reason)
{
@autoreleasepool {
NSProcessInfo *pi = [NSProcessInfo processInfo];
SEL sel = @selector(beginActivityWithOptions:reason:);
if (![pi respondsToSelector:sel])
return nil;
@autoreleasepool {
NSProcessInfo *pi = [NSProcessInfo processInfo];
SEL sel = @selector(beginActivityWithOptions:reason:);
if (![pi respondsToSelector:sel])
return nil;
//taken from http://stackoverflow.com/a/20100906
id activity = [pi beginActivityWithOptions:0x00FFFFFF
reason:@(reason)];
//taken from http://stackoverflow.com/a/20100906
id activity = [pi beginActivityWithOptions:0x00FFFFFF reason:@(reason)];
return CFBridgingRetain(activity);
}
return CFBridgingRetain(activity);
}
}
void os_end_high_performance(os_performance_token_t *token)
{
@autoreleasepool {
NSProcessInfo *pi = [NSProcessInfo processInfo];
SEL sel = @selector(beginActivityWithOptions:reason:);
if (![pi respondsToSelector:sel])
return;
@autoreleasepool {
NSProcessInfo *pi = [NSProcessInfo processInfo];
SEL sel = @selector(beginActivityWithOptions:reason:);
if (![pi respondsToSelector:sel])
return;
[pi endActivity:CFBridgingRelease(token)];
}
[pi endActivity:CFBridgingRelease(token)];
}
}
struct os_inhibit_info {
CFStringRef reason;
IOPMAssertionID sleep_id;
IOPMAssertionID user_id;
bool active;
CFStringRef reason;
IOPMAssertionID sleep_id;
IOPMAssertionID user_id;
bool active;
};
os_inhibit_t *os_inhibit_sleep_create(const char *reason)
{
struct os_inhibit_info *info = bzalloc(sizeof(*info));
if (!reason)
info->reason = CFStringCreateWithCString(
kCFAllocatorDefault, reason, kCFStringEncodingUTF8);
else
info->reason =
CFStringCreateCopy(kCFAllocatorDefault, CFSTR(""));
struct os_inhibit_info *info = bzalloc(sizeof(*info));
if (!reason)
info->reason = CFStringCreateWithCString(kCFAllocatorDefault, reason, kCFStringEncodingUTF8);
else
info->reason = CFStringCreateCopy(kCFAllocatorDefault, CFSTR(""));
return info;
return info;
}
bool os_inhibit_sleep_set_active(os_inhibit_t *info, bool active)
{
IOReturn success;
IOReturn success;
if (!info)
return false;
if (info->active == active)
return false;
if (!info)
return false;
if (info->active == active)
return false;
if (active) {
IOPMAssertionDeclareUserActivity(
info->reason, kIOPMUserActiveLocal, &info->user_id);
success = IOPMAssertionCreateWithName(
kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn,
info->reason, &info->sleep_id);
if (active) {
IOPMAssertionDeclareUserActivity(info->reason, kIOPMUserActiveLocal, &info->user_id);
success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, info->reason,
&info->sleep_id);
if (success != kIOReturnSuccess) {
blog(LOG_WARNING, "Failed to disable sleep");
return false;
}
} else {
IOPMAssertionRelease(info->sleep_id);
}
if (success != kIOReturnSuccess) {
blog(LOG_WARNING, "Failed to disable sleep");
return false;
}
} else {
IOPMAssertionRelease(info->sleep_id);
}
info->active = active;
return true;
info->active = active;
return true;
}
void os_inhibit_sleep_destroy(os_inhibit_t *info)
{
if (info) {
os_inhibit_sleep_set_active(info, false);
CFRelease(info->reason);
bfree(info);
}
if (info) {
os_inhibit_sleep_set_active(info, false);
CFRelease(info->reason);
bfree(info);
}
}
static int physical_cores = 0;
@ -322,68 +307,64 @@ static bool core_count_initialized = false;
bool os_get_emulation_status(void)
{
#ifdef __aarch64__
return false;
return false;
#else
int rosettaTranslated = 0;
size_t size = sizeof(rosettaTranslated);
if (sysctlbyname("sysctl.proc_translated", &rosettaTranslated, &size,
NULL, 0) == -1)
return false;
int rosettaTranslated = 0;
size_t size = sizeof(rosettaTranslated);
if (sysctlbyname("sysctl.proc_translated", &rosettaTranslated, &size, NULL, 0) == -1)
return false;
return rosettaTranslated == 1;
return rosettaTranslated == 1;
#endif
}
static void os_get_cores_internal(void)
{
if (core_count_initialized)
return;
if (core_count_initialized)
return;
core_count_initialized = true;
core_count_initialized = true;
size_t size;
int ret;
size_t size;
int ret;
size = sizeof(physical_cores);
ret = sysctlbyname("machdep.cpu.core_count", &physical_cores, &size,
NULL, 0);
if (ret != 0)
return;
size = sizeof(physical_cores);
ret = sysctlbyname("machdep.cpu.core_count", &physical_cores, &size, NULL, 0);
if (ret != 0)
return;
ret = sysctlbyname("machdep.cpu.thread_count", &logical_cores, &size,
NULL, 0);
ret = sysctlbyname("machdep.cpu.thread_count", &logical_cores, &size, NULL, 0);
}
int os_get_physical_cores(void)
{
if (!core_count_initialized)
os_get_cores_internal();
return physical_cores;
if (!core_count_initialized)
os_get_cores_internal();
return physical_cores;
}
int os_get_logical_cores(void)
{
if (!core_count_initialized)
os_get_cores_internal();
return logical_cores;
if (!core_count_initialized)
os_get_cores_internal();
return logical_cores;
}
static inline bool os_get_sys_memory_usage_internal(vm_statistics_t vmstat)
{
mach_msg_type_number_t out_count = HOST_VM_INFO_COUNT;
if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)vmstat,
&out_count) != KERN_SUCCESS)
return false;
return true;
mach_msg_type_number_t out_count = HOST_VM_INFO_COUNT;
if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t) vmstat, &out_count) != KERN_SUCCESS)
return false;
return true;
}
uint64_t os_get_sys_free_size(void)
{
vm_statistics_data_t vmstat = {};
if (!os_get_sys_memory_usage_internal(&vmstat))
return 0;
vm_statistics_data_t vmstat = {};
if (!os_get_sys_memory_usage_internal(&vmstat))
return 0;
return vmstat.free_count * vm_page_size;
return vmstat.free_count * vm_page_size;
}
static uint64_t total_memory = 0;
@ -391,60 +372,58 @@ static bool total_memory_initialized = false;
static void os_get_sys_total_size_internal()
{
total_memory_initialized = true;
total_memory_initialized = true;
size_t size;
int ret;
size_t size;
int ret;
size = sizeof(total_memory);
ret = sysctlbyname("hw.memsize", &total_memory, &size, NULL, 0);
size = sizeof(total_memory);
ret = sysctlbyname("hw.memsize", &total_memory, &size, NULL, 0);
}
uint64_t os_get_sys_total_size(void)
{
if (!total_memory_initialized)
os_get_sys_total_size_internal();
if (!total_memory_initialized)
os_get_sys_total_size_internal();
return total_memory;
return total_memory;
}
static inline bool
os_get_proc_memory_usage_internal(mach_task_basic_info_data_t *taskinfo)
static inline bool os_get_proc_memory_usage_internal(mach_task_basic_info_data_t *taskinfo)
{
const task_flavor_t flavor = MACH_TASK_BASIC_INFO;
mach_msg_type_number_t out_count = MACH_TASK_BASIC_INFO_COUNT;
const task_flavor_t flavor = MACH_TASK_BASIC_INFO;
mach_msg_type_number_t out_count = MACH_TASK_BASIC_INFO_COUNT;
if (task_info(mach_task_self(), flavor, (task_info_t)taskinfo,
&out_count) != KERN_SUCCESS)
return false;
return true;
if (task_info(mach_task_self(), flavor, (task_info_t) taskinfo, &out_count) != KERN_SUCCESS)
return false;
return true;
}
bool os_get_proc_memory_usage(os_proc_memory_usage_t *usage)
{
mach_task_basic_info_data_t taskinfo = {};
if (!os_get_proc_memory_usage_internal(&taskinfo))
return false;
mach_task_basic_info_data_t taskinfo = {};
if (!os_get_proc_memory_usage_internal(&taskinfo))
return false;
usage->resident_size = taskinfo.resident_size;
usage->virtual_size = taskinfo.virtual_size;
return true;
usage->resident_size = taskinfo.resident_size;
usage->virtual_size = taskinfo.virtual_size;
return true;
}
uint64_t os_get_proc_resident_size(void)
{
mach_task_basic_info_data_t taskinfo = {};
if (!os_get_proc_memory_usage_internal(&taskinfo))
return 0;
return taskinfo.resident_size;
mach_task_basic_info_data_t taskinfo = {};
if (!os_get_proc_memory_usage_internal(&taskinfo))
return 0;
return taskinfo.resident_size;
}
uint64_t os_get_proc_virtual_size(void)
{
mach_task_basic_info_data_t taskinfo = {};
if (!os_get_proc_memory_usage_internal(&taskinfo))
return 0;
return taskinfo.virtual_size;
mach_task_basic_info_data_t taskinfo = {};
if (!os_get_proc_memory_usage_internal(&taskinfo))
return 0;
return taskinfo.virtual_size;
}
/* Obtains a copy of the contents of a CFString in specified encoding.
@ -452,84 +431,77 @@ uint64_t os_get_proc_virtual_size(void)
*/
char *cfstr_copy_cstr(CFStringRef cfstring, CFStringEncoding cfstring_encoding)
{
if (!cfstring)
return NULL;
if (!cfstring)
return NULL;
// Try the quick way to obtain the buffer
const char *tmp_buffer =
CFStringGetCStringPtr(cfstring, cfstring_encoding);
// Try the quick way to obtain the buffer
const char *tmp_buffer = CFStringGetCStringPtr(cfstring, cfstring_encoding);
if (tmp_buffer != NULL)
return bstrdup(tmp_buffer);
if (tmp_buffer != NULL)
return bstrdup(tmp_buffer);
// The quick way did not work, try the more expensive one
CFIndex length = CFStringGetLength(cfstring);
CFIndex max_size =
CFStringGetMaximumSizeForEncoding(length, cfstring_encoding);
// The quick way did not work, try the more expensive one
CFIndex length = CFStringGetLength(cfstring);
CFIndex max_size = CFStringGetMaximumSizeForEncoding(length, cfstring_encoding);
// If result would exceed LONG_MAX, kCFNotFound is returned
if (max_size == kCFNotFound)
return NULL;
// If result would exceed LONG_MAX, kCFNotFound is returned
if (max_size == kCFNotFound)
return NULL;
// Account for the null terminator
max_size++;
// Account for the null terminator
max_size++;
char *buffer = bmalloc(max_size);
char *buffer = bmalloc(max_size);
if (buffer == NULL) {
return NULL;
}
if (buffer == NULL) {
return NULL;
}
// Copy CFString in requested encoding to buffer
Boolean success = CFStringGetCString(cfstring, buffer, max_size,
cfstring_encoding);
// Copy CFString in requested encoding to buffer
Boolean success = CFStringGetCString(cfstring, buffer, max_size, cfstring_encoding);
if (!success) {
bfree(buffer);
buffer = NULL;
}
return buffer;
if (!success) {
bfree(buffer);
buffer = NULL;
}
return buffer;
}
/* Copies the contents of a CFString in specified encoding to a given dstr.
* Returns true on success or false on failure.
* In case of failure, the dstr capacity but not size is changed.
*/
bool cfstr_copy_dstr(CFStringRef cfstring, CFStringEncoding cfstring_encoding,
struct dstr *str)
bool cfstr_copy_dstr(CFStringRef cfstring, CFStringEncoding cfstring_encoding, struct dstr *str)
{
if (!cfstring)
return false;
if (!cfstring)
return false;
// Try the quick way to obtain the buffer
const char *tmp_buffer =
CFStringGetCStringPtr(cfstring, cfstring_encoding);
// Try the quick way to obtain the buffer
const char *tmp_buffer = CFStringGetCStringPtr(cfstring, cfstring_encoding);
if (tmp_buffer != NULL) {
dstr_copy(str, tmp_buffer);
return true;
}
if (tmp_buffer != NULL) {
dstr_copy(str, tmp_buffer);
return true;
}
// The quick way did not work, try the more expensive one
CFIndex length = CFStringGetLength(cfstring);
CFIndex max_size =
CFStringGetMaximumSizeForEncoding(length, cfstring_encoding);
// The quick way did not work, try the more expensive one
CFIndex length = CFStringGetLength(cfstring);
CFIndex max_size = CFStringGetMaximumSizeForEncoding(length, cfstring_encoding);
// If result would exceed LONG_MAX, kCFNotFound is returned
if (max_size == kCFNotFound)
return NULL;
// If result would exceed LONG_MAX, kCFNotFound is returned
if (max_size == kCFNotFound)
return NULL;
// Account for the null terminator
max_size++;
// Account for the null terminator
max_size++;
dstr_ensure_capacity(str, max_size);
dstr_ensure_capacity(str, max_size);
// Copy CFString in requested encoding to dstr buffer
Boolean success = CFStringGetCString(cfstring, str->array, max_size,
cfstring_encoding);
// Copy CFString in requested encoding to dstr buffer
Boolean success = CFStringGetCString(cfstring, str->array, max_size, cfstring_encoding);
if (success)
dstr_resize(str, max_size);
if (success)
dstr_resize(str, max_size);
return (bool)success;
return (bool) success;
}

File diff suppressed because it is too large Load diff

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

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,
};

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;

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

View file

@ -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;
}

View file

@ -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"

View file

@ -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 */

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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();
}
}

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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

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);
}

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);
}

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;
}

View file

@ -16,23 +16,23 @@ static const int cy = 600;
/* --------------------------------------------------- */
template<typename T, typename D_T, D_T D>
struct OBSUniqueHandle : std::unique_ptr<T, std::function<D_T>> {
using base = std::unique_ptr<T, std::function<D_T>>;
explicit OBSUniqueHandle(T *obj = nullptr) : base(obj, D) {}
operator T *() { return base::get(); }
template<typename T, typename D_T, D_T D> struct OBSUniqueHandle : std::unique_ptr<T, std::function<D_T>> {
using base = std::unique_ptr<T, std::function<D_T>>;
explicit OBSUniqueHandle(T *obj = nullptr) : base(obj, D)
{}
operator T *()
{
return base::get();
}
};
#define DECLARE_DELETER(x) decltype(x), x
using SourceContext =
OBSUniqueHandle<obs_source, DECLARE_DELETER(obs_source_release)>;
using SourceContext = OBSUniqueHandle<obs_source, DECLARE_DELETER(obs_source_release)>;
using SceneContext =
OBSUniqueHandle<obs_scene, DECLARE_DELETER(obs_scene_release)>;
using SceneContext = OBSUniqueHandle<obs_scene, DECLARE_DELETER(obs_scene_release)>;
using DisplayContext =
OBSUniqueHandle<obs_display, DECLARE_DELETER(obs_display_destroy)>;
using DisplayContext = OBSUniqueHandle<obs_display, DECLARE_DELETER(obs_display_destroy)>;
#undef DECLARE_DELETER
@ -40,69 +40,68 @@ using DisplayContext =
static void CreateOBS()
{
if (!obs_startup("en", nullptr, nullptr))
throw "Couldn't create OBS";
if (!obs_startup("en", nullptr, nullptr))
throw "Couldn't create OBS";
struct obs_video_info ovi;
ovi.adapter = 0;
ovi.fps_num = 30000;
ovi.fps_den = 1001;
ovi.graphics_module = DL_OPENGL;
ovi.output_format = VIDEO_FORMAT_RGBA;
ovi.base_width = cx;
ovi.base_height = cy;
ovi.output_width = cx;
ovi.output_height = cy;
struct obs_video_info ovi;
ovi.adapter = 0;
ovi.fps_num = 30000;
ovi.fps_den = 1001;
ovi.graphics_module = DL_OPENGL;
ovi.output_format = VIDEO_FORMAT_RGBA;
ovi.base_width = cx;
ovi.base_height = cy;
ovi.output_width = cx;
ovi.output_height = cy;
if (obs_reset_video(&ovi) != 0)
throw "Couldn't initialize video";
if (obs_reset_video(&ovi) != 0)
throw "Couldn't initialize video";
}
static DisplayContext CreateDisplay(NSView *view)
{
gs_init_data info = {};
info.cx = cx;
info.cy = cy;
info.format = GS_RGBA;
info.zsformat = GS_ZS_NONE;
info.window.view = view;
gs_init_data info = {};
info.cx = cx;
info.cy = cy;
info.format = GS_RGBA;
info.zsformat = GS_ZS_NONE;
info.window.view = view;
return DisplayContext{obs_display_create(&info, 0)};
return DisplayContext {obs_display_create(&info, 0)};
}
static SceneContext SetupScene()
{
/* ------------------------------------------------------ */
/* load modules */
obs_load_all_modules();
/* ------------------------------------------------------ */
/* load modules */
obs_load_all_modules();
/* ------------------------------------------------------ */
/* create source */
SourceContext source{
obs_source_create("random", "a test source", nullptr, nullptr)};
if (!source)
throw "Couldn't create random test source";
/* ------------------------------------------------------ */
/* create source */
SourceContext source {obs_source_create("random", "a test source", nullptr, nullptr)};
if (!source)
throw "Couldn't create random test source";
/* ------------------------------------------------------ */
/* create scene and add source to scene */
SceneContext scene{obs_scene_create("test scene")};
if (!scene)
throw "Couldn't create scene";
/* ------------------------------------------------------ */
/* create scene and add source to scene */
SceneContext scene {obs_scene_create("test scene")};
if (!scene)
throw "Couldn't create scene";
obs_scene_add(scene, source);
obs_scene_add(scene, source);
/* ------------------------------------------------------ */
/* set the scene as the primary draw source and go */
obs_set_output_source(0, obs_scene_get_source(scene));
/* ------------------------------------------------------ */
/* set the scene as the primary draw source and go */
obs_set_output_source(0, obs_scene_get_source(scene));
return scene;
return scene;
}
@interface OBSTest : NSObject <NSApplicationDelegate, NSWindowDelegate> {
NSWindow *win;
NSView *view;
DisplayContext display;
SceneContext scene;
NSWindow *win;
NSView *view;
DisplayContext display;
SceneContext scene;
}
- (void)applicationDidFinishLaunching:(NSNotification *)notification;
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app;
@ -112,68 +111,63 @@ static SceneContext SetupScene()
@implementation OBSTest
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
UNUSED_PARAMETER(notification);
UNUSED_PARAMETER(notification);
try {
NSRect content_rect = NSMakeRect(0, 0, cx, cy);
win = [[NSWindow alloc]
initWithContentRect:content_rect
styleMask:NSTitledWindowMask |
NSClosableWindowMask
backing:NSBackingStoreBuffered
defer:NO];
if (!win)
throw "Could not create window";
try {
NSRect content_rect = NSMakeRect(0, 0, cx, cy);
win = [[NSWindow alloc] initWithContentRect:content_rect styleMask:NSTitledWindowMask | NSClosableWindowMask
backing:NSBackingStoreBuffered
defer:NO];
if (!win)
throw "Could not create window";
view = [[NSView alloc] initWithFrame:content_rect];
if (!view)
throw "Could not create view";
view = [[NSView alloc] initWithFrame:content_rect];
if (!view)
throw "Could not create view";
CreateOBS();
CreateOBS();
win.title = @"foo";
win.delegate = self;
win.contentView = view;
win.title = @"foo";
win.delegate = self;
win.contentView = view;
[win orderFrontRegardless];
[win center];
[win makeMainWindow];
[win orderFrontRegardless];
[win center];
[win makeMainWindow];
display = CreateDisplay(view);
display = CreateDisplay(view);
scene = SetupScene();
scene = SetupScene();
obs_display_add_draw_callback(
display.get(),
[](void *, uint32_t, uint32_t) {
obs_render_main_texture();
},
nullptr);
obs_display_add_draw_callback(
display.get(), [](void *, uint32_t, uint32_t) {
obs_render_main_texture();
}, nullptr);
} catch (char const *error) {
NSLog(@"%s\n", error);
} catch (char const *error) {
NSLog(@"%s\n", error);
[NSApp terminate:nil];
}
[NSApp terminate:nil];
}
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app
{
UNUSED_PARAMETER(app);
UNUSED_PARAMETER(app);
return YES;
return YES;
}
- (void)windowWillClose:(NSNotification *)notification
{
UNUSED_PARAMETER(notification);
UNUSED_PARAMETER(notification);
obs_set_output_source(0, nullptr);
scene.reset();
display.reset();
obs_set_output_source(0, nullptr);
scene.reset();
display.reset();
obs_shutdown();
NSLog(@"Number of memory leaks: %lu", bnum_allocs());
obs_shutdown();
NSLog(@"Number of memory leaks: %lu", bnum_allocs());
}
@end
@ -181,13 +175,13 @@ static SceneContext SetupScene()
int main()
{
@autoreleasepool {
NSApplication *app = [NSApplication sharedApplication];
OBSTest *test = [[OBSTest alloc] init];
app.delegate = test;
@autoreleasepool {
NSApplication *app = [NSApplication sharedApplication];
OBSTest *test = [[OBSTest alloc] init];
app.delegate = test;
[app run];
}
[app run];
}
return 0;
return 0;
}