Skip to content

feat: capture the screen in-app with ScreenCaptureKit (drop ReplayKit extension) - #14

Merged
joeblau merged 3 commits into
mainfrom
feat/in-app-screencapturekit-capture
Jul 4, 2026
Merged

feat: capture the screen in-app with ScreenCaptureKit (drop ReplayKit extension)#14
joeblau merged 3 commits into
mainfrom
feat/in-app-screencapturekit-capture

Conversation

@joeblau

@joeblau joeblau commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Summary

Replaces the ReplayKit broadcast upload extension with an in-app iOS 27 ScreenCaptureKit pipeline. Capture, compositing, and publishing now all run in the host process, removing the fragile cross-process keepalive machinery the extension required.

What changed

Capture

  • New ScreenCaptureController drives SCContentSharingPicker + SCStream, paces the native-refresh feed down to the target frame rate by PTS deadline, composites the optional facecam, and feeds the existing publishers. Simulator/preview builds fall back to a device-only stub.
  • The RTMP/SRT/WHIP publishers, NetworkPathMonitor, and facecam capture/compositor are process-agnostic, so they now compile directly into the app target instead of the extension.

Removals

  • Deletes the StreamBroadcast extension target and SampleHandler, the AudioSessionConfigurator, and the host-app BroadcastKeepAlive/BroadcastMonitor/BroadcastPickerView machinery that existed only to keep the suspended extension alive.

App configuration

  • Bumps the deployment target to iOS 27, adds the screen-capture background mode + NSScreenCaptureUsageDescription, and enables landscape orientations.

Tests

  • Adds a StreamCoreTests unit-test target (Swift Testing) covering the Keychain store, protocol handling, settings codable/backup/publish logic, and cross-process contract keys; enables testability on the StreamCore Debug config and wires the target into the scheme's test action.

Misc

  • Adds a Haptics helper for consistent control feedback and rewrites the README around the in-app flow.
  • Advances the vendored HaishinKit submodule pointer to include VideoToolbox invalid-session recovery, so a compression session invalidated during a background transition is rebuilt instead of failing every frame with kVTInvalidSessionErr.

Notes

  • HaishinKit is a local-path SPM package pinned to the submodule. The submodule fix was committed and pushed to the fork branch fix/rtmp-transient-send-keepopen (407adcf4) so CI's recursive checkout resolves it.
  • ScreenCaptureKit's iOS 27 module is device-only; the #else stub keeps the simulator build (and CI) green.

🤖 Generated with Claude Code

joeblau and others added 3 commits July 4, 2026 10:18
… extension)

Replace the ReplayKit broadcast upload extension with an in-app iOS 27
ScreenCaptureKit pipeline. Capture, compositing, and publishing now all
run in the host process, which removes the fragile cross-process dance
the extension required.

Capture
- ScreenCaptureController drives SCContentSharingPicker + SCStream,
  paces the native-refresh feed down to the target frame rate by PTS
  deadline, composites the optional facecam, and feeds the existing
  publishers. Simulator/preview builds get a device-only stub.
- The RTMP/SRT/WHIP publishers, network path monitor, and facecam
  capture/compositor are process-agnostic, so they now compile directly
  into the app target instead of the extension.

Removals
- Delete the broadcast extension target and SampleHandler, the
  AudioSessionConfigurator, and the host-app keepalive/monitor/picker
  machinery that existed only to keep the suspended extension alive.

App configuration
- Bump the deployment target to iOS 27, add the screen-capture
  background mode and NSScreenCaptureUsageDescription, and allow
  landscape orientations for landscape capture.

Tests
- Add a StreamCoreTests unit-test target (Swift Testing) covering the
  Keychain store, protocol handling, settings codable/backup/publish
  logic, and the cross-process contract keys; enable testability on the
  StreamCore Debug config.

Misc
- Add a Haptics helper for consistent control feedback and rewrite the
  README around the in-app capture flow.
- Advance the vendored HaishinKit pointer to include VideoToolbox
  invalid-session recovery, so a compression session invalidated during
  a background transition is rebuilt instead of failing every frame.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
AVAudioSession.activate(options:)/deactivate(options:) are watchOS-only and
fail to compile on iOS ('activate(options:)' is unavailable in iOS). Replace
all four call sites (AudioInputProvider, SettingsView, and ScreenCaptureController)
with the synchronous iOS API setActive(_:options:). CI only caught the first
because the ScreenCaptureController calls are device-only and compiled out on
the simulator.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GitHub runners ship only the iOS 26 SDK (newest hosted Xcode is 26.6), but
the app now requires the iOS 27 SDK (ScreenCaptureKit + the AVAudioInputNode
throwing tap), so the full app can't build in CI until runners gain Xcode 27.

Add a StreamCoreTests scheme that builds only StreamCore + its Swift Testing
suite (pure Foundation/Security logic, no HaishinKit/ScreenCaptureKit) and
pin those two targets to an iOS 18 floor so they build and run on whatever
iOS simulator the runner ships. CI now runs the 45-test suite; the full app
is verified on a physical iOS 27 device (ScreenCaptureKit is device-only).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@joeblau
joeblau merged commit 3e88ea1 into main Jul 4, 2026
1 check passed
@joeblau
joeblau deleted the feat/in-app-screencapturekit-capture branch July 4, 2026 15: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.

1 participant