|
@@ -0,0 +1,308 @@
|
|
|
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
|
+From: Samuel Attard <[email protected]>
|
|
|
+Date: Thu, 8 Aug 2024 08:39:10 -0700
|
|
|
+Subject: feat: allow usage of SCContentSharingPicker on supported platforms
|
|
|
+
|
|
|
+This is implemented as a magic "window id" that instead of pulling an SCStream manually
|
|
|
+instead farms out to the screen picker.
|
|
|
+
|
|
|
+diff --git a/content/browser/media/capture/desktop_capture_device_mac.cc b/content/browser/media/capture/desktop_capture_device_mac.cc
|
|
|
+index 88c56f4dfcc1f8517ef1e8b6f1d37f5ba4d0b2c7..a75493a6d4d8ce8340a2d820eff5eed4e6a95109 100644
|
|
|
+--- a/content/browser/media/capture/desktop_capture_device_mac.cc
|
|
|
++++ b/content/browser/media/capture/desktop_capture_device_mac.cc
|
|
|
+@@ -28,7 +28,7 @@ class DesktopCaptureDeviceMac : public IOSurfaceCaptureDeviceBase {
|
|
|
+ ~DesktopCaptureDeviceMac() override = default;
|
|
|
+
|
|
|
+ // IOSurfaceCaptureDeviceBase:
|
|
|
+- void OnStart() override {
|
|
|
++ void OnStart(std::optional<bool> use_native_picker) override {
|
|
|
+ requested_format_ = capture_params().requested_format;
|
|
|
+ requested_format_.pixel_format = media::PIXEL_FORMAT_NV12;
|
|
|
+ DCHECK_GT(requested_format_.frame_size.GetArea(), 0);
|
|
|
+diff --git a/content/browser/media/capture/io_surface_capture_device_base_mac.cc b/content/browser/media/capture/io_surface_capture_device_base_mac.cc
|
|
|
+index 8a774911ce0f610b2c993976d108f840696c1d02..5ead7287e2d765d043f8b9c0229a2ee825d9f544 100644
|
|
|
+--- a/content/browser/media/capture/io_surface_capture_device_base_mac.cc
|
|
|
++++ b/content/browser/media/capture/io_surface_capture_device_base_mac.cc
|
|
|
+@@ -20,7 +20,7 @@ void IOSurfaceCaptureDeviceBase::AllocateAndStart(
|
|
|
+ client_ = std::move(client);
|
|
|
+ capture_params_ = params;
|
|
|
+
|
|
|
+- OnStart();
|
|
|
++ OnStart(params.use_native_picker);
|
|
|
+ }
|
|
|
+
|
|
|
+ void IOSurfaceCaptureDeviceBase::StopAndDeAllocate() {
|
|
|
+diff --git a/content/browser/media/capture/io_surface_capture_device_base_mac.h b/content/browser/media/capture/io_surface_capture_device_base_mac.h
|
|
|
+index 8ac12480f663a74dfbdcf7128a582a81b4474d25..db6802a2603e1d3c3039e49737438124bf2ee1f1 100644
|
|
|
+--- a/content/browser/media/capture/io_surface_capture_device_base_mac.h
|
|
|
++++ b/content/browser/media/capture/io_surface_capture_device_base_mac.h
|
|
|
+@@ -25,7 +25,7 @@ class CONTENT_EXPORT IOSurfaceCaptureDeviceBase
|
|
|
+ ~IOSurfaceCaptureDeviceBase() override;
|
|
|
+
|
|
|
+ // OnStart is called by AllocateAndStart.
|
|
|
+- virtual void OnStart() = 0;
|
|
|
++ virtual void OnStart(std::optional<bool> use_native_picker) = 0;
|
|
|
+
|
|
|
+ // OnStop is called by StopAndDeAllocate.
|
|
|
+ virtual void OnStop() = 0;
|
|
|
+diff --git a/content/browser/media/capture/screen_capture_kit_device_mac.mm b/content/browser/media/capture/screen_capture_kit_device_mac.mm
|
|
|
+index 5c09b98b0c0ade9197a73186809ae4da28a12506..27b7edd2e99f36ebf3381781f2d2b3e7aff3eca1 100644
|
|
|
+--- a/content/browser/media/capture/screen_capture_kit_device_mac.mm
|
|
|
++++ b/content/browser/media/capture/screen_capture_kit_device_mac.mm
|
|
|
+@@ -24,24 +24,83 @@
|
|
|
+ std::optional<gfx::Size>,
|
|
|
+ std::optional<gfx::Rect>)>;
|
|
|
+ using ErrorCallback = base::RepeatingClosure;
|
|
|
++using CancelCallback = base::RepeatingClosure;
|
|
|
++
|
|
|
++API_AVAILABLE(macos(15.0))
|
|
|
++@interface ScreenCaptureKitPickerHelper
|
|
|
++ : NSObject <SCContentSharingPickerObserver>
|
|
|
++
|
|
|
++- (void)contentSharingPicker:(SCContentSharingPicker *)picker
|
|
|
++ didCancelForStream:(SCStream *)stream;
|
|
|
++
|
|
|
++- (void)contentSharingPicker:(SCContentSharingPicker *)picker
|
|
|
++ didUpdateWithFilter:(SCContentFilter *)filter
|
|
|
++ forStream:(SCStream *)stream;
|
|
|
++
|
|
|
++- (void)contentSharingPickerStartDidFailWithError:(NSError *)error;
|
|
|
++
|
|
|
++@end
|
|
|
++
|
|
|
++@implementation ScreenCaptureKitPickerHelper {
|
|
|
++ base::RepeatingCallback<void(SCContentFilter *)> _pickerCallback;
|
|
|
++ ErrorCallback _errorCallback;
|
|
|
++ CancelCallback _cancelCallback;
|
|
|
++}
|
|
|
++
|
|
|
++- (void)contentSharingPicker:(SCContentSharingPicker *)picker
|
|
|
++ didCancelForStream:(SCStream *)stream {
|
|
|
++ // TODO: This doesn't appear to be called on Apple's side;
|
|
|
++ // implement this logic
|
|
|
++ _cancelCallback.Run();
|
|
|
++}
|
|
|
++
|
|
|
++- (void)contentSharingPicker:(SCContentSharingPicker *)picker
|
|
|
++ didUpdateWithFilter:(SCContentFilter *)filter
|
|
|
++ forStream:(SCStream *)stream {
|
|
|
++ if (stream == nil) {
|
|
|
++ _pickerCallback.Run(filter);
|
|
|
++ [picker removeObserver:self];
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++- (void)contentSharingPickerStartDidFailWithError:(NSError *)error {
|
|
|
++ _errorCallback.Run();
|
|
|
++}
|
|
|
++
|
|
|
++- (instancetype)initWithStreamPickCallback:(base::RepeatingCallback<void(SCContentFilter *)>)pickerCallback
|
|
|
++ cancelCallback:(CancelCallback)cancelCallback
|
|
|
++ errorCallback:(ErrorCallback)errorCallback {
|
|
|
++ if (self = [super init]) {
|
|
|
++ _pickerCallback = pickerCallback;
|
|
|
++ _cancelCallback = cancelCallback;
|
|
|
++ _errorCallback = errorCallback;
|
|
|
++ }
|
|
|
++ return self;
|
|
|
++}
|
|
|
++
|
|
|
++@end
|
|
|
+
|
|
|
+ API_AVAILABLE(macos(12.3))
|
|
|
+ @interface ScreenCaptureKitDeviceHelper
|
|
|
+ : NSObject <SCStreamDelegate, SCStreamOutput>
|
|
|
+
|
|
|
+ - (instancetype)initWithSampleCallback:(SampleCallback)sampleCallback
|
|
|
++ cancelCallback:(CancelCallback)cancelCallback
|
|
|
+ errorCallback:(ErrorCallback)errorCallback;
|
|
|
+ @end
|
|
|
+
|
|
|
+ @implementation ScreenCaptureKitDeviceHelper {
|
|
|
+ SampleCallback _sampleCallback;
|
|
|
++ CancelCallback _cancelCallback;
|
|
|
+ ErrorCallback _errorCallback;
|
|
|
+ }
|
|
|
+
|
|
|
+ - (instancetype)initWithSampleCallback:(SampleCallback)sampleCallback
|
|
|
++ cancelCallback:(CancelCallback)cancelCallback
|
|
|
+ errorCallback:(ErrorCallback)errorCallback {
|
|
|
+ if (self = [super init]) {
|
|
|
+ _sampleCallback = sampleCallback;
|
|
|
++ _cancelCallback = cancelCallback;
|
|
|
+ _errorCallback = errorCallback;
|
|
|
+ }
|
|
|
+ return self;
|
|
|
+@@ -141,7 +200,8 @@ + (SCStreamConfiguration*)streamConfigurationWithFrameSize:(gfx::Size)frameSize
|
|
|
+
|
|
|
+ class API_AVAILABLE(macos(12.3)) ScreenCaptureKitDeviceMac
|
|
|
+ : public IOSurfaceCaptureDeviceBase,
|
|
|
+- public ScreenCaptureKitResetStreamInterface {
|
|
|
++ public ScreenCaptureKitResetStreamInterface
|
|
|
++ {
|
|
|
+ public:
|
|
|
+ explicit ScreenCaptureKitDeviceMac(const DesktopMediaID& source)
|
|
|
+ : source_(source),
|
|
|
+@@ -150,18 +210,41 @@ explicit ScreenCaptureKitDeviceMac(const DesktopMediaID& source)
|
|
|
+ device_task_runner_,
|
|
|
+ base::BindRepeating(&ScreenCaptureKitDeviceMac::OnStreamSample,
|
|
|
+ weak_factory_.GetWeakPtr()));
|
|
|
++ CancelCallback cancel_callback = base::BindPostTask(
|
|
|
++ device_task_runner_,
|
|
|
++ base::BindRepeating(&ScreenCaptureKitDeviceMac::OnStreamError,
|
|
|
++ weak_factory_.GetWeakPtr()));
|
|
|
+ ErrorCallback error_callback = base::BindPostTask(
|
|
|
+ device_task_runner_,
|
|
|
+ base::BindRepeating(&ScreenCaptureKitDeviceMac::OnStreamError,
|
|
|
+ weak_factory_.GetWeakPtr()));
|
|
|
+ helper_ = [[ScreenCaptureKitDeviceHelper alloc]
|
|
|
+ initWithSampleCallback:sample_callback
|
|
|
++ cancelCallback:cancel_callback
|
|
|
+ errorCallback:error_callback];
|
|
|
++
|
|
|
++ if (@available(macOS 15.0, *)) {
|
|
|
++ auto picker_callback = base::BindPostTask(
|
|
|
++ device_task_runner_,
|
|
|
++ base::BindRepeating(&ScreenCaptureKitDeviceMac::OnContentFilterReady, weak_factory_.GetWeakPtr())
|
|
|
++ );
|
|
|
++ auto* picker_observer = [[ScreenCaptureKitPickerHelper alloc] initWithStreamPickCallback:picker_callback cancelCallback:cancel_callback errorCallback:error_callback];
|
|
|
++ [[SCContentSharingPicker sharedPicker] addObserver:picker_observer];
|
|
|
++ }
|
|
|
+ }
|
|
|
+ ScreenCaptureKitDeviceMac(const ScreenCaptureKitDeviceMac&) = delete;
|
|
|
+ ScreenCaptureKitDeviceMac& operator=(const ScreenCaptureKitDeviceMac&) =
|
|
|
+ delete;
|
|
|
+- ~ScreenCaptureKitDeviceMac() override = default;
|
|
|
++ ~ScreenCaptureKitDeviceMac() override {
|
|
|
++ if (@available(macOS 15.0, *)) {
|
|
|
++ auto* picker = [SCContentSharingPicker sharedPicker];
|
|
|
++ ScreenCaptureKitDeviceMac::active_streams_--;
|
|
|
++ picker.maximumStreamCount = @(ScreenCaptureKitDeviceMac::active_streams_);
|
|
|
++ if (ScreenCaptureKitDeviceMac::active_streams_ == 0 && picker.active) {
|
|
|
++ picker.active = false;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ }
|
|
|
+
|
|
|
+ void OnShareableContentCreated(SCShareableContent* content) {
|
|
|
+ DCHECK(device_task_runner_->RunsTasksInCurrentSequence());
|
|
|
+@@ -225,6 +308,18 @@ void OnShareableContentCreated(SCShareableContent* content) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
++ if (@available(macOS 15.0, *)) {
|
|
|
++ // Update the content size. This step is neccessary when used together
|
|
|
++ // with SCContentSharingPicker. If the Chrome picker is used, it will
|
|
|
++ // change to retina resolution if applicable.
|
|
|
++ stream_config_content_size_ =
|
|
|
++ gfx::Size(filter.contentRect.size.width * filter.pointPixelScale,
|
|
|
++ filter.contentRect.size.height * filter.pointPixelScale);
|
|
|
++ }
|
|
|
++
|
|
|
++ OnContentFilterReady(filter);
|
|
|
++ }
|
|
|
++ void OnContentFilterReady(SCContentFilter* filter) {
|
|
|
+ gfx::RectF dest_rect_in_frame;
|
|
|
+ actual_capture_format_ = capture_params().requested_format;
|
|
|
+ actual_capture_format_.pixel_format = media::PIXEL_FORMAT_NV12;
|
|
|
+@@ -238,6 +333,7 @@ void OnShareableContentCreated(SCShareableContent* content) {
|
|
|
+ stream_ = [[SCStream alloc] initWithFilter:filter
|
|
|
+ configuration:config
|
|
|
+ delegate:helper_];
|
|
|
++
|
|
|
+ {
|
|
|
+ NSError* error = nil;
|
|
|
+ bool add_stream_output_result =
|
|
|
+@@ -379,7 +475,7 @@ void OnStreamError() {
|
|
|
+ if (fullscreen_module_) {
|
|
|
+ fullscreen_module_->Reset();
|
|
|
+ }
|
|
|
+- OnStart();
|
|
|
++ OnStart(std::nullopt);
|
|
|
+ } else {
|
|
|
+ client()->OnError(media::VideoCaptureError::kScreenCaptureKitStreamError,
|
|
|
+ FROM_HERE, "Stream delegate called didStopWithError");
|
|
|
+@@ -402,9 +498,30 @@ void OnUpdateConfigurationError() {
|
|
|
+ }
|
|
|
+
|
|
|
+ // IOSurfaceCaptureDeviceBase:
|
|
|
+- void OnStart() override {
|
|
|
++ void OnStart(std::optional<bool> use_native_picker) override {
|
|
|
+ DCHECK(device_task_runner_->RunsTasksInCurrentSequence());
|
|
|
+
|
|
|
++ if (@available(macOS 15.0, *)) {
|
|
|
++ constexpr bool DefaultUseNativePicker = true;
|
|
|
++ if (use_native_picker.value_or(DefaultUseNativePicker) && source_.id < 0 && source_.window_id == 0) {
|
|
|
++ auto* picker = [SCContentSharingPicker sharedPicker];
|
|
|
++ ScreenCaptureKitDeviceMac::active_streams_++;
|
|
|
++ picker.maximumStreamCount = @(ScreenCaptureKitDeviceMac::active_streams_);
|
|
|
++ if (!picker.active) {
|
|
|
++ picker.active = true;
|
|
|
++ }
|
|
|
++ NSMutableArray<NSNumber*>* exclude_ns_windows = [NSMutableArray array];
|
|
|
++ [[[[NSApplication sharedApplication] windows] filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSWindow* win, NSDictionary *bindings) {
|
|
|
++ return [win sharingType] == NSWindowSharingNone;
|
|
|
++ }]] enumerateObjectsUsingBlock:^(NSWindow* win, NSUInteger idx, BOOL *stop) {
|
|
|
++ [exclude_ns_windows addObject:@([win windowNumber])];
|
|
|
++ }];
|
|
|
++ picker.defaultConfiguration.excludedWindowIDs = exclude_ns_windows;
|
|
|
++ [picker present];
|
|
|
++ return;
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
+ auto content_callback = base::BindPostTask(
|
|
|
+ device_task_runner_,
|
|
|
+ base::BindRepeating(
|
|
|
+@@ -470,6 +587,8 @@ void ResetStreamTo(SCWindow* window) override {
|
|
|
+ }
|
|
|
+
|
|
|
+ private:
|
|
|
++ static int active_streams_;
|
|
|
++
|
|
|
+ const DesktopMediaID source_;
|
|
|
+ const scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_;
|
|
|
+
|
|
|
+@@ -498,6 +617,8 @@ void ResetStreamTo(SCWindow* window) override {
|
|
|
+ base::WeakPtrFactory<ScreenCaptureKitDeviceMac> weak_factory_{this};
|
|
|
+ };
|
|
|
+
|
|
|
++int ScreenCaptureKitDeviceMac::active_streams_ = 0;
|
|
|
++
|
|
|
+ } // namespace
|
|
|
+
|
|
|
+ std::unique_ptr<media::VideoCaptureDevice> CreateScreenCaptureKitDeviceMac(
|
|
|
+diff --git a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
|
|
|
+index 6aa143bb74789d205f17014f511b7900001237b1..f38ea5df3b6c694aed3a54486733130a2bec606b 100644
|
|
|
+--- a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
|
|
|
++++ b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
|
|
|
+@@ -344,13 +344,15 @@ void InProcessVideoCaptureDeviceLauncher::LaunchDeviceAsync(
|
|
|
+ std::move(after_start_capture_callback));
|
|
|
+ break;
|
|
|
+ #else
|
|
|
++ media::VideoCaptureParams updated_params = params;
|
|
|
++ updated_params.use_native_picker = stream_type != blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE;
|
|
|
+ // All cases other than tab capture or Aura desktop/window capture.
|
|
|
+ TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("video_and_image_capture"),
|
|
|
+ "UsingDesktopCapturer", TRACE_EVENT_SCOPE_THREAD);
|
|
|
+ start_capture_closure = base::BindOnce(
|
|
|
+ &InProcessVideoCaptureDeviceLauncher::
|
|
|
+ DoStartDesktopCaptureOnDeviceThread,
|
|
|
+- base::Unretained(this), desktop_id, params,
|
|
|
++ base::Unretained(this), desktop_id, updated_params,
|
|
|
+ CreateDeviceClient(media::VideoCaptureBufferType::kSharedMemory,
|
|
|
+ kMaxNumberOfBuffers, std::move(receiver),
|
|
|
+ std::move(receiver_on_io_thread)),
|
|
|
+diff --git a/media/capture/video_capture_types.h b/media/capture/video_capture_types.h
|
|
|
+index f2b75f5b2f547ad135c1288bf3639b26dedc8053..ef18724d9f2ea68a47b66fc3981f58a73ac1b51d 100644
|
|
|
+--- a/media/capture/video_capture_types.h
|
|
|
++++ b/media/capture/video_capture_types.h
|
|
|
+@@ -355,6 +355,8 @@ struct CAPTURE_EXPORT VideoCaptureParams {
|
|
|
+ // Flag indicating whether HiDPI mode should be enabled for tab capture
|
|
|
+ // sessions.
|
|
|
+ bool is_high_dpi_enabled = true;
|
|
|
++
|
|
|
++ std::optional<bool> use_native_picker;
|
|
|
+ };
|
|
|
+
|
|
|
+ CAPTURE_EXPORT std::ostream& operator<<(
|