Carry the camera turn as data, publish the lens list, add a desktop backend - #501
Carry the camera turn as data, publish the lens list, add a desktop backend#501samoylenkodmitry wants to merge 4 commits into
Conversation
96df34c to
e9578c4
Compare
|
Checked on a Pixel 9 Pro ( The turn. The app's own log, same phone, before and after: A landscape sensor buffer was reaching a portrait viewfinder untouched; it is turned upright now, before both the screen and the scan pipeline. On screen the monitor and its dock read the right way up. The lenses. The first device run showed no lens control at all, which is what sent me after the last commit here. That phone lists exactly two cameras — one back, one front — and keeps its ultra wide and tele behind the back one as physical cameras 2 to 6. Reaching them needs
The control now reads Ultra wide · Wide · Tele, and all three open: ultra wide widens the field of view, tele is visibly magnified, and the viewfinder keeps its picture across a switch rather than blanking. Still green after the change: |
…ackend The frame's turn is metadata every backend reports: Android computes it per lens facing and follows display turns while the session runs, iOS stops pre-rotating and reports 90, and upright_rgba8 applies the turn inside the one conversion pass a consumer already pays. The lens list is published state (CameraLenses, LensFacing) with front lenses included, so a lens control observes instead of paying a blocking platform call per recomposition. A lens switch keeps the last frame on screen instead of blanking. A nokhwa backend behind the camera-native feature brings the camera to desktop. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…he caller The capture thread sits inside a platform read for as long as that read takes, so joining it where the session is stopped holds up the screen being left. The thread is told to end and parked; the next session opening waits for it, which is also what keeps two sessions from holding one device. A lens switch that cannot reopen now says so instead of leaving a Running nobody serves. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A Pixel 9 Pro lists one back camera and one front camera, so a lens control had nothing to switch between: its ultra wide and tele sit behind the back one and never appear in getCameraIdList. Camera2 reaches them by opening the listed camera and pointing the outputs at the lens behind it, which is what a logical:physical lens id names here. A session that a lens refuses falls back to the camera itself rather than leaving the screen with no picture. The same lens is exposed more than once — that phone carries six cameras behind its back camera, which are its three lenses twice over — so one camera per focal length is kept. On the phone the control now reads Ultra wide, Wide, Tele, and each one opens. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
a7b9b7d to
6ad4b48
Compare
…endency The camera-native feature carried no lockfile entry, so every build resolved nokhwa afresh and the all-features tree grew a second rustix family: nokhwa reaches v4l2 on Linux through a bindgen old enough to bring its own, and a second family of a crate this workspace already builds is what the duplicate dependency budget exists to refuse. macOS and Windows are clean. So the backend serves those two and the lock now records what it pulls. Linux desktop capture waits for that v4l2 chain to carry a bindgen of this decade; the alternative was the first entry in an allowlist this workspace has kept empty, which is a wider promise than one backend is worth. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
What changes
The turn is data, and one pass applies it. Every backend now reports how far a frame must be turned clockwise, and
CameraFrame::upright_rgba8()applies that turn inside the same pass as the format conversion. Before this, Android reported the turn and no consumer read it, iOS turned the pixels itself and reported zero, and desktop reported zero: three different contracts for one field. This is what rendered the Pixel 9 viewfinder 90 degrees off.JPEG_ORIENTATIONon Android, the photo pipeline on iOS).The lens list is published state.
CameraLenses { lenses, active }withLensFacing(Back/Front/External) arrives throughpublish_camera_lenseswhen a session opens and when the device changes;rememberCameraLenses()observes it. Both phone lens lists are blocking platform calls, so a lens control must not poll them per recomposition. Front lenses are listed now; the Android line protocol isid|facing|name.A lens switch keeps the picture. Both phone backends restart the session without publishing
Stoppedin between, so the viewfinder holds the last frame instead of blanking; the new device arrives as a secondRunning.Desktop backend. A nokhwa capture thread (
AVFoundation/MSMF/V4L2) behind thecamera-nativefeature, installed withinstall_native_camera(), publishingRgb8frames. Lifted from cranscan, which deletes its own copy.Verification
cargo test -p cranpose-services --features camera-native: green on Linux (samarch-1) and macOS (macm3), 246 tests. New tests pin the quarter-turn mapping per format, the non-quarter fallback, and the lens-list publish/observe/clear path.cargo fmt --all --checkon the pinned nightly: clean.aarch64-apple-ios,aarch64-linux-android(viacargo ndk).docs/capability_parity.mdcamera row updated (desktop ■, web still □).🤖 Generated with Claude Code