fix(recorder): camera flip no longer kills the record button or drops the H.264 pin (iOS) - #154
Merged
Merged
Conversation
…264 per connection epoch Two regressions from one root: #150 removed the accidental session restart a camera flip used to trigger (the flip's cameraReady reset flipped micWanted, rebuilding the video output and restarting the session), and two behaviors were riding it. 1. On iOS, flipCamera()'s setCameraReady(false) never re-armed: onStarted maps to AVCaptureSession.didStartRunningNotification, and an iOS device swap runs inside beginConfiguration/commitConfiguration on a RUNNING session - the notification never fires (start() even early-returns on isRunning). Record gestures (enabled: cameraReady && ...) died permanently; flipping back couldn't recover. The reset exists solely for CameraX (#133 torch-mid-rebind crash) and CameraX re-fires onStarted per bind, so it is now Android-only - mirroring the existing platform scoping of the zoom/torch prop gates (ba7647b). 2. The H.264 pin is applied per-connection natively (output.setOutputSettings(settings, for: connection)), and a flip forms a new connection - the pin died with the old one and the effect, keyed on output identity, never re-applied: post-flip clips silently recorded HEVC. The pin now re-keys on a connection epoch bumped from <Camera onConfigured> (fires after connections are formed on every reconfigure: cold open, enableAudio rebuild, flip), so it re-lands on the new connection with the existing bounded retry riding any race. Repro'd on TestFlight 2.0.0 (31) = main @ 82e3b95, the first build containing #150. Fixes #153
There was a problem hiding this comment.
Pull request overview
This PR fixes an iOS recorder regression where flipping the camera could permanently disable recording interactions and silently revert post-flip clips to HEVC by ensuring iOS flip no longer relies on a cameraReady reset and by re-applying the H.264 codec pin per newly formed video connection.
Changes:
- Makes
flipCamera()’scameraReadyreset Android-scoped to avoid permanently disabling record gestures on iOS. - Re-keys the iOS H.264 pin logic from “video output identity” to a “connection epoch” bumped via
<Camera onConfigured>. - Wires a new
onSessionConfiguredcallback from the recorder hook into the<Camera onConfigured>event.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/features/recorder/use-recorder.ts | Adds connection-epoch-based H.264 pinning and scopes cameraReady reset during camera flips. |
| src/app/recorder.tsx | Wires <Camera onConfigured> to the recorder hook to bump the connection epoch after reconfigures. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…but iOS" The guard exists for CameraX; any other platform would inherit iOS's stuck-cameraReady failure mode from a reset that never re-arms. Addresses review on #154.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/features/recorder/use-recorder.ts:557
onSessionConfiguredalways bumpsconnectionEpoch, even on non-iOS platforms whereconnectionEpochis never read (the pin effect is iOS-only). This introduces extra state updates/renders on Android (and any other platform) every time VisionCamera emitsonConfigured, without any functional benefit.
onCameraReady: () => setCameraReady(true),
// Wire to <Camera onConfigured>: fires whenever the session's connections are (re)formed —
// cold open, enableAudio output rebuild, camera flip. Bumping the epoch re-arms the H.264
// pin for the NEW video connection (the codec is applied per-connection natively, so it
// dies with the old one on every reconfigure).
onSessionConfigured: () => setConnectionEpoch((prev) => prev + 1),
morepriyam
added a commit
that referenced
this pull request
Aug 8, 2026
morepriyam
added a commit
that referenced
this pull request
Aug 12, 2026
…but iOS" The guard exists for CameraX; any other platform would inherit iOS's stuck-cameraReady failure mode from a reset that never re-arms. Addresses review on #154.
morepriyam
added a commit
that referenced
this pull request
Aug 12, 2026
fix(recorder): camera flip no longer kills the record button or drops the H.264 pin (iOS)
morepriyam
added a commit
that referenced
this pull request
Aug 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the TestFlight build 31 regression: flipping the camera on iOS permanently killed the record button, and (silently) dropped the H.264 codec pin for all post-flip clips. Full root-cause analysis in #153.
The one-line version
#150 correctly stopped gating the mic on
cameraReady— but that gate flip was what made a camera flip rebuild the video output and restart the session. Two behaviors were secretly riding that accidental restart, and build 31 (main @ 82e3b95, first build with #150) exposed both.Changes
flipCamera()'scameraReadyreset is now Android-only. It exists for CameraX's torch-mid-rebind crash (Android parity: first device-testing wave — recorder fixes, call detection, release hygiene #133), and CameraX re-firesonStartedper bind so it re-arms there. On iOS the flip is an input swap insidebeginConfiguration/commitConfigurationon a running session —didStartRunningNotificationnever fires (verified in HybridCameraSession.swift: the listener maps to that notification, andstart()early-returns onisRunning), so the reset could never re-arm and permanently disabled the record gestures. iOS's zoom/torch props already bind ungated (ba7647b), so the reset served no purpose there. This mirrors the existing platform scoping pattern.The H.264 pin re-applies per connection, not per output instance. Natively the codec lands per-connection (
output.setOutputSettings(settings, for: connection)), and every reconfigure — flip, enableAudio rebuild — forms a new connection, killing the pin. The pin effect now keys on a connection epoch bumped from<Camera onConfigured>(VisionCamera's documented "connections are formed" hook), which fires after the new connection exists — the only ordering that can't lose the pin to an async reconfigure landing later. The existing bounded retry still rides any residual race; fail-open behavior (worst case: one HEVC clip) is unchanged.Verification
tsc --noEmitclean, eslint clean, jest 122 passedNo flash unitcrash (the Android parity: first device-testing wave — recorder fixes, call detection, release hygiene #133 guard is intact)Fixes #153