Repository navigation
Publish a broadcast started from the extension instead of presenting the picker - #1145
Conversation
There was a problem hiding this comment.
Note
Newer findings are available below. Devin Review posted a newer report on this PR, in addition to the findings presented here.
Devin Review found 2 potential issues.
1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)
| } | ||
| do { | ||
| try await setScreenShare(enabled: true) | ||
| try await set(source: .screenShareVideo, enabled: true, captureOptions: nil, publishOptions: nil, forBroadcastStart: true) |
There was a problem hiding this comment.
🟡 Existing screen share strands new broadcast
When an active ScreenCaptureKit share precedes a broadcast, set reuses its publication instead of attaching a broadcast receiver. The extension cannot deliver its frames and ends the broadcast.
Learn more
A screen share started through setScreenShare(enabled: true) on iOS 27 normally uses an in-process ScreenCaptureKit capturer. The automatic broadcast callback calls the new set overload, but set returns any existing screen-share publication before reaching the broadcast routing branch. The extension connects to a socket owned by a BroadcastScreenCapturer; an existing ScreenCaptureKit track does not create that receiver. createUploader fails when no receiver exists, so the newly started broadcast has no frames delivered and closes.
Example: Alice starts an explicit ScreenCaptureKit share, then starts the app's broadcast from Control Center. The callback returns Alice's current publication; the broadcast extension finds no broadcast receiver instead of publishing its samples.
Recommended fix: Handle an existing ScreenCaptureKit publication explicitly in the broadcast-start path. Either replace it with a broadcast capturer before returning success, or prevent the extension broadcast from starting when it cannot be consumed.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
This path predates the PR. The broadcast-start publish already returned an existing screen share publication before routing. With a live ScreenCaptureKit share, that share keeps running and only the second broadcast ends, so nothing is lost. Replacing the track on broadcast start is a separate design change, tracking it as a follow-up.
…the picker On iOS 27 with default options, a broadcast started from Control Center made the automatic screen share publish take the ScreenCaptureKit branch, since useScreenCaptureKit defaults to true. The picker then either showed up in the app or was cancelled by the system while Control Center was still open, and the failure stopped the broadcast. The publish triggered by a broadcast start now uses Broadcast Capture when the room is set up for the extension. Explicit screen shares keep using ScreenCaptureKit, so a stale broadcast state cannot turn them into a track that never receives frames.
28c19cf to
905d996
Compare
The publish triggered by a broadcast start can wait in the serial runner behind another publish. If the broadcast stopped in the meantime, routing read isBroadcasting as false and fell back to the ScreenCaptureKit picker, or to requestActivation on the ReplayKit path, for a broadcast that no longer exists. The job now returns early instead.
Stacked on #1142.
On iOS 27 with default options, a broadcast started from Control Center never reaches the broadcast extension path.
broadcastStateChangedcallssetScreenShare(enabled: true), which takes the ScreenCaptureKit branch first becauseuseScreenCaptureKitdefaults totrue. What happens next depends on the app state when the broadcast starts:On main the failure is only logged, so the broadcast keeps running and nothing gets published. With #1142 the failure calls
requestStop(), so the broadcast ends.This change makes the publish triggered by a broadcast start use Broadcast Capture when the room is set up for the extension. Explicit
setScreenSharecalls keep using ScreenCaptureKit, so a staleisBroadcasting(for example after the extension is killed without posting stopped) can't turn them into a track that never receives frames.Verified on an iPhone 17 Pro Max on iOS 27.0.1 with the example app and its extension, in both Control Center cases: the
BroadcastScreenCapturertrack is published, the receiver connects, frames are sent, and there is no picker and norequestStop. Unit tests cover the routing helper, since the SCK branch is compiled out on the simulator.