Compare commits

...

27 Commits

Author SHA1 Message Date
Keeley Hammond
589272e065 chore(todo): debug patch 2025-04-15 16:36:03 -07:00
George Xu
e364a20ba1 intense debug session with not many results... 2025-04-09 17:40:57 -07:00
George Xu
6b6ef0b9ef chore: update patch: 2025-03-28 15:29:14 -07:00
George Xu
bb092132be kick ci 2025-03-19 14:32:44 -07:00
George Xu
0cdf1012bb remove old patch and lint 2025-03-19 14:32:44 -07:00
George Xu
eaa952361c wrap macos code in buildflag 2025-03-19 14:32:44 -07:00
George Xu
8ef66cade3 remove log statements 2025-03-19 14:32:43 -07:00
George Xu
2fb1bfec42 cleaning up 2025-03-19 14:32:43 -07:00
George Xu
65864f6203 test 2025-03-19 14:32:43 -07:00
George Xu
75eb7aabd6 add more logging 2025-03-19 14:32:43 -07:00
George Xu
f8aae32c6c some linting and fixing 2025-03-19 14:32:43 -07:00
George Xu
3e6834099e make things work with type none 2025-03-19 14:32:43 -07:00
George Xu
3d5d5f5cb0 happy path share window: 2025-03-19 14:32:43 -07:00
George Xu
3268c6ba76 new changes 2025-03-19 14:32:43 -07:00
George Xu
50f96692df add more logging, picker works on macos 2025-03-19 14:32:43 -07:00
George Xu
53a7802e95 update patches 2025-03-19 14:32:43 -07:00
George Xu
b6c4c727d1 UNDO ME- onpush for ci 2025-03-19 14:32:43 -07:00
Keeley Hammond
4aa9773388 feat: allow screen or window to return depending on preferredDisplaySurface set 2025-03-19 14:32:43 -07:00
Keeley Hammond
7b6b5bf224 chore: skip skip_refresh_callback if ShouldUseSCContentSharingPicker is false 2025-03-19 14:32:43 -07:00
Keeley Hammond
bd1ed17c02 feat: modify API to use useSystemPicker option 2025-03-19 14:32:43 -07:00
Keeley Hammond
813fc3af58 docs: add documentation 2025-03-19 14:32:43 -07:00
Keeley Hammond
e7675a1a1c chore: remove OnSourceAdded (re-add in the future) 2025-03-19 14:32:43 -07:00
Keeley Hammond
729ca429b8 feat: add getNativePickerSource() API option 2025-03-19 14:32:43 -07:00
Keeley Hammond
da56809b62 fix: make capturer work on both screens and windows 2025-03-19 14:32:43 -07:00
Keeley Hammond
12ca8b30be fix: make window capturer work, still issue with first refresh 2025-03-19 14:32:43 -07:00
Keeley Hammond
56d4433e46 feat: use upstream MacOS SCContentSharingPicker implementation pt. 1 2025-03-19 14:32:43 -07:00
Keeley Hammond
0ee7150832 feat: partially implement IsDisplayMediaSystemPickerAvailable 2025-03-19 14:32:43 -07:00
17 changed files with 1162 additions and 446 deletions

View File

@@ -30,7 +30,7 @@ on:
required: false
push:
branches:
- main
- sckp-macos15
- '[1-9][0-9]-x-y'
pull_request:

View File

@@ -17,7 +17,8 @@ app.whenReady().then(() => {
session.defaultSession.setDisplayMediaRequestHandler((request, callback) => {
desktopCapturer.getSources({ types: ['screen'] }).then((sources) => {
// Grant access to the first screen found.
// Your app shows some UI, but in this example
// grant access to the first screen found.
callback({ video: sources[0], audio: 'loopback' })
})
// If true, use the system picker if available.
@@ -42,7 +43,8 @@ startButton.addEventListener('click', () => {
video: {
width: 320,
height: 240,
frameRate: 30
frameRate: 30,
displaySurface: 'monitor'
}
}).then(stream => {
video.srcObject = stream
@@ -98,6 +100,16 @@ which can detected by [`systemPreferences.getMediaAccessStatus`][].
[`navigator.mediaDevices.getUserMedia`]: https://developer.mozilla.org/en/docs/Web/API/MediaDevices/getUserMedia
[`systemPreferences.getMediaAccessStatus`]: system-preferences.md#systempreferencesgetmediaaccessstatusmediatype-windows-macos
### `desktopCapturer.isDisplayMediaSystemPickerAvailable()` _Experimental_ _macOS_
Returns `Boolean`, whether or not requesting desktop content via
the system picker is supported on this platform.
Currently this will only return `true` on macOS 14.4 and higher. When
true, use the `getNativePickerSource` method to return the system picker's
selected media stream. Not doing so may cause a warning dialog to your users
on macOS 15 and higher.
## Caveats
`navigator.mediaDevices.getUserMedia` does not work on macOS for audio capture due to a fundamental limitation whereby apps that want to access the system's audio require a [signed kernel extension](https://developer.apple.com/library/archive/documentation/Security/Conceptual/System_Integrity_Protection_Guide/KernelExtensions/KernelExtensions.html). Chromium, and by extension Electron, does not provide this.

View File

@@ -971,6 +971,7 @@ session.fromPartition('some-partition').setPermissionCheckHandler((webContents,
* `videoRequested` Boolean - true if the web content requested a video stream.
* `audioRequested` Boolean - true if the web content requested an audio stream.
* `userGesture` Boolean - Whether a user gesture was active when this request was triggered.
* `preferredDisplaySurface` String - The preferred display used for sharing screen in this request.
* `callback` Function
* `streams` Object
* `video` Object | [WebFrameMain](web-frame-main.md) (optional)

View File

@@ -594,6 +594,8 @@ filenames = {
"shell/common/gin_converters/callback_converter.h",
"shell/common/gin_converters/content_converter.cc",
"shell/common/gin_converters/content_converter.h",
"shell/common/gin_converters/display_surface_converter.cc",
"shell/common/gin_converters/display_surface_converter.h",
"shell/common/gin_converters/file_dialog_converter.cc",
"shell/common/gin_converters/file_dialog_converter.h",
"shell/common/gin_converters/file_path_converter.h",

View File

@@ -16,13 +16,13 @@ function isValid (options: Electron.SourcesOptions) {
export { isDisplayMediaSystemPickerAvailable };
export async function getSources (args: Electron.SourcesOptions) {
export async function getSources (args: Electron.SourcesOptions, useSystemPicker: boolean = false) {
if (!isValid(args)) throw new Error('Invalid options');
const resizableValues = new Map();
if (process.platform === 'darwin') {
// Fix for bug in ScreenCaptureKit that modifies a window's styleMask the first time
// it captures a non-resizable window. We record each non-resizable window's styleMask,
// it captures a non-resizable window. We record each non-resizable window's styleMask
// and we restore modified styleMasks later, after the screen capture.
for (const win of BrowserWindow.getAllWindows()) {
resizableValues.set([win.id], win.resizable);
@@ -32,14 +32,15 @@ export async function getSources (args: Electron.SourcesOptions) {
const captureWindow = args.types.includes('window');
const captureScreen = args.types.includes('screen');
const { thumbnailSize = { width: 150, height: 150 } } = args;
const { thumbnailSize = { width: 0, height: 0 } } = args;
const { fetchWindowIcons = false } = args;
const options = {
captureWindow,
captureScreen,
thumbnailSize,
fetchWindowIcons
fetchWindowIcons,
useSystemPicker
};
for (const running of currentlyRunning) {
@@ -81,7 +82,7 @@ export async function getSources (args: Electron.SourcesOptions) {
resolve(sources);
};
capturer.startHandling(captureWindow, captureScreen, thumbnailSize, fetchWindowIcons);
capturer.startHandling(captureWindow, captureScreen, thumbnailSize, fetchWindowIcons, useSystemPicker);
});
currentlyRunning.push({

View File

@@ -2,25 +2,61 @@ import { fetchWithSession } from '@electron/internal/browser/api/net-fetch';
import { addIpcDispatchListeners } from '@electron/internal/browser/ipc-dispatch';
import * as deprecate from '@electron/internal/common/deprecate';
import { net } from 'electron/main';
import { desktopCapturer, net } from 'electron/main';
const { fromPartition, fromPath, Session } = process._linkedBinding('electron_browser_session');
const { isDisplayMediaSystemPickerAvailable } = process._linkedBinding('electron_browser_desktop_capturer');
// Fake video window that activates the native system picker
// This is used to get around the need for a screen/window
// id in Chrome's desktopCapturer.
let fakeVideoWindowId = -1;
// See content/public/browser/desktop_media_id.h
const kMacOsNativePickerId = -4;
const systemPickerVideoSource = Object.create(null);
Object.defineProperty(systemPickerVideoSource, 'id', {
get () {
return `window:${kMacOsNativePickerId}:${fakeVideoWindowId--}`;
async function getNativePickerSource (preferredDisplaySurface: string) {
console.log('SESSION.TS getNativePickerSource');
// Fake video window that activates the native system picker
// This is used to get around the need for a screen/window
// id in Chrome's desktopCapturer.
// let fakeVideoWindowId = -1;
// const kMacOsNativePickerId = -4;
if (process.platform !== 'darwin') {
throw new Error('Native system picker option is currently only supported on MacOS');
}
});
systemPickerVideoSource.name = '';
Object.freeze(systemPickerVideoSource);
if (!isDisplayMediaSystemPickerAvailable) {
throw new Error(`Native system picker unavailable.
Note: This is an experimental API; please check the API documentation for updated restrictions`);
}
let types: Electron.SourcesOptions['types'];
switch (preferredDisplaySurface) {
case 'no_preference':
types = ['screen', 'window'];
break;
case 'monitor':
types = ['screen'];
break;
case 'window':
types = ['window'];
break;
default:
types = ['screen', 'window'];
}
// Pass in the needed options for a more native experience
// screen & windows by default, no thumbnails, since the native picker doesn't return them
const options: Electron.SourcesOptions = {
types,
thumbnailSize: { width: 0, height: 0 },
fetchWindowIcons: false
};
const mediaStreams = await desktopCapturer.getSources(options);
if (mediaStreams.length === 0) {
throw new Error('No media streams found');
}
// mediaStreams[0].id = `none:${kMacOsNativePickerId}:${fakeVideoWindowId--}`;
console.log('SESSION.TS mediaStreams', mediaStreams);
return mediaStreams[0];
}
Session.prototype._init = function () {
addIpcDispatchListeners(this);
@@ -48,12 +84,12 @@ Session.prototype.fetch = function (input: RequestInfo, init?: RequestInit) {
Session.prototype.setDisplayMediaRequestHandler = function (handler, opts) {
if (!handler) return this._setDisplayMediaRequestHandler(handler, opts);
this._setDisplayMediaRequestHandler(async (req, callback) => {
this._setDisplayMediaRequestHandler(async (request, callback) => {
if (opts && opts.useSystemPicker && isDisplayMediaSystemPickerAvailable()) {
return callback({ video: systemPickerVideoSource });
return callback({ video: await getNativePickerSource(request.preferredDisplaySurface) });
}
return handler(req, callback);
// return handler(request, callback);
}, opts);
};

View File

@@ -128,7 +128,6 @@ chore_remove_reference_to_chrome_browser_themes.patch
feat_enable_customizing_symbol_color_in_framecaptionbutton.patch
build_allow_electron_mojom_interfaces_to_depend_on_blink.patch
osr_shared_texture_remove_keyed_mutex_on_win_dxgi.patch
feat_allow_usage_of_sccontentsharingpicker_on_supported_platforms.patch
chore_partial_revert_of.patch
fix_software_compositing_infinite_loop.patch
fix_add_method_which_disables_headless_mode_on_native_widget.patch
@@ -143,3 +142,5 @@ fix_win32_synchronous_spellcheck.patch
fix_enable_wrap_iter_in_string_view_and_array.patch
fix_linter_error.patch
fix_take_snapped_status_into_account_when_showing_a_window.patch
feat_make_macos_sccontentsharingpicker_work_in_electron_and_return_screen_window_or_both.patch
fix_debug_desktop_capturer_picture.patch

View File

@@ -1,383 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samuel Attard <marshallofsound@electronjs.org>
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 afb657b7c9e1ede1273532b16428d37cc5d75c59..16707cf516cd34682c84ea2ccebddaa0f84e01f8 100644
--- a/content/browser/media/capture/desktop_capture_device_mac.cc
+++ b/content/browser/media/capture/desktop_capture_device_mac.cc
@@ -29,7 +29,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 4ec4c895423151d2c907d97de1461cbde8c8a639..0e59797833b81c07299b8c342f591e192183c6f1 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 e2771b7b281274cdcb601a5bc78a948ad592087b..48d116823a28213e50775f378e6ce04ce3af5072 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 7e17594c30ac3cf8cb484b53563b03fc75bd2e0b..0e4a68f2fd8179640f877cb258b4049610fd49da 100644
--- a/content/browser/media/capture/screen_capture_kit_device_mac.mm
+++ b/content/browser/media/capture/screen_capture_kit_device_mac.mm
@@ -27,6 +27,61 @@
std::optional<float>,
bool)>;
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
namespace {
API_AVAILABLE(macos(12.3))
@@ -123,18 +178,22 @@ @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;
@@ -211,29 +270,53 @@ + (SCStreamConfiguration*)streamConfigurationWithFrameSize:(gfx::Size)frameSize
class API_AVAILABLE(macos(12.3)) ScreenCaptureKitDeviceMac
: public IOSurfaceCaptureDeviceBase,
- public ScreenCaptureKitResetStreamInterface {
+ public ScreenCaptureKitResetStreamInterface
+ {
public:
explicit ScreenCaptureKitDeviceMac(const DesktopMediaID& source,
- SCContentFilter* filter)
+ [[maybe_unused]] SCContentFilter* filter)
: source_(source),
- filter_(filter),
device_task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()) {
SampleCallback sample_callback = base::BindPostTask(
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())
+ );
+ picker_helper_ = [[ScreenCaptureKitPickerHelper alloc] initWithStreamPickCallback:picker_callback cancelCallback:cancel_callback errorCallback:error_callback];
+ [[SCContentSharingPicker sharedPicker] addObserver:picker_helper_];
+ }
}
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;
+ [[SCContentSharingPicker sharedPicker] removeObserver:picker_helper_];
+ }
+ }
+ }
void OnShareableContentCreated(SCShareableContent* content) {
DCHECK(device_task_runner_->RunsTasksInCurrentSequence());
@@ -301,7 +384,7 @@ void CreateStream(SCContentFilter* filter) {
return;
}
- if (@available(macOS 14.0, *)) {
+ 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.
@@ -310,6 +393,9 @@ void CreateStream(SCContentFilter* filter) {
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;
@@ -323,6 +409,7 @@ void CreateStream(SCContentFilter* filter) {
stream_ = [[SCStream alloc] initWithFilter:filter
configuration:config
delegate:helper_];
+
{
NSError* error = nil;
bool add_stream_output_result =
@@ -480,7 +567,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");
@@ -503,23 +590,41 @@ void OnUpdateConfigurationError() {
}
// IOSurfaceCaptureDeviceBase:
- void OnStart() override {
+ void OnStart(std::optional<bool> use_native_picker) override {
DCHECK(device_task_runner_->RunsTasksInCurrentSequence());
- if (filter_) {
- // SCContentSharingPicker is used where filter_ is set on creation.
- CreateStream(filter_);
- } else {
- // Chrome picker is used.
- auto content_callback = base::BindPostTask(
- device_task_runner_,
- base::BindRepeating(
- &ScreenCaptureKitDeviceMac::OnShareableContentCreated,
- weak_factory_.GetWeakPtr()));
- auto handler = ^(SCShareableContent* content, NSError* error) {
- content_callback.Run(content);
- };
- [SCShareableContent getShareableContentWithCompletionHandler:handler];
+
+ if (@available(macOS 15.0, *)) {
+ constexpr bool DefaultUseNativePicker = true;
+ if (use_native_picker.value_or(DefaultUseNativePicker) &&
+ source_.id == DesktopMediaID::kMacOsNativePickerId &&
+ 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(
+ &ScreenCaptureKitDeviceMac::OnShareableContentCreated,
+ weak_factory_.GetWeakPtr()));
+ auto handler = ^(SCShareableContent* content, NSError* error) {
+ content_callback.Run(content);
+ };
+ [SCShareableContent getShareableContentWithCompletionHandler:handler];
}
void OnStop() override {
DCHECK(device_task_runner_->RunsTasksInCurrentSequence());
@@ -577,8 +682,9 @@ void ResetStreamTo(SCWindow* window) override {
}
private:
+ static int active_streams_;
+
const DesktopMediaID source_;
- SCContentFilter* const filter_;
const scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_;
// The actual format of the video frames that are sent to `client`.
@@ -594,6 +700,10 @@ void ResetStreamTo(SCWindow* window) override {
// Helper class that acts as output and delegate for `stream_`.
ScreenCaptureKitDeviceHelper* __strong helper_;
+ // Helper class that acts as an observer for SCContentSharingPicker
+ API_AVAILABLE(macos(15.0))
+ ScreenCaptureKitPickerHelper* __strong picker_helper_;
+
// This is used to detect when a captured presentation enters fullscreen mode.
// If this happens, the module will call the ResetStreamTo function.
std::unique_ptr<ScreenCaptureKitFullscreenModule> fullscreen_module_;
@@ -606,6 +716,8 @@ void ResetStreamTo(SCWindow* window) override {
base::WeakPtrFactory<ScreenCaptureKitDeviceMac> weak_factory_{this};
};
+int ScreenCaptureKitDeviceMac::active_streams_ = 0;
+
} // namespace
// Although ScreenCaptureKit is available in 12.3 there were some bugs that
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 0ab684dc8e792359abb90b91917eddae42e6123d..f0f08a834f06c7669da6030640434308a5cbd056 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
@@ -316,8 +316,16 @@ void InProcessVideoCaptureDeviceLauncher::LaunchDeviceAsync(
break;
}
+#if defined(USE_AURA)
+ bool allow_window_id = false;
+#elif BUILDFLAG(IS_MAC)
+ bool allow_window_id =
+ desktop_id.id == DesktopMediaID::kMacOsNativePickerId;
+#endif
+
#if defined(USE_AURA) || BUILDFLAG(IS_MAC)
- if (desktop_id.window_id != DesktopMediaID::kNullId) {
+ if (!allow_window_id &&
+ desktop_id.window_id != DesktopMediaID::kNullId) {
// For the other capturers, when a bug reports the type of capture it's
// easy enough to determine which capturer was used, but it's a little
// fuzzier with window capture.
@@ -333,13 +341,15 @@ void InProcessVideoCaptureDeviceLauncher::LaunchDeviceAsync(
}
#endif // defined(USE_AURA) || BUILDFLAG(IS_MAC)
+ 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/content/public/browser/desktop_media_id.h b/content/public/browser/desktop_media_id.h
index 415156d403a59bf426cf4561a9d58ecdb27524b4..78aa7b2359c684d5305bf6352751dfbb7ca00d29 100644
--- a/content/public/browser/desktop_media_id.h
+++ b/content/public/browser/desktop_media_id.h
@@ -27,6 +27,8 @@ struct CONTENT_EXPORT DesktopMediaID {
static constexpr Id kNullId = 0;
// Represents a fake id to create a dummy capturer for autotests.
static constexpr Id kFakeId = -3;
+ // Represents an id to use native macOS picker for screenshare
+ static constexpr Id kMacOsNativePickerId = -4;
#if defined(USE_AURA) || BUILDFLAG(IS_MAC)
// Assigns integer identifier to the |window| and returns its DesktopMediaID.
diff --git a/media/capture/video_capture_types.h b/media/capture/video_capture_types.h
index 53e8077c9c0b635df0abdeca43fa9a6373c68252..2805e36cc42190d8197d83f5df235094570e3d5d 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<<(

View File

@@ -0,0 +1,737 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Keeley Hammond <khammond@slack-corp.com>
Date: Tue, 14 Jan 2025 15:50:29 -0800
Subject: feat: allow desktop capturer to return either screen, window or both
and make MacOS SCContentSharingPicker work in Electron
This patch is a work in progress that contains assorted changes to make the MacOS SCContentSharingPicker upstream implementation work within Electron. If this comment is still in this patch during PR review, it is not ready for prime time
This patch can be removed after our desktopCapturer is refactored.
diff --git a/chrome/browser/media/webrtc/capture_policy_utils.cc b/chrome/browser/media/webrtc/capture_policy_utils.cc
index 36797f37627d534bd446e0d8270618722ed9a2fa..32222cb526dc047a02cd9382814edd71cca0a378 100644
--- a/chrome/browser/media/webrtc/capture_policy_utils.cc
+++ b/chrome/browser/media/webrtc/capture_policy_utils.cc
@@ -356,7 +356,9 @@ void FilterMediaList(std::vector<DesktopMediaList::Type>& media_types,
media_types, [capture_level](const DesktopMediaList::Type& type) {
switch (type) {
case DesktopMediaList::Type::kNone:
- NOTREACHED();
+ //TODO(review): are we able to remove this?
+ return capture_level < AllowedScreenCaptureLevel::kDesktop;
+ // NOTREACHED();
// SameOrigin is more restrictive than just Tabs, so as long as
// at least SameOrigin is allowed, these entries should stay.
// They should be filtered later by the caller.
diff --git a/chrome/browser/media/webrtc/desktop_media_list_base.cc b/chrome/browser/media/webrtc/desktop_media_list_base.cc
index 08400be4d1bae18502d19beed6b2d9057e55dd4f..4df1a9f44959db1f73d6a8a535d297343f7f0c00 100644
--- a/chrome/browser/media/webrtc/desktop_media_list_base.cc
+++ b/chrome/browser/media/webrtc/desktop_media_list_base.cc
@@ -12,9 +12,11 @@
#include <set>
#include <utility>
+#include "base/logging.h"
#include "base/functional/bind.h"
#include "base/hash/hash.h"
#include "chrome/browser/media/webrtc/desktop_media_list.h"
+#include "chrome/browser/media/webrtc/thumbnail_capturer_mac.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "third_party/skia/include/core/SkBitmap.h"
@@ -60,24 +62,34 @@ void DesktopMediaListBase::StartUpdating(DesktopMediaListObserver* observer) {
// If there is a delegated source list, it may not have been started yet.
if (IsSourceListDelegated())
StartDelegatedCapturer();
+ LOG(ERROR) << "start delegated capturer";
// Process sources previously discovered by a call to Update().
if (observer_) {
for (size_t i = 0; i < sources_.size(); i++) {
observer_->OnSourceAdded(i);
+ LOG(ERROR) << "OnSourceAdded, i: " << i;
}
}
-
+ LOG(INFO)<< "Refresh callback_ is null: " << refresh_callback_.is_null() << ";";
DCHECK(!refresh_callback_);
+ LOG(INFO) << "dcheck passed";
refresh_callback_ = base::BindOnce(&DesktopMediaListBase::ScheduleNextRefresh,
weak_factory_.GetWeakPtr());
Refresh(true);
}
void DesktopMediaListBase::Update(UpdateCallback callback, bool refresh_thumbnails) {
+ LOG(ERROR) << "Update";
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(sources_.empty());
- DCHECK(!refresh_callback_);
+ #if BUILDFLAG(IS_MAC)
+ if (!ShouldUseSCContentSharingPicker()) {
+ DCHECK(!refresh_callback_);
+ };
+ #else
+ DCHECK(!refresh_callback_);
+ #endif
refresh_callback_ = std::move(callback);
Refresh(refresh_thumbnails);
}
diff --git a/chrome/browser/media/webrtc/native_desktop_media_list.cc b/chrome/browser/media/webrtc/native_desktop_media_list.cc
index 312882d656f9f6b3d3fd98128131cea63f818e0d..f61697e1717fcfa0500974e8523438389fbab5ae 100644
--- a/chrome/browser/media/webrtc/native_desktop_media_list.cc
+++ b/chrome/browser/media/webrtc/native_desktop_media_list.cc
@@ -45,6 +45,7 @@
#endif
#if BUILDFLAG(IS_MAC)
+#include "chrome/browser/media/webrtc/thumbnail_capturer_mac.h"
#include "components/remote_cocoa/browser/scoped_cg_window_id.h"
#endif
@@ -162,9 +163,10 @@ content::DesktopMediaID::Type ConvertToDesktopMediaIDType(
return content::DesktopMediaID::Type::TYPE_SCREEN;
case DesktopMediaList::Type::kWindow:
return content::DesktopMediaID::Type::TYPE_WINDOW;
+ case DesktopMediaList::Type::kNone:
+ return content::DesktopMediaID::Type::TYPE_NONE;
case DesktopMediaList::Type::kWebContents:
case DesktopMediaList::Type::kCurrentTab:
- case DesktopMediaList::Type::kNone:
break;
}
NOTREACHED();
@@ -372,7 +374,8 @@ NativeDesktopMediaList::Worker::Worker(
nullptr) {
DCHECK(capturer_);
- DCHECK(source_type_ == DesktopMediaID::Type::TYPE_WINDOW ||
+ DCHECK(source_type_ == DesktopMediaID::Type::TYPE_WINDOW || source_type_ ==
+ DesktopMediaID::Type::TYPE_NONE ||
!add_current_process_windows_);
}
@@ -505,11 +508,27 @@ NativeDesktopMediaList::Worker::FormatSources(
break;
case DesktopMediaID::Type::TYPE_WINDOW:
+ case DesktopMediaID::Type::TYPE_NONE:
+ #if BUILDFLAG(IS_MAC)
+ // If using NativeScreenCapturePickerMac,
+ // skipping the picker will skip the first window selection.
+ if (ShouldUseSCContentSharingPicker()) {
+ title = base::UTF8ToUTF16(sources[i].title);
+ LOG(ERROR) << "formatting native picker source, id: " << sources[i].id << " title: " << title;
+ LOG(ERROR) << "sources size: " << sources.size();
+ LOG(ERROR) << "excluded window id: " << excluded_window_id;
+ } else if (sources[i].id == excluded_window_id) {
+ // Skip the picker dialog window.
+ continue;
+ }
+ title = base::UTF8ToUTF16(sources[i].title);
+ #else
// Skip the picker dialog window.
if (sources[i].id == excluded_window_id) {
continue;
}
title = base::UTF8ToUTF16(sources[i].title);
+ #endif
break;
default:
@@ -775,14 +794,10 @@ NativeDesktopMediaList::NativeDesktopMediaList(
is_source_list_delegated_(capturer->GetDelegatedSourceListController() !=
nullptr) {
type_ = type;
-
- DCHECK(type_ == DesktopMediaList::Type::kWindow ||
+ DCHECK(type_ == DesktopMediaList::Type::kWindow || type_ == DesktopMediaList::Type::kNone ||
!add_current_process_windows_);
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
- // webrtc::DesktopCapturer implementations on Windows, MacOS and Fuchsia
- // expect to run on a thread with a UI message pump. Under Fuchsia the
- // capturer needs an async loop to support FIDL I/O.
base::MessagePumpType thread_type = base::MessagePumpType::UI;
#else
base::MessagePumpType thread_type = base::MessagePumpType::DEFAULT;
@@ -794,8 +809,9 @@ NativeDesktopMediaList::NativeDesktopMediaList(
std::move(capturer), add_current_process_windows_,
auto_show_delegated_source_list);
- if (!is_source_list_delegated_)
+ if (!is_source_list_delegated_) {
StartCapturer();
+ }
}
NativeDesktopMediaList::~NativeDesktopMediaList() {
@@ -903,7 +919,7 @@ void NativeDesktopMediaList::ShowDelegatedList() {
}
void NativeDesktopMediaList::Refresh(bool update_thumbnails) {
- DCHECK(can_refresh());
+ DCHECK(can_refresh());
#if defined(USE_AURA)
DCHECK_EQ(pending_aura_capture_requests_, 0);
diff --git a/chrome/browser/media/webrtc/thumbnail_capturer_mac.h b/chrome/browser/media/webrtc/thumbnail_capturer_mac.h
index 12a74f8f32cc00a7f3d7802865ae4b309961341d..acbcfb08ae8c44e24a04b326096289428bc6ff60 100644
--- a/chrome/browser/media/webrtc/thumbnail_capturer_mac.h
+++ b/chrome/browser/media/webrtc/thumbnail_capturer_mac.h
@@ -8,6 +8,9 @@
#include "chrome/browser/media/webrtc/desktop_media_list.h"
#include "chrome/browser/media/webrtc/thumbnail_capturer.h"
+// Returns true if the SCK sharing picker is available and enabled.
+bool ShouldUseSCContentSharingPicker();
+
// Returns true if the SCK thumbnail capturer is available and enabled.
bool ShouldUseThumbnailCapturerMac(DesktopMediaList::Type type);
diff --git a/chrome/browser/media/webrtc/thumbnail_capturer_mac.mm b/chrome/browser/media/webrtc/thumbnail_capturer_mac.mm
index 744f2447dc2d43c4f6be695bf561474993468705..bb5781b0f0df11422f8355dcc59f5f0f46ebcf89 100644
--- a/chrome/browser/media/webrtc/thumbnail_capturer_mac.mm
+++ b/chrome/browser/media/webrtc/thumbnail_capturer_mac.mm
@@ -13,6 +13,7 @@
#include <optional>
#include <unordered_map>
+#include "base/logging.h"
#include "base/apple/bridging.h"
#include "base/apple/foundation_util.h"
#include "base/apple/scoped_cftyperef.h"
@@ -71,11 +72,11 @@
return content::DesktopMediaID::Type::TYPE_SCREEN;
case DesktopMediaList::Type::kWindow:
return content::DesktopMediaID::Type::TYPE_WINDOW;
+ case DesktopMediaList::Type::kNone:
+ return content::DesktopMediaID::Type::TYPE_NONE;
case DesktopMediaList::Type::kWebContents:
case DesktopMediaList::Type::kCurrentTab:
return content::DesktopMediaID::Type::TYPE_WEB_CONTENTS;
- case DesktopMediaList::Type::kNone:
- break;
}
NOTREACHED();
}
@@ -442,7 +443,7 @@ void OnCapturedFrame(base::apple::ScopedCFTypeRef<CGImageRef> image,
max_frame_rate_(kThumbnailCapturerMacMaxFrameRate),
shareable_windows_([[NSArray<SCWindow*> alloc] init]) {
CHECK(type_ == DesktopMediaList::Type::kWindow ||
- type_ == DesktopMediaList::Type::kScreen);
+ type_ == DesktopMediaList::Type::kScreen || type_ == DesktopMediaList::Type::kNone);
}
ThumbnailCapturerMac::~ThumbnailCapturerMac() {
@@ -680,17 +681,15 @@ void OnCapturedFrame(base::apple::ScopedCFTypeRef<CGImageRef> image,
source_id);
}
+} // namespace
+
bool ShouldUseSCContentSharingPicker() {
if (@available(macOS 15.0, *)) {
- if (base::FeatureList::IsEnabled(media::kUseSCContentSharingPicker)) {
- return true;
- }
+ return base::FeatureList::IsEnabled(media::kUseSCContentSharingPicker);
}
return false;
}
-} // namespace
-
bool ShouldUseThumbnailCapturerMac(DesktopMediaList::Type type) {
// There was a bug in ScreenCaptureKit that was fixed in 14.4,
// see b/40076027.
@@ -704,6 +703,9 @@ bool ShouldUseThumbnailCapturerMac(DesktopMediaList::Type type) {
return ShouldUseSCContentSharingPicker() ||
base::FeatureList::IsEnabled(kScreenCaptureKitPickerScreen);
case DesktopMediaList::Type::kNone:
+ return ShouldUseSCContentSharingPicker() ||
+ base::FeatureList::IsEnabled(kScreenCaptureKitStreamPickerSonoma) ||
+ base::FeatureList::IsEnabled(kScreenCaptureKitPickerScreen);
case DesktopMediaList::Type::kCurrentTab:
case DesktopMediaList::Type::kWebContents:
return false;
diff --git a/content/browser/media/capture/desktop_capture_device.cc b/content/browser/media/capture/desktop_capture_device.cc
index b4b1e9ee805a8565a04737e0898ad8e46709c4d8..7b856f89028fe8f5594c1ff35900019250865ab5 100644
--- a/content/browser/media/capture/desktop_capture_device.cc
+++ b/content/browser/media/capture/desktop_capture_device.cc
@@ -899,7 +899,6 @@ std::unique_ptr<media::VideoCaptureDevice> DesktopCaptureDevice::Create(
result.reset(new DesktopCaptureDevice(std::move(capturer), source.type));
return result;
}
-
switch (source.type) {
case DesktopMediaID::TYPE_SCREEN: {
std::unique_ptr<webrtc::DesktopCapturer> screen_capturer;
@@ -920,7 +919,7 @@ std::unique_ptr<media::VideoCaptureDevice> DesktopCaptureDevice::Create(
}
break;
}
-
+ case DesktopMediaID::TYPE_NONE:
case DesktopMediaID::TYPE_WINDOW: {
std::unique_ptr<webrtc::DesktopCapturer> window_capturer;
if (auto generic_capturer =
diff --git a/content/browser/media/capture/native_screen_capture_picker.cc b/content/browser/media/capture/native_screen_capture_picker.cc
index 152f3aa78032ee3f8c48fbefe052a2f1d85bed6b..d57df80dff9c6867cddf669efe3f90c43c03a0a7 100644
--- a/content/browser/media/capture/native_screen_capture_picker.cc
+++ b/content/browser/media/capture/native_screen_capture_picker.cc
@@ -7,6 +7,7 @@
#if BUILDFLAG(IS_MAC)
#include "content/browser/media/capture/native_screen_capture_picker_mac.h"
#include "media/base/media_switches.h"
+#include "base/logging.h"
#endif
namespace content {
diff --git a/content/browser/media/capture/native_screen_capture_picker_mac.mm b/content/browser/media/capture/native_screen_capture_picker_mac.mm
index 0dbc7ebc4ba6066eb6b20f8e66f50a1dff4d94f3..17d658ee011b81208b77ee1e65925ae9ad1e68f7 100644
--- a/content/browser/media/capture/native_screen_capture_picker_mac.mm
+++ b/content/browser/media/capture/native_screen_capture_picker_mac.mm
@@ -206,8 +206,12 @@ void Open(DesktopMediaID::Type type,
base::OnceClosure cancel_callback,
base::OnceClosure error_callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ LOG(ERROR) << "Open";
+ // Chrome doesn't allow both screens & windows in their picker,
+ // but Electron does - add a check for TYPE_NONE.
CHECK(type == DesktopMediaID::Type::TYPE_SCREEN ||
- type == DesktopMediaID::Type::TYPE_WINDOW);
+ type == DesktopMediaID::Type::TYPE_WINDOW ||
+ type == DesktopMediaID::Type::TYPE_NONE);
if (@available(macOS 14.0, *)) {
NSNumber* source_id = @(next_id_);
PickerObserver* picker_observer = [[PickerObserver alloc]
@@ -226,25 +230,32 @@ void Open(DesktopMediaID::Type type,
// TODO(https://crbug.com/360781940): Add support for changing selected
// content. The problem to solve is how this should interact with stream
// restart.
- config.allowsChangingSelectedContent = false;
+ config.allowsChangingSelectedContent = true;
NSNumber* max_stream_count = @(kMaxContentShareCountValue.Get());
- if (type == DesktopMediaID::Type::TYPE_SCREEN) {
- config.allowedPickerModes = SCContentSharingPickerModeSingleDisplay;
- picker.defaultConfiguration = config;
- picker.maximumStreamCount = max_stream_count;
- [picker presentPickerUsingContentStyle:SCShareableContentStyleDisplay];
- VLOG(1) << "NSCPM: Show screen-sharing picker for source_id = "
- << source_id.longValue;
- LogToUma(SCContentSharingPickerOperation::kPresentScreen_Start);
- } else {
- config.allowedPickerModes = SCContentSharingPickerModeSingleWindow;
- picker.defaultConfiguration = config;
- picker.maximumStreamCount = max_stream_count;
- [picker presentPickerUsingContentStyle:SCShareableContentStyleWindow];
- VLOG(1) << "NSCPM: Show window-sharing picker for source_id = "
- << source_id.longValue;
- LogToUma(SCContentSharingPickerOperation::kPresentWindow_Start);
- }
+ // LOG(ERROR) << "Type: " << type;
+ // Chrome doesn't allow both screens & windows in their picker,
+ // but Electron does; we patch out the MediaID::Type conditional here
+ // if (type == DesktopMediaID::Type::TYPE_SCREEN) {
+ // config.allowedPickerModes = SCContentSharingPickerModeSingleDisplay;
+ // picker.defaultConfiguration = config;
+ // picker.maximumStreamCount = max_stream_count;
+ // [picker presentPickerUsingContentStyle:SCShareableContentStyleDisplay];
+ // } else if (type == DesktopMediaID::Type::TYPE_WINDOW) {
+ // config.allowedPickerModes = SCContentSharingPickerModeSingleWindow;
+ // picker.defaultConfiguration = config;
+ // picker.maximumStreamCount = max_stream_count;
+ // [picker presentPickerUsingContentStyle:SCShareableContentStyleWindow];
+ // VLOG(1) << "NSCPM: Show screen-sharing picker for source_id = "
+ // << source_id.longValue;
+ // LogToUma(SCContentSharingPickerOperation::kPresentScreen_Start);
+ // } else {
+ picker.defaultConfiguration = config;
+ picker.maximumStreamCount = max_stream_count;
+ [picker present];
+ // VLOG(1) << "NSCPM: Show window-sharing picker for source_id = "
+ // << source_id.longValue;
+ // LogToUma(SCContentSharingPickerOperation::kPresentWindow_Start);
+ // }
} else {
NOTREACHED();
}
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 7e17594c30ac3cf8cb484b53563b03fc75bd2e0b..fef50841f472bf3e76c171d9e106c689157adaae 100644
--- a/content/browser/media/capture/screen_capture_kit_device_mac.mm
+++ b/content/browser/media/capture/screen_capture_kit_device_mac.mm
@@ -281,6 +281,7 @@ void OnShareableContentCreated(SCShareableContent* content) {
if (!fullscreen_module_) {
fullscreen_module_ = MaybeCreateScreenCaptureKitFullscreenModule(
device_task_runner_, *this, window);
+ break;
}
}
}
@@ -616,6 +617,7 @@ void ResetStreamTo(SCWindow* window) override {
SCContentFilter* filter) {
switch (source.type) {
case DesktopMediaID::TYPE_SCREEN:
+ case DesktopMediaID::TYPE_NONE:
// ScreenCaptureKitDeviceMac only supports a single display at a time.
// It will not stitch desktops together. If
// kScreenCaptureKitFullDesktopFallback is enabled, we will fallback to
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 0ab684dc8e792359abb90b91917eddae42e6123d..2351919905fbec6c1bfe9ce59d799192a9e2e0e6 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
@@ -177,19 +177,20 @@ DesktopCaptureImplementation CreatePlatformDependentVideoCaptureDevice(
#if BUILDFLAG(IS_MAC)
// Use ScreenCaptureKit with picker if specified. `desktop_id` for the picker
// is not compatible with the other implementations.
- if (picker) {
- device_out = picker->CreateDevice(desktop_id);
- if (device_out) {
- return kScreenCaptureKitDeviceMac;
- }
- return kNoImplementation;
- }
+ // if (picker) {
+ // device_out = picker->CreateDevice(desktop_id);
+ // if (device_out) {
+ // return kScreenCaptureKitDeviceMac;
+ // }
+ // return kNoImplementation;
+ // }
// Prefer using ScreenCaptureKit. After that try DesktopCaptureDeviceMac, and
// if both fail, use the generic DesktopCaptureDevice.
if (desktop_id.type == DesktopMediaID::TYPE_WINDOW ||
(desktop_id.type == DesktopMediaID::TYPE_SCREEN &&
- base::FeatureList::IsEnabled(kScreenCaptureKitMacScreen))) {
+ base::FeatureList::IsEnabled(kScreenCaptureKitMacScreen)) ||
+ (desktop_id.type == DesktopMediaID::TYPE_NONE && base::FeatureList::IsEnabled(kScreenCaptureKitMacScreen))) {
device_out = CreateScreenCaptureKitDeviceMac(desktop_id);
if (device_out) {
return kScreenCaptureKitDeviceMac;
@@ -237,6 +238,9 @@ void InProcessVideoCaptureDeviceLauncher::LaunchDeviceAsync(
DCHECK(state_ == State::READY_TO_LAUNCH);
if (receiver_on_io_thread) {
+ LOG(INFO) << "InProcessVideoCaptureDeviceLauncher::LaunchDeviceAsync: Posting "
+ "start request to device thread for device_id = "
+ << device_id;
std::ostringstream string_stream;
string_stream
<< "InProcessVideoCaptureDeviceLauncher::LaunchDeviceAsync: Posting "
@@ -244,7 +248,7 @@ void InProcessVideoCaptureDeviceLauncher::LaunchDeviceAsync(
<< device_id;
receiver_on_io_thread->OnLog(string_stream.str());
}
-
+ LOG(ERROR) << "receiver_on_io_thread: ";
// Wrap the receiver, to trampoline all its method calls from the device
// to the IO thread.
auto receiver = std::make_unique<media::VideoFrameReceiverOnTaskRunner>(
@@ -257,12 +261,14 @@ void InProcessVideoCaptureDeviceLauncher::LaunchDeviceAsync(
base::BindPostTaskToCurrentDefault(base::BindOnce(
&InProcessVideoCaptureDeviceLauncher::OnDeviceStarted,
base::Unretained(this), callbacks, std::move(done_cb)));
-
+ LOG(ERROR) << "stream type: " << stream_type;
+ LOG(ERROR) << "gum tab video capture: " << blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE;
switch (stream_type) {
- case blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE:
- // Only the Service-based device launcher is supported for device capture
- // from cameras etc.
- NOTREACHED();
+ // case blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE:
+ // // Only the Service-based device launcher is supported for device capture
+ // // from cameras etc.
+ // LOG(INFO) << "device video capture not reached";
+ // NOTREACHED();
#if BUILDFLAG(ENABLE_SCREEN_CAPTURE)
case blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE:
start_capture_closure = base::BindOnce(
@@ -274,10 +280,14 @@ void InProcessVideoCaptureDeviceLauncher::LaunchDeviceAsync(
case blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE:
case blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE:
case blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_THIS_TAB:
+ case blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE:
case blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET: {
const DesktopMediaID desktop_id = DesktopMediaID::Parse(device_id);
- if (desktop_id.is_null()) {
- DLOG(ERROR) << "Desktop media ID is null";
+ LOG(ERROR) << "desktop_id.is_null(): " << desktop_id.is_null();
+ LOG(ERROR) << "desktop_id.id: " << desktop_id.id;
+ LOG(ERROR) << "DesktopMediaID::kMacOsNativePickerId: " << DesktopMediaID::kMacOsNativePickerId;
+ if (desktop_id.is_null() && desktop_id.id != DesktopMediaID::kMacOsNativePickerId) {
+ LOG(ERROR) << "in null case";
start_capture_closure =
base::BindOnce(std::move(after_start_capture_callback), nullptr);
break;
@@ -316,8 +326,21 @@ void InProcessVideoCaptureDeviceLauncher::LaunchDeviceAsync(
break;
}
+#if defined(USE_AURA)
+ bool allow_window_id = false;
+#elif BUILDFLAG(IS_MAC)
+
+ bool allow_window_id =
+ desktop_id.id == DesktopMediaID::kMacOsNativePickerId;
+
+ LOG(ERROR) << "allow_window_id: " << allow_window_id << " desktop_id.id: " << desktop_id.id;
+#endif
+
#if defined(USE_AURA) || BUILDFLAG(IS_MAC)
- if (desktop_id.window_id != DesktopMediaID::kNullId) {
+ LOG(ERROR) << "in if block";
+ if (!allow_window_id &&
+ desktop_id.window_id != DesktopMediaID::kNullId) {
+ LOG(ERROR) << "allow_window_id: " << allow_window_id << " desktop_id.window_id: " << desktop_id.window_id;
// For the other capturers, when a bug reports the type of capture it's
// easy enough to determine which capturer was used, but it's a little
// fuzzier with window capture.
@@ -344,6 +367,7 @@ void InProcessVideoCaptureDeviceLauncher::LaunchDeviceAsync(
kMaxNumberOfBuffers, std::move(receiver),
std::move(receiver_on_io_thread)),
std::move(after_start_capture_callback));
+ LOG(ERROR) << "capture closure";
break;
}
#endif // BUILDFLAG(ENABLE_SCREEN_CAPTURE)
@@ -358,6 +382,7 @@ void InProcessVideoCaptureDeviceLauncher::LaunchDeviceAsync(
// above are NOTREACHED() then.
#if BUILDFLAG(ENABLE_SCREEN_CAPTURE)
state_ = State::DEVICE_START_IN_PROGRESS;
+ LOG(ERROR) << "start capture closure";
device_task_runner_->PostTask(FROM_HERE, std::move(start_capture_closure));
#endif
}
@@ -408,9 +433,11 @@ void InProcessVideoCaptureDeviceLauncher::OnDeviceStarted(
base::OnceClosure done_cb,
std::unique_ptr<media::VideoCaptureDevice> device) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ LOG(ERROR) << "on device started";
State state_copy = state_;
state_ = State::READY_TO_LAUNCH;
if (!device) {
+ LOG(ERROR) << "device is null";
switch (state_copy) {
case State::DEVICE_START_IN_PROGRESS:
callbacks->OnDeviceLaunchFailed(
@@ -429,13 +456,15 @@ void InProcessVideoCaptureDeviceLauncher::OnDeviceStarted(
auto launched_device = std::make_unique<InProcessLaunchedVideoCaptureDevice>(
std::move(device), device_task_runner_);
-
+ LOG(ERROR) << "launched device";
switch (state_copy) {
case State::DEVICE_START_IN_PROGRESS:
+ LOG(ERROR) << "on device started, case 1";
callbacks->OnDeviceLaunched(std::move(launched_device));
std::move(done_cb).Run();
return;
case State::DEVICE_START_ABORTING:
+ LOG(ERROR) << "on device started, case 2";
launched_device.reset();
callbacks->OnDeviceLaunchAborted();
std::move(done_cb).Run();
@@ -495,7 +524,8 @@ void InProcessVideoCaptureDeviceLauncher::DoStartDesktopCaptureOnDeviceThread(
std::unique_ptr<media::VideoCaptureDeviceClient> device_client,
ReceiveDeviceCallback result_callback) {
DCHECK(device_task_runner_->BelongsToCurrentThread());
- DCHECK(!desktop_id.is_null());
+ LOG(INFO) << "DO START DESKTOP CAPTURE ON DEVICE THREAD " << desktop_id.id;
+ DCHECK(!desktop_id.is_null() || desktop_id.id == DesktopMediaID::kMacOsNativePickerId);
std::unique_ptr<media::VideoCaptureDevice> video_capture_device;
DesktopCaptureImplementation implementation =
@@ -504,8 +534,10 @@ void InProcessVideoCaptureDeviceLauncher::DoStartDesktopCaptureOnDeviceThread(
DVLOG(1) << __func__ << " implementation " << implementation << " type "
<< desktop_id.type;
ReportDesktopCaptureImplementationAndType(implementation, desktop_id.type);
- if (video_capture_device)
+ if (video_capture_device) {
+ LOG(ERROR) << "allocate and start capture device";
video_capture_device->AllocateAndStart(params, std::move(device_client));
+ }
std::move(result_callback).Run(std::move(video_capture_device));
}
diff --git a/content/browser/renderer_host/media/in_process_video_capture_provider.cc b/content/browser/renderer_host/media/in_process_video_capture_provider.cc
index 4462efa154a2be9cc25f82688fdbc7edf71a7bb1..4207dd1a79c16df5cc972943da32699f414f81fa 100644
--- a/content/browser/renderer_host/media/in_process_video_capture_provider.cc
+++ b/content/browser/renderer_host/media/in_process_video_capture_provider.cc
@@ -9,6 +9,7 @@
#include "base/functional/bind.h"
#include "base/task/single_thread_task_runner.h"
#include "content/browser/renderer_host/media/in_process_video_capture_device_launcher.h"
+#include "content/browser/media/capture/native_screen_capture_picker.h"
namespace content {
diff --git a/content/browser/renderer_host/media/video_capture_manager.cc b/content/browser/renderer_host/media/video_capture_manager.cc
index 2949c2abd5f97e66719ec0f77cb23813865bb701..bd11a0fcdc084da00407c80e4ee229a130fdedc8 100644
--- a/content/browser/renderer_host/media/video_capture_manager.cc
+++ b/content/browser/renderer_host/media/video_capture_manager.cc
@@ -283,6 +283,7 @@ void VideoCaptureManager::QueueStartDevice(
mojo::PendingRemote<media::mojom::ReadonlyVideoEffectsManager>
readonly_video_effects_manager) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ LOG(ERROR) << "QueueStartDevice";
DCHECK(lock_time_.is_null());
device_start_request_queue_.push_back(
CaptureDeviceStartRequest(std::move(controller), session_id, params,
@@ -297,6 +298,7 @@ void VideoCaptureManager::DoStopDevice(VideoCaptureController* controller) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("video_and_image_capture"),
"VideoCaptureManager::DoStopDevice");
DCHECK(base::Contains(controllers_, controller));
+ LOG(ERROR) << "DoStopDevice called for stream type: " << controller->stream_type();
// If start request has not yet started processing, i.e. if it is not at the
// beginning of the queue, remove it from the queue.
@@ -327,6 +329,7 @@ void VideoCaptureManager::DoStopDevice(VideoCaptureController* controller) {
void VideoCaptureManager::ProcessDeviceStartRequestQueue() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ LOG(ERROR) << "ProcessDeviceStartRequestQueue";
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("video_and_image_capture"),
"VideoCaptureManager::ProcessDeviceStartRequestQueue");
auto request = device_start_request_queue_.begin();
@@ -335,7 +338,7 @@ void VideoCaptureManager::ProcessDeviceStartRequestQueue() {
scoped_refptr<VideoCaptureController> const controller =
request->controller();
-
+ LOG(ERROR) << "ProcessDeviceStartRequestQueue, controller: " << controller->device_id();
EmitLogMessage("VideoCaptureManager::ProcessDeviceStartRequestQueue", 3);
// The unit test VideoCaptureManagerTest.OpenNotExisting requires us to fail
// synchronously if the stream_type is MEDIA_DEVICE_VIDEO_CAPTURE and no
@@ -351,6 +354,7 @@ void VideoCaptureManager::ProcessDeviceStartRequestQueue() {
const media::VideoCaptureDeviceInfo* device_info =
GetDeviceInfoById(controller->device_id());
if (!device_info) {
+ LOG(ERROR) << "ProcessDeviceStartRequestQueue, device_info not found";
OnDeviceLaunchFailed(
controller.get(),
media::VideoCaptureError::
@@ -381,6 +385,7 @@ void VideoCaptureManager::ProcessDeviceStartRequestQueue() {
void VideoCaptureManager::OnDeviceLaunched(VideoCaptureController* controller) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
std::ostringstream string_stream;
+ LOG(ERROR) << "OnDeviceLaunched";
string_stream << "Launching device has succeeded. device_id = "
<< controller->device_id();
EmitLogMessage(string_stream.str(), 1);
@@ -391,7 +396,9 @@ void VideoCaptureManager::OnDeviceLaunched(VideoCaptureController* controller) {
if (blink::IsVideoDesktopCaptureMediaType(controller->stream_type())) {
const media::VideoCaptureSessionId session_id =
device_start_request_queue_.front().session_id();
+ LOG(ERROR) << "isvideodesktopcapturemediatype";
DCHECK_NE(session_id, FakeSessionId());
+ LOG(ERROR) << "maybe post desktop capture window id";
MaybePostDesktopCaptureWindowId(session_id);
}
@@ -407,7 +414,9 @@ void VideoCaptureManager::OnDeviceLaunched(VideoCaptureController* controller) {
}
}
+ LOG(ERROR) << "OnDeviceLaunched, device_start_request_queue_.pop_front";
device_start_request_queue_.pop_front();
+ LOG(ERROR) << "OnDeviceLaunched, ProcessDeviceStartRequestQueue";
ProcessDeviceStartRequestQueue();
}
@@ -467,6 +476,7 @@ void VideoCaptureManager::ConnectClient(
DCHECK_CURRENTLY_ON(BrowserThread::IO);
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("video_and_image_capture"),
"VideoCaptureManager::ConnectClient");
+ LOG(ERROR) << "ConnectClient";
{
std::ostringstream string_stream;
string_stream << "ConnectClient: session_id = " << session_id
@@ -479,6 +489,7 @@ void VideoCaptureManager::ConnectClient(
scoped_refptr<VideoCaptureController> controller =
GetOrCreateController(session_id, params);
if (!controller) {
+ LOG(ERROR) << "ConnectClient, controller not found";
std::move(done_cb).Run(nullptr);
return;
}
@@ -502,6 +513,7 @@ void VideoCaptureManager::ConnectClient(
string_stream
<< "VideoCaptureManager queueing device start for device_id = "
<< controller->device_id();
+ LOG(ERROR) << "ConnectClient, videocapturemanager queueing device start for device_id = " << controller->device_id();
EmitLogMessage(string_stream.str(), 1);
mojo::PendingRemote<video_effects::mojom::VideoEffectsProcessor>
video_effects_processor;
@@ -524,6 +536,7 @@ void VideoCaptureManager::ConnectClient(
readonly_video_effects_manager.InitWithNewPipeAndPassReceiver());
}
#endif // BUILDFLAG(ENABLE_VIDEO_EFFECTS)
+ LOG(ERROR) << "ConnectClient, videocapturemanager queueing device start for device_id = " << controller->device_id();
QueueStartDevice(session_id, controller, params,
std::move(video_effects_processor),
std::move(readonly_video_effects_manager));
diff --git a/content/public/browser/desktop_media_id.cc b/content/public/browser/desktop_media_id.cc
index b39b684ff84baaf292eef1a23b7f9fb4585023c5..55507f7a782a9472800221f1e908618a5bad2914 100644
--- a/content/public/browser/desktop_media_id.cc
+++ b/content/public/browser/desktop_media_id.cc
@@ -21,6 +21,7 @@ namespace content {
const char kScreenPrefix[] = "screen";
const char kWindowPrefix[] = "window";
+const char kNonePrefix[] = "none";
#if defined(USE_AURA) || BUILDFLAG(IS_MAC)
// static
@@ -82,6 +83,8 @@ DesktopMediaID DesktopMediaID::Parse(const std::string& str) {
type = TYPE_SCREEN;
} else if (parts[0] == kWindowPrefix) {
type = TYPE_WINDOW;
+ } else if (parts[0] == kNonePrefix) {
+ type = TYPE_NONE;
} else {
return DesktopMediaID();
}
@@ -104,6 +107,8 @@ std::string DesktopMediaID::ToString() const {
std::string prefix;
switch (type) {
case TYPE_NONE:
+ prefix = kNonePrefix;
+ break;
NOTREACHED();
case TYPE_SCREEN:
prefix = kScreenPrefix;
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index c458c1e87dc46cf4f92d5c4423ab837f29067aa0..1749fb96bbd0c69ad5c6377eabb4bbab12e3f7c3 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -356,7 +356,7 @@ BASE_FEATURE(kMacLoopbackAudioForScreenShare,
// is required to avoid recurring permission dialogs.
BASE_FEATURE(kUseSCContentSharingPicker,
"UseSCContentSharingPicker",
- base::FEATURE_DISABLED_BY_DEFAULT);
+ base::FEATURE_ENABLED_BY_DEFAULT);
#endif // BUILDFLAG(IS_MAC)
#if BUILDFLAG(IS_LINUX)
diff --git a/media/capture/video/file_video_capture_device.cc b/media/capture/video/file_video_capture_device.cc
index 5090aae198e4e0e52212eb502bb4de0475c79b04..c74887a14bcc4249e433d2afd545029d36f744bf 100644
--- a/media/capture/video/file_video_capture_device.cc
+++ b/media/capture/video/file_video_capture_device.cc
@@ -482,6 +482,7 @@ void FileVideoCaptureDevice::AllocateAndStart(
CHECK(!capture_thread_.IsRunning());
capture_thread_.Start();
+ LOG(ERROR) << "allocate and start file video capture device";
capture_thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&FileVideoCaptureDevice::OnAllocateAndStart,
@@ -604,6 +605,7 @@ void FileVideoCaptureDevice::TakePhoto(TakePhotoCallback callback) {
void FileVideoCaptureDevice::OnAllocateAndStart(
const VideoCaptureParams& params,
std::unique_ptr<VideoCaptureDevice::Client> client) {
+ LOG(ERROR) << "on allocate and start file video capture device";
DCHECK(capture_thread_.task_runner()->BelongsToCurrentThread());
client_ = std::move(client);

View File

@@ -0,0 +1,130 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Keeley Hammond <khammond@slack-corp.com>
Date: Tue, 15 Apr 2025 16:34:52 -0700
Subject: fix: debug desktop capturer picture
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 fef50841f472bf3e76c171d9e106c689157adaae..1e9ffc1e097439eea731e2cff6193c5f50c984d8 100644
--- a/content/browser/media/capture/screen_capture_kit_device_mac.mm
+++ b/content/browser/media/capture/screen_capture_kit_device_mac.mm
@@ -286,6 +286,25 @@ void OnShareableContentCreated(SCShareableContent* content) {
}
}
break;
+ case DesktopMediaID::TYPE_NONE:
+ for (SCWindow* window in content.windows) {
+ LOG(ERROR) << "Inside TYPE_NONE for loop";
+ LOG(ERROR) << "source_.id: " << source_.id;
+ LOG(ERROR) << "window.windowID: " << window.windowID;
+ if (source_.id == window.windowID) {
+ LOG(ERROR) << "window.windowID: " << window.windowID;
+ filter = [[SCContentFilter alloc]
+ initWithDesktopIndependentWindow:window];
+ CGRect frame = window.frame;
+ stream_config_content_size_ = gfx::Size(frame.size);
+ if (!fullscreen_module_) {
+ fullscreen_module_ = MaybeCreateScreenCaptureKitFullscreenModule(
+ device_task_runner_, *this, window);
+ break;
+ }
+ }
+ }
+ break;
default:
NOTREACHED();
}
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 2351919905fbec6c1bfe9ce59d799192a9e2e0e6..aa97cad0ca0fecc8dfc1ee44760ab1d36086785c 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
@@ -104,6 +104,7 @@ void IncrementDesktopCaptureCounters(const DesktopMediaID& device_id) {
: SCREEN_CAPTURER_CREATED_WITHOUT_AUDIO);
break;
case DesktopMediaID::TYPE_WINDOW:
+ case DesktopMediaID::TYPE_NONE:
IncrementDesktopCaptureCounter(WINDOW_CAPTURER_CREATED);
break;
case DesktopMediaID::TYPE_WEB_CONTENTS:
@@ -112,8 +113,8 @@ void IncrementDesktopCaptureCounters(const DesktopMediaID& device_id) {
device_id.audio_share ? TAB_VIDEO_CAPTURER_CREATED_WITH_AUDIO
: TAB_VIDEO_CAPTURER_CREATED_WITHOUT_AUDIO);
break;
- case DesktopMediaID::TYPE_NONE:
- NOTREACHED();
+ // case DesktopMediaID::TYPE_NONE:
+ // NOTREACHED();
}
}
@@ -285,13 +286,15 @@ void InProcessVideoCaptureDeviceLauncher::LaunchDeviceAsync(
const DesktopMediaID desktop_id = DesktopMediaID::Parse(device_id);
LOG(ERROR) << "desktop_id.is_null(): " << desktop_id.is_null();
LOG(ERROR) << "desktop_id.id: " << desktop_id.id;
- LOG(ERROR) << "DesktopMediaID::kMacOsNativePickerId: " << DesktopMediaID::kMacOsNativePickerId;
- if (desktop_id.is_null() && desktop_id.id != DesktopMediaID::kMacOsNativePickerId) {
- LOG(ERROR) << "in null case";
- start_capture_closure =
- base::BindOnce(std::move(after_start_capture_callback), nullptr);
- break;
- }
+ // TODO(Keeley): kMacOsNativePickerId seems to have been removed upstream.
+ // Reconcile this with the upstream changes.
+ // LOG(ERROR) << "DesktopMediaID::kMacOsNativePickerId: " << DesktopMediaID::kMacOsNativePickerId;
+ // if (desktop_id.is_null() && desktop_id.id != DesktopMediaID::kMacOsNativePickerId) {
+ // LOG(ERROR) << "in null case";
+ // start_capture_closure =
+ // base::BindOnce(std::move(after_start_capture_callback), nullptr);
+ // break;
+ // }
if (desktop_id.id == DesktopMediaID::kFakeId) {
start_capture_closure = base::BindOnce(
@@ -329,18 +332,16 @@ void InProcessVideoCaptureDeviceLauncher::LaunchDeviceAsync(
#if defined(USE_AURA)
bool allow_window_id = false;
#elif BUILDFLAG(IS_MAC)
-
- bool allow_window_id =
- desktop_id.id == DesktopMediaID::kMacOsNativePickerId;
-
- LOG(ERROR) << "allow_window_id: " << allow_window_id << " desktop_id.id: " << desktop_id.id;
+ // bool allow_window_id =
+ // desktop_id.id == DesktopMediaID::kMacOsNativePickerId;
+ // LOG(ERROR) << "allow_window_id: " << allow_window_id << " desktop_id.id: " << desktop_id.id;
#endif
#if defined(USE_AURA) || BUILDFLAG(IS_MAC)
- LOG(ERROR) << "in if block";
- if (!allow_window_id &&
- desktop_id.window_id != DesktopMediaID::kNullId) {
- LOG(ERROR) << "allow_window_id: " << allow_window_id << " desktop_id.window_id: " << desktop_id.window_id;
+ // if (!allow_window_id &&
+ // desktop_id.window_id != DesktopMediaID::kNullId) {
+ if (desktop_id.window_id != DesktopMediaID::kNullId) {
+ // LOG(ERROR) << "allow_window_id: " << allow_window_id << " desktop_id.window_id: " << desktop_id.window_id;
// For the other capturers, when a bug reports the type of capture it's
// easy enough to determine which capturer was used, but it's a little
// fuzzier with window capture.
@@ -525,7 +526,7 @@ void InProcessVideoCaptureDeviceLauncher::DoStartDesktopCaptureOnDeviceThread(
ReceiveDeviceCallback result_callback) {
DCHECK(device_task_runner_->BelongsToCurrentThread());
LOG(INFO) << "DO START DESKTOP CAPTURE ON DEVICE THREAD " << desktop_id.id;
- DCHECK(!desktop_id.is_null() || desktop_id.id == DesktopMediaID::kMacOsNativePickerId);
+ // DCHECK(!desktop_id.is_null() || desktop_id.id == DesktopMediaID::kMacOsNativePickerId);
std::unique_ptr<media::VideoCaptureDevice> video_capture_device;
DesktopCaptureImplementation implementation =
diff --git a/content/browser/renderer_host/media/video_capture_manager.cc b/content/browser/renderer_host/media/video_capture_manager.cc
index bd11a0fcdc084da00407c80e4ee229a130fdedc8..74d9ce9facea9cda90073ca6b70e8c4be3b62f6f 100644
--- a/content/browser/renderer_host/media/video_capture_manager.cc
+++ b/content/browser/renderer_host/media/video_capture_manager.cc
@@ -920,6 +920,8 @@ void VideoCaptureManager::DestroyControllerIfNoClients(
// closed.
CloseNativeScreenCapturePicker(
DesktopMediaID::Parse(controller->device_id()));
+ LOG(ERROR) << "Closing native screen capture picker";
+ LOG(ERROR) << "VideoCaptureManager: " << string_stream.str();
// The VideoCaptureController is removed from |controllers_| immediately.
// The controller is deleted immediately, and the device is freed

View File

@@ -9,6 +9,8 @@
#include <vector>
#include "base/containers/flat_map.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/media/webrtc/desktop_capturer_wrapper.h"
@@ -18,6 +20,7 @@
#include "content/public/browser/desktop_capture.h"
#include "gin/handle.h"
#include "gin/object_template_builder.h"
#include "media/base/media_switches.h"
#include "shell/browser/javascript_environment.h"
#include "shell/common/api/electron_api_native_image.h"
#include "shell/common/gin_converters/gfx_converter.h"
@@ -143,6 +146,21 @@ base::flat_map<int32_t, uint32_t> MonitorAtomIdToDisplayId() {
}
#endif
std::unique_ptr<ThumbnailCapturer> MakeScreenAndWindowCapturer() {
#if BUILDFLAG(IS_MAC)
if (ShouldUseThumbnailCapturerMac(DesktopMediaList::Type::kNone)) {
LOG(ERROR) << "creating thumbnail capturer, knone *****";
return CreateThumbnailCapturerMac(DesktopMediaList::Type::kNone);
}
#endif // BUILDFLAG(IS_MAC)
LOG(ERROR) << "should not hit this *****";
std::unique_ptr<webrtc::DesktopCapturer> window_capturer =
content::desktop_capture::CreateWindowCapturer();
return window_capturer ? std::make_unique<DesktopCapturerWrapper>(
std::move(window_capturer))
: nullptr;
}
std::unique_ptr<ThumbnailCapturer> MakeWindowCapturer() {
#if BUILDFLAG(IS_MAC)
if (ShouldUseThumbnailCapturerMac(DesktopMediaList::Type::kWindow)) {
@@ -258,7 +276,8 @@ void DesktopCapturer::DesktopListListener::OnDelegatedSourceListDismissed() {
void DesktopCapturer::StartHandling(bool capture_window,
bool capture_screen,
const gfx::Size& thumbnail_size,
bool fetch_window_icons) {
bool fetch_window_icons,
bool use_system_picker) {
fetch_window_icons_ = fetch_window_icons;
#if BUILDFLAG(IS_WIN)
if (content::desktop_capture::CreateDesktopCaptureOptions()
@@ -272,35 +291,76 @@ void DesktopCapturer::StartHandling(bool capture_window,
// clear any existing captured sources.
captured_sources_.clear();
LOG(ERROR) << "capture_window: " << capture_window << " capture_screen: " << capture_screen;
if (capture_window && capture_screen) {
// Some capturers like PipeWire support a single capturer for both screens
// and windows. Use it if possible, treating both as window capture
std::unique_ptr<webrtc::DesktopCapturer> desktop_capturer =
webrtc::DesktopCapturer::CreateGenericCapturer(
content::desktop_capture::CreateDesktopCaptureOptions());
auto capturer = desktop_capturer ? std::make_unique<DesktopCapturerWrapper>(
std::move(desktop_capturer))
: nullptr;
if (capturer && capturer->GetDelegatedSourceListController()) {
capture_screen_ = false;
if (IsDisplayMediaSystemPickerAvailable()) {
auto capturer = MakeScreenAndWindowCapturer();
capture_screen_ = capture_screen;
capture_window_ = capture_window;
window_capturer_ = std::make_unique<NativeDesktopMediaList>(
DesktopMediaList::Type::kWindow, std::move(capturer), true, true);
window_capturer_->SetThumbnailSize(thumbnail_size);
// TODO(review): Maybe just call this a capturer
screen_capturer_ = std::make_unique<NativeDesktopMediaList>(
DesktopMediaList::Type::kNone, std::move(capturer), true, true);
screen_capturer_->SetThumbnailSize(thumbnail_size);
screen_capturer_->ShowDelegatedList();
#if BUILDFLAG(IS_MAC)
screen_capturer_->skip_next_refresh_ =
ShouldUseThumbnailCapturerMac(DesktopMediaList::Type::kNone) ? 2 : 0;
LOG(ERROR) << "SET SKIP NEXT REFRESH TO 2";
#endif
LOG(ERROR) << "screen_capturer_->skip_next_refresh_ " << screen_capturer_->skip_next_refresh_;
OnceCallback update_callback = base::BindOnce(
&DesktopCapturer::UpdateSourcesList, weak_ptr_factory_.GetWeakPtr(),
window_capturer_.get());
OnceCallback failure_callback = base::BindOnce(
&DesktopCapturer::HandleFailure, weak_ptr_factory_.GetWeakPtr());
screen_capturer_.get());
window_listener_ = std::make_unique<DesktopListListener>(
std::move(update_callback), std::move(failure_callback),
thumbnail_size.IsEmpty());
window_capturer_->StartUpdating(window_listener_.get());
// Needed to force a refresh for the native MacOS Picker
OnceCallback wrapped_update_callback = base::BindOnce(
&DesktopCapturer::RequestUpdate, weak_ptr_factory_.GetWeakPtr(),
screen_capturer_.get(), std::move(update_callback));
if (screen_capturer_->IsSourceListDelegated()) {
OnceCallback failure_callback = base::BindOnce(
&DesktopCapturer::HandleFailure, weak_ptr_factory_.GetWeakPtr());
screen_listener_ = std::make_unique<DesktopListListener>(
std::move(wrapped_update_callback), std::move(failure_callback),
thumbnail_size.IsEmpty());
screen_capturer_->StartUpdating(screen_listener_.get());
} else {
screen_capturer_->Update(std::move(update_callback),
/* refresh_thumbnails = */ true);
}
return;
} else {
// Some capturers like PipeWire support a single capturer for both screens
// and windows. Use it if possible, treating both as window capture
std::unique_ptr<webrtc::DesktopCapturer> desktop_capturer =
webrtc::DesktopCapturer::CreateGenericCapturer(
content::desktop_capture::CreateDesktopCaptureOptions());
auto capturer = desktop_capturer
? std::make_unique<DesktopCapturerWrapper>(
std::move(desktop_capturer))
: nullptr;
if (capturer && capturer->GetDelegatedSourceListController()) {
capture_screen_ = false;
capture_window_ = capture_window;
window_capturer_ = std::make_unique<NativeDesktopMediaList>(
DesktopMediaList::Type::kWindow, std::move(capturer));
window_capturer_->SetThumbnailSize(thumbnail_size);
OnceCallback update_callback = base::BindOnce(
&DesktopCapturer::UpdateSourcesList, weak_ptr_factory_.GetWeakPtr(),
window_capturer_.get());
OnceCallback failure_callback = base::BindOnce(
&DesktopCapturer::HandleFailure, weak_ptr_factory_.GetWeakPtr());
window_listener_ = std::make_unique<DesktopListListener>(
std::move(update_callback), std::move(failure_callback),
thumbnail_size.IsEmpty());
window_capturer_->StartUpdating(window_listener_.get());
return;
}
}
}
@@ -319,21 +379,30 @@ void DesktopCapturer::StartHandling(bool capture_window,
// Initialize the source list.
// Apply the new thumbnail size and restart capture.
if (capture_window) {
LOG(ERROR) << "WE SHOULDNT BE HERE *****";
auto capturer = MakeWindowCapturer();
if (capturer) {
window_capturer_ = std::make_unique<NativeDesktopMediaList>(
DesktopMediaList::Type::kWindow, std::move(capturer), true, true);
window_capturer_->SetThumbnailSize(thumbnail_size);
#if BUILDFLAG(IS_MAC)
window_capturer_->skip_next_refresh_ =
ShouldUseThumbnailCapturerMac(DesktopMediaList::Type::kWindow) ? 2
: 0;
#endif
OnceCallback update_callback = base::BindOnce(
&DesktopCapturer::UpdateSourcesList, weak_ptr_factory_.GetWeakPtr(),
window_capturer_.get());
#if BUILDFLAG(IS_MAC)
window_capturer_->skip_next_refresh_ =
ShouldUseThumbnailCapturerMac(DesktopMediaList::Type::kWindow) ? 2
: 0;
if (base::FeatureList::IsEnabled(media::kUseSCContentSharingPicker)) {
window_capturer_->ShowDelegatedList();
// Needed to force a refresh for the native MacOS Picker
update_callback = base::BindOnce(
&DesktopCapturer::RequestUpdate, weak_ptr_factory_.GetWeakPtr(),
window_capturer_.get(), std::move(update_callback));
}
#endif
if (window_capturer_->IsSourceListDelegated()) {
OnceCallback failure_callback = base::BindOnce(
&DesktopCapturer::HandleFailure, weak_ptr_factory_.GetWeakPtr());
@@ -354,16 +423,25 @@ void DesktopCapturer::StartHandling(bool capture_window,
screen_capturer_ = std::make_unique<NativeDesktopMediaList>(
DesktopMediaList::Type::kScreen, std::move(capturer));
screen_capturer_->SetThumbnailSize(thumbnail_size);
#if BUILDFLAG(IS_MAC)
screen_capturer_->skip_next_refresh_ =
ShouldUseThumbnailCapturerMac(DesktopMediaList::Type::kScreen) ? 2
: 0;
#endif
OnceCallback update_callback = base::BindOnce(
&DesktopCapturer::UpdateSourcesList, weak_ptr_factory_.GetWeakPtr(),
screen_capturer_.get());
#if BUILDFLAG(IS_MAC)
screen_capturer_->skip_next_refresh_ =
ShouldUseThumbnailCapturerMac(DesktopMediaList::Type::kScreen) ? 2
: 0;
if (base::FeatureList::IsEnabled(media::kUseSCContentSharingPicker)) {
screen_capturer_->ShowDelegatedList();
// Needed to force a refresh for the native MacOS Picker
update_callback = base::BindOnce(
&DesktopCapturer::RequestUpdate, weak_ptr_factory_.GetWeakPtr(),
screen_capturer_.get(), std::move(update_callback));
}
#endif
if (screen_capturer_->IsSourceListDelegated()) {
OnceCallback failure_callback = base::BindOnce(
&DesktopCapturer::HandleFailure, weak_ptr_factory_.GetWeakPtr());
@@ -380,9 +458,15 @@ void DesktopCapturer::StartHandling(bool capture_window,
}
}
void DesktopCapturer::RequestUpdate(DesktopMediaList* list,
OnceCallback update_callback) {
list->Update(std::move(update_callback));
}
void DesktopCapturer::UpdateSourcesList(DesktopMediaList* list) {
if (capture_window_ &&
list->GetMediaListType() == DesktopMediaList::Type::kWindow) {
(list->GetMediaListType() == DesktopMediaList::Type::kWindow ||
list->GetMediaListType() == DesktopMediaList::Type::kNone)) {
capture_window_ = false;
std::vector<DesktopCapturer::Source> window_sources;
window_sources.reserve(list->GetSourceCount());
@@ -395,7 +479,8 @@ void DesktopCapturer::UpdateSourcesList(DesktopMediaList* list) {
}
if (capture_screen_ &&
list->GetMediaListType() == DesktopMediaList::Type::kScreen) {
(list->GetMediaListType() == DesktopMediaList::Type::kScreen ||
list->GetMediaListType() == DesktopMediaList::Type::kNone)) {
capture_screen_ = false;
std::vector<DesktopCapturer::Source> screen_sources;
screen_sources.reserve(list->GetSourceCount());
@@ -465,7 +550,6 @@ void DesktopCapturer::UpdateSourcesList(DesktopMediaList* list) {
std::move(screen_sources.begin(), screen_sources.end(),
std::back_inserter(captured_sources_));
}
if (!capture_window_ && !capture_screen_)
HandleSuccess();
}

View File

@@ -41,7 +41,8 @@ class DesktopCapturer final : public gin::Wrappable<DesktopCapturer>,
void StartHandling(bool capture_window,
bool capture_screen,
const gfx::Size& thumbnail_size,
bool fetch_window_icons);
bool fetch_window_icons,
bool use_system_picker);
// gin::Wrappable
static gin::WrapperInfo kWrapperInfo;
@@ -94,6 +95,7 @@ class DesktopCapturer final : public gin::Wrappable<DesktopCapturer>,
bool have_thumbnail_ = false;
};
void RequestUpdate(DesktopMediaList* list, OnceCallback update_callback);
void UpdateSourcesList(DesktopMediaList* list);
void HandleFailure();
void HandleSuccess();

View File

@@ -26,6 +26,27 @@ std::string EnablePlatformSpecificFeatures() {
#else
return "ScreenCaptureKitPickerScreen,ScreenCaptureKitStreamPickerSonoma,"
"ThumbnailCapturerMac:capture_mode/sc_screenshot_manager";
#endif
}
if (@available(macOS 15.0, *)) {
// These flags aren't exported so reference them by name directly, they are
// used to ensure that screen and window capture exclusive use
// ScreenCaptureKit APIs to avoid warning dialogs on macOS 15.0 and higher.
// kScreenCaptureKitPickerScreen,
// chrome/browser/media/webrtc/thumbnail_capturer_mac.mm
// kScreenCaptureKitStreamPickerSonoma,
// chrome/browser/media/webrtc/thumbnail_capturer_mac.mm
// kThumbnailCapturerMac,
// chrome/browser/media/webrtc/thumbnail_capturer_mac.mm
// kUseSCContentSharingPicker,
// chrome/browser/media/webrtc/thumbnail_capturer_mac.mm
#if DCHECK_IS_ON()
return "ScreenCaptureKitPickerScreen,ScreenCaptureKitStreamPickerSonoma,"
"UseSCContentSharingPicker";
#else
return "ScreenCaptureKitPickerScreen,ScreenCaptureKitStreamPickerSonoma,"
"UseSCContentSharingPicker,"
"ThumbnailCapturerMac:capture_mode/sc_screenshot_manager";
#endif
}
return "";

View File

@@ -0,0 +1,30 @@
// Copyright (c) 2021 Slack Technologies, LLC.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/common/gin_converters/display_surface_converter.h"
#include "content/public/browser/media_stream_request.h"
#include "gin/data_object_builder.h"
#include "shell/common/gin_converters/frame_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
namespace gin {
v8::Local<v8::Value> Converter<blink::mojom::PreferredDisplaySurface>::ToV8(
v8::Isolate* isolate,
blink::mojom::PreferredDisplaySurface type) {
switch (type) {
case blink::mojom::PreferredDisplaySurface::NO_PREFERENCE:
return StringToV8(isolate, "no_preference");
case blink::mojom::PreferredDisplaySurface::MONITOR:
return StringToV8(isolate, "monitor");
case blink::mojom::PreferredDisplaySurface::WINDOW:
return StringToV8(isolate, "window");
default:
return StringToV8(isolate, "unknown");
}
}
} // namespace gin

View File

@@ -0,0 +1,36 @@
// Copyright (c) 2024 Slack, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ELECTRON_SHELL_BROWSER_API_DISPLAY_SOURCE_CONVERTER_H_
#define ELECTRON_SHELL_BROWSER_API_DISPLAY_SOURCE_CONVERTER_H_
#include <memory>
#include <string>
#include "gin/converter.h"
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
namespace electron {
// cf:
// Chrome also allows browser here, but Electron does not.
enum PreferredDisplaySurface {
NO_PREFERENCE,
MONITOR,
WINDOW,
};
} // namespace electron
namespace gin {
template <>
struct Converter<blink::mojom::PreferredDisplaySurface> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
blink::mojom::PreferredDisplaySurface val);
};
} // namespace gin
#endif // ELECTRON_SHELL_BROWSER_DISPLAY_SOURCE_CONVERTER_H_

View File

@@ -7,6 +7,7 @@
#include "content/public/browser/media_stream_request.h"
#include "content/public/browser/render_frame_host.h"
#include "gin/data_object_builder.h"
#include "shell/common/gin_converters/display_surface_converter.h"
#include "shell/common/gin_converters/frame_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
@@ -22,6 +23,7 @@ v8::Local<v8::Value> Converter<content::MediaStreamRequest>::ToV8(
.Set("frame", rfh)
.Set("securityOrigin", request.security_origin)
.Set("userGesture", request.user_gesture)
.Set("preferredDisplaySurface", request.preferred_display_surface)
.Set("videoRequested",
request.video_type != blink::mojom::MediaStreamType::NO_SERVICE)
.Set("audioRequested",

View File

@@ -149,6 +149,10 @@ declare namespace Electron {
_setDisplayMediaRequestHandler: Electron.Session['setDisplayMediaRequestHandler'];
}
interface DisplayMediaRequestHandlerOpts {
userSystemPicker: boolean,
}
type CreateWindowFunction = (options: BrowserWindowConstructorOptions) => WebContents;
interface Menu {
@@ -262,7 +266,7 @@ declare namespace Electron {
declare namespace ElectronInternal {
interface DesktopCapturer {
startHandling(captureWindow: boolean, captureScreen: boolean, thumbnailSize: Electron.Size, fetchWindowIcons: boolean): void;
startHandling(captureWindow: boolean, captureScreen: boolean, thumbnailSize: Electron.Size, fetchWindowIcons: boolean, useSystemPicker: boolean): void;
_onerror?: (error: string) => void;
_onfinished?: (sources: Electron.DesktopCapturerSource[], fetchWindowIcons: boolean) => void;
}