You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Why this repo exists, and what the first milestone is.
Background
android-cam-tui streams a phone camera into a v4l2loopback sink by driving scrcpy --video-source=camera. That path has a hard floor and a hard wall:
Floor: upstream scrcpy gates camera capture at Android 12 / API 31 (Add option to mirror camera Genymobile/scrcpy#4213). On Android ≤11 the camera service only serves clients it considers foreground, and the shell uid cannot reliably be one.
Wall: a patched server that fakes foreground status with an invisible overlay window was tried and does not help on vendor builds that revoke the CAMERA permission from com.android.shell outright.
Measured on OPPO CPH1931 (ColorOS, Android 10, unrooted), with the patched server from officialdad/scrcpy@camera-a10 loaded via SCRCPY_SERVER_PATH:
SecurityException: validateClientPermissionsLocked:1076: Caller "com.android.shell"
(PID 2000, UID 19157) cannot open camera "0" without camera permission
The overlay window itself was created fine — the refusal happens earlier, on permissions. pm grant com.android.shell android.permission.CAMERA is refused too (shell lacks GRANT_RUNTIME_PERMISSIONS on this ROM), and there is no root. Full writeup: officialdad/android-cam-tui#4.
Conclusion: everything that runs as the shell uid is a dead end on this class of device. Only code running as an installed app, holding its own user-granted CAMERA permission, can open the camera.
The pivot
A small companion APK that owns the camera and streams frames to the Linux host, with android-cam-tui gaining a second backend behind the same interface it already uses for StreamRunner.
Sketch, deliberately minimal for v1:
Capture: Camera2 → MediaCodec H.264, same shape as scrcpy's own encoder.
Transport: raw Annex-B over a TCP socket on localhost, reached from the host with adb forward tcp:<port> tcp:<port>. USB only, no Wi-Fi, no pairing, no discovery — adb is already a hard dependency of the TUI.
Host side: existing pipeline, unchanged in spirit — decode and write into the v4l2loopback sink.
Control: none in v1. Launch with adb shell am start, hardcode back camera at 1280x720@30. Camera/size/fps selection comes later, once the stream works at all.
Open questions to settle before writing much code
Background camera access. Android 9+ blocks camera from the background. A foreground service with foregroundServiceType="camera" is the documented route (API 29+), but it still needs the app foregrounded once to start. Does that survive the screen going off?
Keyguard interaction.android-cam-tui deliberately never wakes or unlocks the phone: waking a locked device starts face unlock, which opens a camera of its own and pushes the device past the system open-camera limit, so our own open gets rejected. Whatever launch sequence this app needs must not reintroduce that.
minSdk. API 21 is cheap to target and covers everything the scrcpy path cannot. Confirm nothing in the encoder path forces higher.
Latency budget. scrcpy's camera path is the baseline to beat, or at least match; worth measuring before committing to a socket format.
First milestone
One APK, one hardcoded resolution, one socket, frames landing in /dev/video0 on the OPPO CPH1931 — the exact device that no scrcpy-based path can serve. Everything else is deferred until that works end to end.
Why this repo exists, and what the first milestone is.
Background
android-cam-tuistreams a phone camera into a v4l2loopback sink by drivingscrcpy --video-source=camera. That path has a hard floor and a hard wall:com.android.shelloutright.Measured on OPPO CPH1931 (ColorOS, Android 10, unrooted), with the patched server from officialdad/scrcpy@camera-a10 loaded via
SCRCPY_SERVER_PATH:The overlay window itself was created fine — the refusal happens earlier, on permissions.
pm grant com.android.shell android.permission.CAMERAis refused too (shell lacksGRANT_RUNTIME_PERMISSIONSon this ROM), and there is no root. Full writeup: officialdad/android-cam-tui#4.Conclusion: everything that runs as the shell uid is a dead end on this class of device. Only code running as an installed app, holding its own user-granted CAMERA permission, can open the camera.
The pivot
A small companion APK that owns the camera and streams frames to the Linux host, with
android-cam-tuigaining a second backend behind the same interface it already uses forStreamRunner.Sketch, deliberately minimal for v1:
MediaCodecH.264, same shape as scrcpy's own encoder.adb forward tcp:<port> tcp:<port>. USB only, no Wi-Fi, no pairing, no discovery —adbis already a hard dependency of the TUI.adb shell am start, hardcode back camera at 1280x720@30. Camera/size/fps selection comes later, once the stream works at all.Open questions to settle before writing much code
foregroundServiceType="camera"is the documented route (API 29+), but it still needs the app foregrounded once to start. Does that survive the screen going off?android-cam-tuideliberately never wakes or unlocks the phone: waking a locked device starts face unlock, which opens a camera of its own and pushes the device past the system open-camera limit, so our own open gets rejected. Whatever launch sequence this app needs must not reintroduce that.First milestone
One APK, one hardcoded resolution, one socket, frames landing in
/dev/video0on the OPPO CPH1931 — the exact device that no scrcpy-based path can serve. Everything else is deferred until that works end to end.