Browse Source

Revert "mac-capture: Improve window capture performance"

This reverts commit 257715d31f300aa4838784954a8caf567a04855d.

Was merged prematurely by mistake (clicked the merge button on the wrong
tab).
jp9000 3 years ago
parent
commit
51050b3075

+ 1 - 1
plugins/mac-capture/mac-display-capture.m

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

+ 66 - 69
plugins/mac-capture/mac-window-capture.m

@@ -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(cap->capture_thread, NULL);
 
-	pthread_join(wc->capture_thread, NULL);
+	CGColorSpaceRelease(cap->color_space);
 
-	os_event_destroy(wc->capture_event);
-	os_event_destroy(wc->stop_event);
+	da_free(cap->buffer);
 
-	destroy_window(&wc->window);
+	os_event_destroy(cap->capture_event);
+	os_event_destroy(cap->stop_event);
 
-	bfree(wc);
+	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 = {

+ 4 - 5
plugins/mac-capture/window-utils.h

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

+ 73 - 91
plugins/mac-capture/window-utils.m

@@ -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 (![cw->owner_name isEqualToString:dict[OWNER_NAME]])
+			continue;
 
-		if (!owner_names_match || !window_names_match)
+		if (![cw->window_name isEqualToString:dict[WINDOW_NAME]])
 			continue;
 
-		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);
-
-		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;
 }