Thanks for being a part of WWDC25!

How did we do? We’d love to know your thoughts on this year’s conference. Take the survey here

ScreenCaptureKit confuses virtual displays

If there are multiple virtual displays connected when an app starts using SCStream, then if there is any change in the configuration of connected virtual screens (one of them gets disconnected, reconnected, etc), a new SCStream will always stream the content of the last connected virtual screen, no matter which virtual screen is intended to be streamed.

This happens despite the fact that the SCContentFilter is properly configured for the stream, with the filter's content having the right displayID with the proper frame in the global display coordinate system and the filter also confirms that it knows the proper contentRect size and pointPixelScale.

When all virtual displays are disconnected and reconnected, things return to normal - it's as is SCStream somehow gets confused and can't properly handle or internally reenumerate multiple virtual screens until none is connected.

This issue does not normally come up, as most users will probably have only one virtual displays (like a Sidecar display) connected at most. However some configurations (like systems using multiple DisplayLink displays or apps using the undocumented CGVirtualDisplay to create virtual screens) encounter this issue.

Note: this is a longstanding problem, has been like this from the first introduction of ScreenCaptureKit and even before, affected CGDisplayStream which similarly confused virtual screens.

Answered by Steve4442 in 841969022

Of course, thank you!

FB17797423


I also added some more details and info about how to reproduce the issue quickly with the app BetterDisplay which contains the feature of both creating virtual screens and showing their content in Picture in Picture windows. One can consult this GitHub issue/discussion about reproducing the problem quickly (and get a bit more info):

https://github.com/waydabber/BetterDisplay/issues/4405

Quick steps to reproduce the issue with BetterDisplay installed:

Steps to reproduce:

(0. Enable Screen Recording permissions for the app.)

  1. Set up two virtual screens (app Settings - gear icon - > Displays > Overview > Create New Virtual Screen), name them ("Virtual 1" and "Virtual 2").
  2. In the app menu, connect the created virtual screens (toggle in the header), and then launch a Picture in Picture window (this uses SCStream) for both screens (see the Picture in Picture menu item in the app menu for the virtual screens).
  3. Show some content on the virtual screens or use "Identify Visually" (quick way: OPTION + Click on the header in the virtual screen’s header in the menu block) to confirm that all is well at this point.
  4. Disconnect "Virtual 1" (using the toggle in the menu header)
  5. Reconnect "Virtual 1" (using the toggle in the menu header)
  6. Reopen the PIP window for "Virtual 1"
  7. Confirm that the PIP window "Virtual 1" shows the content of "Virtual 2"

I am happy to provide more info and share the relevant code or the app is using to reproduce the issue in XCode (but regarding SCStream, everything is done according to the book, so there is not much to share).

I think the root cause is that whatever SCStream (and previously CGDisplayStream) relies upon ends up working with the wrong framebuffer (?) in certain cases when virtual screens are involved (note: interestingly, if confused virtual screens have different dimensions/resolutions, the streamed content is clipped, so the dimensions reported by SCContentFilter is properly taken into account - the native macOS Screen Sharing menubar extra item also shows the wrong, clipped content - to me this shows that the ScreenCaptureKit API at some higher lever tries to do the right thing).

This sounds eminently bugworthy. See Bug Reporting: How and Why? for advice on how to file a bug about it.

Please post your bug number, just for the record.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Of course, thank you!

FB17797423


I also added some more details and info about how to reproduce the issue quickly with the app BetterDisplay which contains the feature of both creating virtual screens and showing their content in Picture in Picture windows. One can consult this GitHub issue/discussion about reproducing the problem quickly (and get a bit more info):

https://github.com/waydabber/BetterDisplay/issues/4405

Quick steps to reproduce the issue with BetterDisplay installed:

Steps to reproduce:

(0. Enable Screen Recording permissions for the app.)

  1. Set up two virtual screens (app Settings - gear icon - > Displays > Overview > Create New Virtual Screen), name them ("Virtual 1" and "Virtual 2").
  2. In the app menu, connect the created virtual screens (toggle in the header), and then launch a Picture in Picture window (this uses SCStream) for both screens (see the Picture in Picture menu item in the app menu for the virtual screens).
  3. Show some content on the virtual screens or use "Identify Visually" (quick way: OPTION + Click on the header in the virtual screen’s header in the menu block) to confirm that all is well at this point.
  4. Disconnect "Virtual 1" (using the toggle in the menu header)
  5. Reconnect "Virtual 1" (using the toggle in the menu header)
  6. Reopen the PIP window for "Virtual 1"
  7. Confirm that the PIP window "Virtual 1" shows the content of "Virtual 2"

I am happy to provide more info and share the relevant code or the app is using to reproduce the issue in XCode (but regarding SCStream, everything is done according to the book, so there is not much to share).

I think the root cause is that whatever SCStream (and previously CGDisplayStream) relies upon ends up working with the wrong framebuffer (?) in certain cases when virtual screens are involved (note: interestingly, if confused virtual screens have different dimensions/resolutions, the streamed content is clipped, so the dimensions reported by SCContentFilter is properly taken into account - the native macOS Screen Sharing menubar extra item also shows the wrong, clipped content - to me this shows that the ScreenCaptureKit API at some higher lever tries to do the right thing).

ScreenCaptureKit confuses virtual displays
 
 
Q