Revert "mac-capture: Improve window capture performance"
This reverts commit 257715d31f300aa4838784954a8caf567a04855d. Was merged prematurely by mistake (clicked the merge button on the wrong tab).
This commit is contained in:
parent
447b17e75e
commit
51050b3075
@ -134,7 +134,7 @@ static inline void update_window_params(struct display_capture *dc)
|
||||
[dc->screen convertRectToBacking:dc->window_rect];
|
||||
|
||||
} else {
|
||||
if (find_window(&dc->window, NULL))
|
||||
if (find_window(&dc->window, NULL, false))
|
||||
update_window_params(dc);
|
||||
else
|
||||
dc->on_screen = false;
|
||||
|
@ -13,8 +13,14 @@ struct window_capture {
|
||||
|
||||
struct cocoa_window window;
|
||||
|
||||
//CGRect bounds;
|
||||
//CGWindowListOption window_option;
|
||||
CGWindowImageOption image_option;
|
||||
|
||||
CGColorSpaceRef color_space;
|
||||
|
||||
DARRAY(uint8_t) buffer;
|
||||
|
||||
pthread_t capture_thread;
|
||||
os_event_t *capture_event;
|
||||
os_event_t *stop_event;
|
||||
@ -22,66 +28,56 @@ struct window_capture {
|
||||
|
||||
static CGImageRef get_image(struct window_capture *wc)
|
||||
{
|
||||
CFMutableArrayRef arr =
|
||||
CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
|
||||
CFArrayAppendValue(arr, (void *)(uintptr_t)wc->window.window_id);
|
||||
NSArray *arr = (NSArray *)CGWindowListCreate(
|
||||
kCGWindowListOptionIncludingWindow, wc->window.window_id);
|
||||
[arr autorelease];
|
||||
|
||||
CGImageRef image = CGWindowListCreateImageFromArray(
|
||||
CGRectNull, (CFArrayRef)arr, wc->image_option);
|
||||
CFRelease(arr);
|
||||
if (!arr.count && !find_window(&wc->window, NULL, false))
|
||||
return NULL;
|
||||
|
||||
if (!image && find_window(&wc->window, NULL))
|
||||
image = get_image(wc);
|
||||
|
||||
return image;
|
||||
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 image = get_image(wc);
|
||||
|
||||
if (!image)
|
||||
CGImageRef img = get_image(wc);
|
||||
if (!img)
|
||||
return;
|
||||
|
||||
size_t width = CGImageGetWidth(image);
|
||||
size_t height = CGImageGetHeight(image);
|
||||
size_t bytes_per_row = CGImageGetBytesPerRow(image);
|
||||
size_t width = CGImageGetWidth(img);
|
||||
size_t height = CGImageGetHeight(img);
|
||||
|
||||
if ((!width && !height) || CGImageGetBitsPerPixel(image) != 32 ||
|
||||
CGImageGetBitsPerComponent(image) != 8) {
|
||||
CGImageRelease(image);
|
||||
return;
|
||||
}
|
||||
CGRect rect = {{0, 0}, {width, height}};
|
||||
da_resize(wc->buffer, width * height * 4);
|
||||
uint8_t *data = wc->buffer.array;
|
||||
|
||||
// float fps = 1e9 / (ts - wc->frame.timestamp);
|
||||
// NSLog(@"FPS %.4f", fps);
|
||||
|
||||
CGDataProviderRef provider = CGImageGetDataProvider(image);
|
||||
CFDataRef data = CGDataProviderCopyData(provider);
|
||||
const uint8_t *buffer = CFDataGetBytePtr(data);
|
||||
CGContextRef cg_context = CGBitmapContextCreate(
|
||||
data, width, height, 8, width * 4, wc->color_space,
|
||||
kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst);
|
||||
CGContextSetBlendMode(cg_context, kCGBlendModeCopy);
|
||||
CGContextDrawImage(cg_context, rect, img);
|
||||
CGContextRelease(cg_context);
|
||||
CGImageRelease(img);
|
||||
|
||||
struct obs_source_frame frame = {
|
||||
.format = VIDEO_FORMAT_BGRA,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.data[0] = (uint8_t *)buffer,
|
||||
.linesize[0] = bytes_per_row,
|
||||
.data[0] = data,
|
||||
.linesize[0] = width * 4,
|
||||
.timestamp = ts,
|
||||
};
|
||||
|
||||
obs_source_output_video(wc->source, &frame);
|
||||
|
||||
CFRelease(data);
|
||||
CGImageRelease(image);
|
||||
}
|
||||
|
||||
static void *capture_thread(void *data)
|
||||
{
|
||||
struct window_capture *wc = data;
|
||||
|
||||
os_set_thread_name(obs_source_get_name(wc->source));
|
||||
|
||||
for (;;) {
|
||||
os_event_wait(wc->capture_event);
|
||||
if (os_event_try(wc->stop_event) != EAGAIN)
|
||||
@ -102,6 +98,10 @@ static inline void *window_capture_create_internal(obs_data_t *settings,
|
||||
|
||||
wc->source = source;
|
||||
|
||||
wc->color_space = CGColorSpaceCreateDeviceRGB();
|
||||
|
||||
da_init(wc->buffer);
|
||||
|
||||
init_window(&wc->window, settings);
|
||||
|
||||
wc->image_option = obs_data_get_bool(settings, "show_shadow")
|
||||
@ -111,21 +111,7 @@ static inline void *window_capture_create_internal(obs_data_t *settings,
|
||||
os_event_init(&wc->capture_event, OS_EVENT_TYPE_AUTO);
|
||||
os_event_init(&wc->stop_event, OS_EVENT_TYPE_MANUAL);
|
||||
|
||||
/*
|
||||
* "To let Cocoa know that you intend to use multiple threads, all you
|
||||
* have to do is spawn a single thread using the NSThread class and
|
||||
* let that thread immediately exit."
|
||||
*
|
||||
* @see https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html#//apple_ref/doc/uid/10000057i-CH15-SW21
|
||||
*/
|
||||
|
||||
[[NSThread new] start];
|
||||
|
||||
if ([NSThread isMultiThreaded] != 1)
|
||||
abort();
|
||||
|
||||
if (pthread_create(&wc->capture_thread, NULL, capture_thread, wc))
|
||||
abort();
|
||||
pthread_create(&wc->capture_thread, NULL, capture_thread, wc);
|
||||
|
||||
return wc;
|
||||
}
|
||||
@ -139,26 +125,29 @@ static void *window_capture_create(obs_data_t *settings, obs_source_t *source)
|
||||
|
||||
static void window_capture_destroy(void *data)
|
||||
{
|
||||
struct window_capture *wc = data;
|
||||
struct window_capture *cap = data;
|
||||
|
||||
os_event_signal(wc->stop_event);
|
||||
os_event_signal(wc->capture_event);
|
||||
os_event_signal(cap->stop_event);
|
||||
os_event_signal(cap->capture_event);
|
||||
|
||||
pthread_join(wc->capture_thread, NULL);
|
||||
pthread_join(cap->capture_thread, NULL);
|
||||
|
||||
os_event_destroy(wc->capture_event);
|
||||
os_event_destroy(wc->stop_event);
|
||||
CGColorSpaceRelease(cap->color_space);
|
||||
|
||||
destroy_window(&wc->window);
|
||||
da_free(cap->buffer);
|
||||
|
||||
bfree(wc);
|
||||
os_event_destroy(cap->capture_event);
|
||||
os_event_destroy(cap->stop_event);
|
||||
|
||||
destroy_window(&cap->window);
|
||||
|
||||
bfree(cap);
|
||||
}
|
||||
|
||||
static void window_capture_defaults(obs_data_t *settings)
|
||||
{
|
||||
window_defaults(settings);
|
||||
|
||||
obs_data_set_default_bool(settings, "show_shadow", false);
|
||||
window_defaults(settings);
|
||||
}
|
||||
|
||||
static obs_properties_t *window_capture_properties(void *unused)
|
||||
@ -184,14 +173,15 @@ static inline void window_capture_update_internal(struct window_capture *wc,
|
||||
|
||||
update_window(&wc->window, settings);
|
||||
|
||||
blog(LOG_INFO,
|
||||
"[window-capture: '%s'] update settings:\n"
|
||||
"\twindow: %s\n"
|
||||
"\towner: %s",
|
||||
obs_source_get_name(wc->source),
|
||||
wc->window.window_name.length ? [wc->window.window_name UTF8String]
|
||||
: "(null)",
|
||||
[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)
|
||||
@ -207,16 +197,23 @@ static const char *window_capture_getname(void *unused)
|
||||
return obs_module_text("WindowCapture");
|
||||
}
|
||||
|
||||
static void window_capture_tick(void *data, float seconds)
|
||||
static inline void window_capture_tick_internal(struct window_capture *wc,
|
||||
float seconds)
|
||||
{
|
||||
UNUSED_PARAMETER(seconds);
|
||||
os_event_signal(wc->capture_event);
|
||||
}
|
||||
|
||||
static void window_capture_tick(void *data, float seconds)
|
||||
{
|
||||
struct window_capture *wc = data;
|
||||
|
||||
if (!obs_source_showing(wc->source))
|
||||
return;
|
||||
|
||||
os_event_signal(wc->capture_event);
|
||||
@autoreleasepool {
|
||||
return window_capture_tick_internal(data, seconds);
|
||||
}
|
||||
}
|
||||
|
||||
struct obs_source_info window_capture_info = {
|
||||
|
@ -8,18 +8,17 @@ struct cocoa_window {
|
||||
CGWindowID window_id;
|
||||
int owner_pid;
|
||||
|
||||
pthread_mutex_t name_lock;
|
||||
NSString *owner_name;
|
||||
NSString *window_name;
|
||||
|
||||
uint64_t last_search_time;
|
||||
|
||||
pthread_mutex_t mutex;
|
||||
uint64_t next_search_time;
|
||||
};
|
||||
typedef struct cocoa_window *cocoa_window_t;
|
||||
|
||||
NSArray *enumerate_windows(void);
|
||||
NSArray *enumerate_cocoa_windows(void);
|
||||
|
||||
bool find_window(cocoa_window_t cw, obs_data_t *settings);
|
||||
bool find_window(cocoa_window_t cw, obs_data_t *settings, bool force);
|
||||
|
||||
void init_window(cocoa_window_t cw, obs_data_t *settings);
|
||||
|
||||
|
@ -27,6 +27,7 @@ NSArray *enumerate_windows(void)
|
||||
{
|
||||
NSArray *arr = (NSArray *)CGWindowListCopyWindowInfo(
|
||||
kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
|
||||
|
||||
[arr autorelease];
|
||||
|
||||
return [arr sortedArrayUsingComparator:win_info_cmp];
|
||||
@ -36,205 +37,186 @@ NSArray *enumerate_windows(void)
|
||||
#define WAIT_TIME_US WAIT_TIME_MS * 1000
|
||||
#define WAIT_TIME_NS WAIT_TIME_US * 1000
|
||||
|
||||
bool find_window(cocoa_window_t cw, obs_data_t *settings)
|
||||
bool find_window(cocoa_window_t cw, obs_data_t *settings, bool force)
|
||||
{
|
||||
uint64_t ts = os_gettime_ns();
|
||||
|
||||
if (cw->last_search_time + WAIT_TIME_NS > ts)
|
||||
if (!force && cw->next_search_time > os_gettime_ns())
|
||||
return false;
|
||||
|
||||
cw->last_search_time = ts;
|
||||
cw->next_search_time = os_gettime_ns() + WAIT_TIME_NS;
|
||||
|
||||
if (pthread_mutex_lock(&cw->mutex))
|
||||
abort();
|
||||
pthread_mutex_lock(&cw->name_lock);
|
||||
|
||||
if (!cw->window_name.length && !cw->owner_name.length)
|
||||
goto unlock;
|
||||
goto invalid_name;
|
||||
|
||||
for (NSDictionary *dict in enumerate_windows()) {
|
||||
bool owner_names_match =
|
||||
(!cw->owner_name.length && !dict[OWNER_NAME]) ||
|
||||
[cw->owner_name isEqualToString:dict[OWNER_NAME]];
|
||||
bool window_names_match =
|
||||
(!cw->window_name.length && !dict[WINDOW_NAME]) ||
|
||||
[cw->window_name isEqualToString:dict[WINDOW_NAME]];
|
||||
|
||||
if (!owner_names_match || !window_names_match)
|
||||
if (![cw->owner_name isEqualToString:dict[OWNER_NAME]])
|
||||
continue;
|
||||
|
||||
cw->window_id = [dict[WINDOW_NUMBER] intValue];
|
||||
cw->owner_pid = [dict[OWNER_PID] intValue];
|
||||
if (![cw->window_name isEqualToString:dict[WINDOW_NAME]])
|
||||
continue;
|
||||
|
||||
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;
|
||||
|
||||
obs_data_set_int(settings, "window", cw->window_id);
|
||||
obs_data_set_int(settings, "owner_pid", cw->owner_pid);
|
||||
|
||||
if (pthread_mutex_unlock(&cw->mutex))
|
||||
abort();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unlock:
|
||||
if (pthread_mutex_unlock(&cw->mutex))
|
||||
abort();
|
||||
|
||||
invalid_name:
|
||||
pthread_mutex_unlock(&cw->name_lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
void init_window(cocoa_window_t cw, obs_data_t *settings)
|
||||
{
|
||||
if (pthread_mutex_init(&cw->mutex, NULL))
|
||||
abort();
|
||||
|
||||
cw->window_id = obs_data_get_int(settings, "window");
|
||||
cw->owner_pid = obs_data_get_int(settings, "owner_pid");
|
||||
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->last_search_time = 0;
|
||||
// Find initial window.
|
||||
pthread_mutex_lock(&cw->name_lock);
|
||||
|
||||
if (!cw->window_name.length && !cw->owner_name.length)
|
||||
return;
|
||||
|
||||
NSNumber *window_id = @(cw->window_id);
|
||||
NSNumber *owner_pid = @(cw->owner_pid);
|
||||
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.length && !dict[OWNER_NAME]) ||
|
||||
[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.length && !dict[WINDOW_NAME]) ||
|
||||
[cw->window_name isEqualToString:dict[WINDOW_NAME]];
|
||||
|
||||
if (owner_names_match && (ids_match || window_names_match)) {
|
||||
cw->window_id = [dict[WINDOW_NUMBER] intValue];
|
||||
cw->owner_pid = [dict[OWNER_PID] intValue];
|
||||
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;
|
||||
|
||||
obs_data_set_int(settings, "window", cw->window_id);
|
||||
obs_data_set_int(settings, "owner_pid", cw->owner_pid);
|
||||
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
invalid_name:
|
||||
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];
|
||||
|
||||
if (pthread_mutex_destroy(&cw->mutex))
|
||||
abort();
|
||||
}
|
||||
|
||||
void update_window(cocoa_window_t cw, obs_data_t *settings)
|
||||
{
|
||||
if (pthread_mutex_lock(&cw->mutex))
|
||||
abort();
|
||||
|
||||
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 = obs_data_get_int(settings, "owner_pid");
|
||||
cw->window_id = obs_data_get_int(settings, "window");
|
||||
|
||||
if (pthread_mutex_unlock(&cw->mutex))
|
||||
abort();
|
||||
}
|
||||
|
||||
static inline NSString *make_name(NSString *owner, NSString *name)
|
||||
static inline const char *make_name(NSString *owner, NSString *name)
|
||||
{
|
||||
if (!owner.length)
|
||||
return @"";
|
||||
return "";
|
||||
|
||||
return [NSString stringWithFormat:@"[%@] %@", owner, name];
|
||||
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;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static inline bool window_changed_internal(obs_property_t *p,
|
||||
obs_data_t *settings)
|
||||
{
|
||||
NSNumber *window_id = @(obs_data_get_int(settings, "window"));
|
||||
NSNumber *owner_pid = @(obs_data_get_int(settings, "owner_pid"));
|
||||
NSString *owner_name = @(obs_data_get_string(settings, "owner_name"));
|
||||
int window_id = 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"));
|
||||
|
||||
bool show_empty_names = obs_data_get_bool(settings, "show_empty_names");
|
||||
|
||||
NSDictionary *win_info = @{
|
||||
OWNER_NAME: owner_name,
|
||||
OWNER_PID: owner_pid,
|
||||
OWNER_NAME: window_owner,
|
||||
WINDOW_NAME: window_name,
|
||||
WINDOW_NUMBER: window_id,
|
||||
};
|
||||
|
||||
NSArray *arr = enumerate_windows();
|
||||
NSDictionary *cur = nil;
|
||||
|
||||
for (NSDictionary *dict in arr) {
|
||||
if ([window_id isEqualToNumber:dict[WINDOW_NUMBER]]) {
|
||||
cur = dict;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
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(owner_name, window_name).UTF8String,
|
||||
window_id.intValue);
|
||||
|
||||
p, make_name(window_owner, window_name),
|
||||
window_id);
|
||||
obs_property_list_item_disable(p, idx, true);
|
||||
}
|
||||
|
||||
if (!show_empty_names &&
|
||||
(!dict[WINDOW_NAME] || ![dict[WINDOW_NAME] length]) &&
|
||||
![window_id isEqualToNumber:dict[WINDOW_NUMBER]])
|
||||
if (!show_empty_names && !name.length &&
|
||||
window_id != wid.intValue)
|
||||
continue;
|
||||
|
||||
obs_property_list_add_int(p,
|
||||
make_name(dict[OWNER_NAME],
|
||||
dict[WINDOW_NAME])
|
||||
.UTF8String,
|
||||
[dict[WINDOW_NUMBER] 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(owner_name, window_name).UTF8String,
|
||||
window_id.intValue);
|
||||
|
||||
p, make_name(window_owner, window_name), window_id);
|
||||
obs_property_list_item_disable(p, idx, true);
|
||||
}
|
||||
|
||||
if (!window_found)
|
||||
return true;
|
||||
|
||||
obs_data_set_int(settings, "window", [cur[WINDOW_NUMBER] intValue]);
|
||||
obs_data_set_int(settings, "owner_pid", [cur[OWNER_PID] intValue]);
|
||||
obs_data_set_string(settings, "owner_name",
|
||||
[cur[OWNER_NAME] UTF8String]);
|
||||
obs_data_set_string(settings, "window_name",
|
||||
[cur[WINDOW_NAME] UTF8String]);
|
||||
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);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user