Browse Source

mac-capture: Disable all SCK modes besides WindowCapture on macOS 12

SCK has too many open issues on macOS 12 to enable full functionality
on that version. Window Capture has the biggest performance uplift and
so far the least amount of quirks, so leave this variant (with the
"Beta" qualifier) for macOS versions before Ventura.
PatTheMav 2 năm trước cách đây
mục cha
commit
7cedd324e1

+ 1 - 0
plugins/mac-capture/data/locale/en-US.ini

@@ -24,4 +24,5 @@ Crop.size.height="Crop bottom"
 SCK.Name="macOS Screen Capture"
 SCK.Name="macOS Screen Capture"
 SCK.Name.Beta="macOS Screen Capture (BETA)"
 SCK.Name.Beta="macOS Screen Capture (BETA)"
 SCK.AudioUnavailable="Audio capture requires macOS 13 or newer."
 SCK.AudioUnavailable="Audio capture requires macOS 13 or newer."
+SCK.CaptureTypeUnavailable="Selected capture type requires macOS 13 or newer."
 SCK.Method="Method"
 SCK.Method="Method"

+ 97 - 48
plugins/mac-capture/mac-screen-capture.m

@@ -69,6 +69,8 @@ struct screen_capture {
 	NSString *application_id;
 	NSString *application_id;
 };
 };
 
 
+#pragma mark -
+
 static void destroy_screen_stream(struct screen_capture *sc)
 static void destroy_screen_stream(struct screen_capture *sc)
 {
 {
 	if (sc->disp) {
 	if (sc->disp) {
@@ -365,33 +367,9 @@ static bool init_screen_stream(struct screen_capture *sc)
 	case ScreenCaptureDisplayStream: {
 	case ScreenCaptureDisplayStream: {
 		SCDisplay *target_display = get_target_display();
 		SCDisplay *target_display = get_target_display();
 
 
-		if (@available(macOS 13.0, *)) {
-			content_filter = [[SCContentFilter alloc]
-				 initWithDisplay:target_display
-				excludingWindows:[[NSArray alloc] init]];
-		} else {
-			NSArray *excluded = [sc->shareable_content.applications
-				filteredArrayUsingPredicate:
-					[NSPredicate predicateWithBlock:^BOOL(
-							     SCRunningApplication
-								     *application,
-							     NSDictionary<
-								     NSString *,
-								     id>
-								     *_Nullable bindings
-							     __attribute__((
-								     unused))) {
-						return [application
-								.bundleIdentifier
-							isEqualToString:
-								@"com.apple.controlcenter"];
-					}]];
-
-			content_filter = [[SCContentFilter alloc]
-				      initWithDisplay:target_display
-				excludingApplications:excluded
-				     exceptingWindows:[[NSArray alloc] init]];
-		}
+		content_filter = [[SCContentFilter alloc]
+			 initWithDisplay:target_display
+			excludingWindows:[[NSArray alloc] init]];
 
 
 		set_display_mode(sc, target_display);
 		set_display_mode(sc, target_display);
 	} break;
 	} break;
@@ -464,13 +442,22 @@ static bool init_screen_stream(struct screen_capture *sc)
 	[sc->stream_properties setShowsCursor:!sc->hide_cursor];
 	[sc->stream_properties setShowsCursor:!sc->hide_cursor];
 	[sc->stream_properties setColorSpaceName:kCGColorSpaceDisplayP3];
 	[sc->stream_properties setColorSpaceName:kCGColorSpaceDisplayP3];
 	[sc->stream_properties setPixelFormat:'l10r'];
 	[sc->stream_properties setPixelFormat:'l10r'];
-#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 130000
+
 	if (@available(macOS 13.0, *)) {
 	if (@available(macOS 13.0, *)) {
+#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 130000
 		[sc->stream_properties setCapturesAudio:TRUE];
 		[sc->stream_properties setCapturesAudio:TRUE];
 		[sc->stream_properties setExcludesCurrentProcessAudio:TRUE];
 		[sc->stream_properties setExcludesCurrentProcessAudio:TRUE];
 		[sc->stream_properties setChannelCount:2];
 		[sc->stream_properties setChannelCount:2];
-	}
 #endif
 #endif
+	} else {
+		if (sc->capture_type != ScreenCaptureWindowStream) {
+			sc->disp = NULL;
+			os_event_init(&sc->disp_finished, OS_EVENT_TYPE_MANUAL);
+			os_event_init(&sc->stream_start_completed,
+				      OS_EVENT_TYPE_MANUAL);
+			return true;
+		}
+	}
 
 
 	sc->disp = [[SCStream alloc] initWithFilter:content_filter
 	sc->disp = [[SCStream alloc] initWithFilter:content_filter
 				      configuration:sc->stream_properties
 				      configuration:sc->stream_properties
@@ -491,7 +478,7 @@ static bool init_screen_stream(struct screen_capture *sc)
 	}
 	}
 
 
 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 130000
 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 130000
-	if (__builtin_available(macOS 13.0, *)) {
+	if (@available(macOS 13.0, *)) {
 		did_add_output = [sc->disp
 		did_add_output = [sc->disp
 			   addStreamOutput:sc->capture_delegate
 			   addStreamOutput:sc->capture_delegate
 				      type:SCStreamOutputTypeAudio
 				      type:SCStreamOutputTypeAudio
@@ -577,8 +564,8 @@ static void *screen_capture_create(obs_data_t *settings, obs_source_t *source)
 	sc->show_empty_names = obs_data_get_bool(settings, "show_empty_names");
 	sc->show_empty_names = obs_data_get_bool(settings, "show_empty_names");
 	sc->show_hidden_windows =
 	sc->show_hidden_windows =
 		obs_data_get_bool(settings, "show_hidden_windows");
 		obs_data_get_bool(settings, "show_hidden_windows");
-	sc->window = obs_data_get_int(settings, "window");
-	sc->capture_type = obs_data_get_int(settings, "type");
+	sc->window = (CGWindowID)obs_data_get_int(settings, "window");
+	sc->capture_type = (unsigned int)obs_data_get_int(settings, "type");
 
 
 	os_sem_init(&sc->shareable_content_available, 1);
 	os_sem_init(&sc->shareable_content_available, 1);
 	screen_capture_build_content_list(
 	screen_capture_build_content_list(
@@ -591,7 +578,7 @@ static void *screen_capture_create(obs_data_t *settings, obs_source_t *source)
 	if (!sc->effect)
 	if (!sc->effect)
 		goto fail;
 		goto fail;
 
 
-	sc->display = obs_data_get_int(settings, "display");
+	sc->display = (CGDirectDisplayID)obs_data_get_int(settings, "display");
 	sc->application_id = [[NSString alloc]
 	sc->application_id = [[NSString alloc]
 		initWithUTF8String:obs_data_get_string(settings,
 		initWithUTF8String:obs_data_get_string(settings,
 						       "application")];
 						       "application")];
@@ -699,10 +686,11 @@ static void screen_capture_defaults(obs_data_t *settings)
 		}
 		}
 	}
 	}
 
 
-	obs_data_set_default_int(settings, "type", 0);
 	obs_data_set_default_int(settings, "display", initial_display);
 	obs_data_set_default_int(settings, "display", initial_display);
-	obs_data_set_default_int(settings, "window", kCGNullWindowID);
+
 	obs_data_set_default_obj(settings, "application", NULL);
 	obs_data_set_default_obj(settings, "application", NULL);
+	obs_data_set_default_int(settings, "type", ScreenCaptureDisplayStream);
+	obs_data_set_default_int(settings, "window", kCGNullWindowID);
 	obs_data_set_default_bool(settings, "show_cursor", true);
 	obs_data_set_default_bool(settings, "show_cursor", true);
 	obs_data_set_default_bool(settings, "show_empty_names", false);
 	obs_data_set_default_bool(settings, "show_empty_names", false);
 	obs_data_set_default_bool(settings, "show_hidden_windows", false);
 	obs_data_set_default_bool(settings, "show_hidden_windows", false);
@@ -713,7 +701,8 @@ static void screen_capture_update(void *data, obs_data_t *settings)
 	struct screen_capture *sc = data;
 	struct screen_capture *sc = data;
 
 
 	CGWindowID old_window_id = sc->window;
 	CGWindowID old_window_id = sc->window;
-	CGWindowID new_window_id = obs_data_get_int(settings, "window");
+	CGWindowID new_window_id =
+		(CGWindowID)obs_data_get_int(settings, "window");
 
 
 	if (new_window_id > 0 && new_window_id != old_window_id)
 	if (new_window_id > 0 && new_window_id != old_window_id)
 		sc->window = new_window_id;
 		sc->window = new_window_id;
@@ -766,6 +755,8 @@ static void screen_capture_update(void *data, obs_data_t *settings)
 	obs_leave_graphics();
 	obs_leave_graphics();
 }
 }
 
 
+#pragma mark - obs_properties
+
 static bool build_display_list(struct screen_capture *sc,
 static bool build_display_list(struct screen_capture *sc,
 			       obs_properties_t *props)
 			       obs_properties_t *props)
 {
 {
@@ -894,7 +885,8 @@ static bool content_settings_changed(void *data, obs_properties_t *props,
 {
 {
 	struct screen_capture *sc = data;
 	struct screen_capture *sc = data;
 
 
-	unsigned int capture_type_id = obs_data_get_int(settings, "type");
+	unsigned int capture_type_id =
+		(unsigned int)obs_data_get_int(settings, "type");
 	obs_property_t *display_list = obs_properties_get(props, "display");
 	obs_property_t *display_list = obs_properties_get(props, "display");
 	obs_property_t *window_list = obs_properties_get(props, "window");
 	obs_property_t *window_list = obs_properties_get(props, "window");
 	obs_property_t *app_list = obs_properties_get(props, "application");
 	obs_property_t *app_list = obs_properties_get(props, "application");
@@ -902,6 +894,9 @@ static bool content_settings_changed(void *data, obs_properties_t *props,
 	obs_property_t *hidden =
 	obs_property_t *hidden =
 		obs_properties_get(props, "show_hidden_windows");
 		obs_properties_get(props, "show_hidden_windows");
 
 
+	obs_property_t *capture_type_error =
+		obs_properties_get(props, "capture_type_info");
+
 	if (sc->capture_type != capture_type_id) {
 	if (sc->capture_type != capture_type_id) {
 		switch (capture_type_id) {
 		switch (capture_type_id) {
 		case 0: {
 		case 0: {
@@ -910,6 +905,11 @@ static bool content_settings_changed(void *data, obs_properties_t *props,
 			obs_property_set_visible(app_list, false);
 			obs_property_set_visible(app_list, false);
 			obs_property_set_visible(empty, false);
 			obs_property_set_visible(empty, false);
 			obs_property_set_visible(hidden, false);
 			obs_property_set_visible(hidden, false);
+
+			if (capture_type_error) {
+				obs_property_set_visible(capture_type_error,
+							 true);
+			}
 			break;
 			break;
 		}
 		}
 		case 1: {
 		case 1: {
@@ -918,6 +918,11 @@ static bool content_settings_changed(void *data, obs_properties_t *props,
 			obs_property_set_visible(app_list, false);
 			obs_property_set_visible(app_list, false);
 			obs_property_set_visible(empty, true);
 			obs_property_set_visible(empty, true);
 			obs_property_set_visible(hidden, true);
 			obs_property_set_visible(hidden, true);
+
+			if (capture_type_error) {
+				obs_property_set_visible(capture_type_error,
+							 false);
+			}
 			break;
 			break;
 		}
 		}
 		case 2: {
 		case 2: {
@@ -926,21 +931,26 @@ static bool content_settings_changed(void *data, obs_properties_t *props,
 			obs_property_set_visible(window_list, false);
 			obs_property_set_visible(window_list, false);
 			obs_property_set_visible(empty, false);
 			obs_property_set_visible(empty, false);
 			obs_property_set_visible(hidden, true);
 			obs_property_set_visible(hidden, true);
+
+			if (capture_type_error) {
+				obs_property_set_visible(capture_type_error,
+							 true);
+			}
 			break;
 			break;
 		}
 		}
 		}
 		}
 	}
 	}
 
 
-	sc->show_empty_names = obs_data_get_bool(settings, "show_empty_names");
-	sc->show_hidden_windows =
-		obs_data_get_bool(settings, "show_hidden_windows");
-
 	screen_capture_build_content_list(
 	screen_capture_build_content_list(
 		sc, capture_type_id == ScreenCaptureDisplayStream);
 		sc, capture_type_id == ScreenCaptureDisplayStream);
 	build_display_list(sc, props);
 	build_display_list(sc, props);
 	build_window_list(sc, props);
 	build_window_list(sc, props);
 	build_application_list(sc, props);
 	build_application_list(sc, props);
 
 
+	sc->show_empty_names = obs_data_get_bool(settings, "show_empty_names");
+	sc->show_hidden_windows =
+		obs_data_get_bool(settings, "show_hidden_windows");
+
 	return true;
 	return true;
 }
 }
 
 
@@ -949,9 +959,11 @@ static obs_properties_t *screen_capture_properties(void *data)
 	struct screen_capture *sc = data;
 	struct screen_capture *sc = data;
 
 
 	obs_properties_t *props = obs_properties_create();
 	obs_properties_t *props = obs_properties_create();
+
 	obs_property_t *capture_type = obs_properties_add_list(
 	obs_property_t *capture_type = obs_properties_add_list(
 		props, "type", obs_module_text("SCK.Method"),
 		props, "type", obs_module_text("SCK.Method"),
 		OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
 		OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
+
 	obs_property_list_add_int(capture_type,
 	obs_property_list_add_int(capture_type,
 				  obs_module_text("DisplayCapture"), 0);
 				  obs_module_text("DisplayCapture"), 0);
 	obs_property_list_add_int(capture_type,
 	obs_property_list_add_int(capture_type,
@@ -982,9 +994,6 @@ static obs_properties_t *screen_capture_properties(void *data)
 		props, "show_hidden_windows",
 		props, "show_hidden_windows",
 		obs_module_text("WindowUtils.ShowHidden"));
 		obs_module_text("WindowUtils.ShowHidden"));
 
 
-	obs_properties_add_bool(props, "show_cursor",
-				obs_module_text("DisplayCapture.ShowCursor"));
-
 	if (sc) {
 	if (sc) {
 		obs_property_set_modified_callback2(
 		obs_property_set_modified_callback2(
 			hidden, content_settings_changed, sc);
 			hidden, content_settings_changed, sc);
@@ -1020,12 +1029,48 @@ static obs_properties_t *screen_capture_properties(void *data)
 			empty, content_settings_changed, sc);
 			empty, content_settings_changed, sc);
 	}
 	}
 
 
+	obs_properties_add_bool(props, "show_cursor",
+				obs_module_text("DisplayCapture.ShowCursor"));
+
 	if (@available(macOS 13.0, *))
 	if (@available(macOS 13.0, *))
 		;
 		;
-	else
-		obs_properties_add_text(props, "audio_info",
-					obs_module_text("SCK.AudioUnavailable"),
-					OBS_TEXT_INFO);
+	else {
+		obs_property_t *audio_warning = obs_properties_add_text(
+			props, "audio_info",
+			obs_module_text("SCK.AudioUnavailable"), OBS_TEXT_INFO);
+		obs_property_text_set_info_type(audio_warning,
+						OBS_TEXT_INFO_WARNING);
+
+		obs_property_t *capture_type_error = obs_properties_add_text(
+			props, "capture_type_info",
+			obs_module_text("SCK.CaptureTypeUnavailable"),
+			OBS_TEXT_INFO);
+
+		obs_property_text_set_info_type(capture_type_error,
+						OBS_TEXT_INFO_ERROR);
+
+		if (sc) {
+			switch (sc->capture_type) {
+			case ScreenCaptureDisplayStream: {
+				obs_property_set_visible(capture_type_error,
+							 true);
+				break;
+			}
+			case ScreenCaptureWindowStream: {
+				obs_property_set_visible(capture_type_error,
+							 false);
+				break;
+			}
+			case ScreenCaptureApplicationStream: {
+				obs_property_set_visible(capture_type_error,
+							 true);
+				break;
+			}
+			}
+		} else {
+			obs_property_set_visible(capture_type_error, false);
+		}
+	}
 
 
 	return props;
 	return props;
 }
 }
@@ -1053,6 +1098,8 @@ enum gs_color_space screen_capture_video_get_color_space(
 	return GS_CS_SRGB_16F;
 	return GS_CS_SRGB_16F;
 }
 }
 
 
+#pragma mark - obs_source_info
+
 struct obs_source_info screen_capture_info = {
 struct obs_source_info screen_capture_info = {
 	.id = "screen_capture",
 	.id = "screen_capture",
 	.type = OBS_SOURCE_TYPE_INPUT,
 	.type = OBS_SOURCE_TYPE_INPUT,
@@ -1077,6 +1124,8 @@ struct obs_source_info screen_capture_info = {
 	.video_get_color_space = screen_capture_video_get_color_space,
 	.video_get_color_space = screen_capture_video_get_color_space,
 };
 };
 
 
+#pragma mark - ScreenCaptureDelegate
+
 @implementation ScreenCaptureDelegate
 @implementation ScreenCaptureDelegate
 
 
 - (void)stream:(SCStream *)stream
 - (void)stream:(SCStream *)stream