Skip to content

Publish a broadcast started from the extension instead of presenting the picker - #1145

Merged
hiroshihorie merged 2 commits into
mainfrom
hiroshi/broadcast-start-publishes-extension
Oct 6, 2026
Merged

hiroshihorie merged 2 commits into
mainfrom
hiroshi/broadcast-start-publishes-extension

Conversation

@hiroshihorie

Copy link
Copy Markdown
Member

Stacked on #1142.

On iOS 27 with default options, a broadcast started from Control Center never reaches the broadcast extension path. broadcastStateChanged calls setScreenShare(enabled: true), which takes the ScreenCaptureKit branch first because useScreenCaptureKit defaults to true. What happens next depends on the app state when the broadcast starts:

  • App active (Control Center closed during the countdown): the in-app SCK picker shows up, and Cancel or the 60s timeout fails the publish.
  • App inactive (Control Center still open): no picker shows, and the system cancels it about 2s later.

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 setScreenShare calls keep using ScreenCaptureKit, so a stale isBroadcasting (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 BroadcastScreenCapturer track is published, the receiver connects, frames are sent, and there is no picker and no requestStop. Unit tests cover the routing helper, since the SCK branch is compiled out on the simulator.

@hiroshihorie
hiroshihorie marked this pull request as ready for review October 6, 2026 05:39

@devin-ai-integration devin-ai-integration Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Devin Review

}
do {
try await setScreenShare(enabled: true)
try await set(source: .screenShareVideo, enabled: true, captureOptions: nil, publishOptions: nil, forBroadcastStart: true)

@devin-ai-integration devin-ai-integration Bot Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@pblazej
pblazej added this pull request to stack #1146 October 6, 2026 09:08
Base automatically changed from blaze/broadcast-fixes to main October 6, 2026 11:41
…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.
@pblazej
pblazej force-pushed the hiroshi/broadcast-start-publishes-extension branch from 28c19cf to 905d996 Compare October 6, 2026 11:41
devin-ai-integration[bot]

This comment was marked as resolved.

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.
@hiroshihorie
hiroshihorie merged commit d885f9e into main Oct 6, 2026
33 checks passed
@hiroshihorie
hiroshihorie deleted the hiroshi/broadcast-start-publishes-extension branch October 6, 2026 14:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants