Render the main video on a SurfaceView unless object detection needs the TextureView - #114
Conversation
PR Summary by QodoUse SurfaceView unless object detection requires TextureView
AI Description
Diagram
High-Level Assessment
Files changed (2)
|
Code Review by Qodo
1. Restart can lose toggle
|
| if (!isVRMode && enabled != videoUsesTextureView) { | ||
| Toast.makeText(this, "Restarting to switch video renderer...", Toast.LENGTH_SHORT).show(); | ||
| resetApp(); |
There was a problem hiding this comment.
1. Restart can lose toggle 🐞 Bug ☼ Reliability
The new renderer-switch branch calls resetApp() immediately after od_enabled was saved with asynchronous apply(), and resetApp() terminates the process with System.exit(0). If the disk write has not completed, the relaunched activity reads the old value and selects the old renderer, so the user's toggle appears to fail.
Agent Prompt
## Issue description
The renderer restart can terminate the process before the asynchronous `od_enabled` preference write reaches disk, causing the app to restart with the old renderer.
## Issue Context
`resetApp()` immediately starts a fresh task and calls `System.exit(0)`. The existing VR restart path uses synchronous `commit()` before invoking the same reset helper.
## Fix Focus Areas
- app/src/main/java/com/openipc/pixelpilot/VideoActivity.java[2004-2013]
- app/src/main/java/com/openipc/pixelpilot/VideoActivity.java[202-210]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| videoUsesTextureView = getSharedPreferences("general", MODE_PRIVATE) | ||
| .getBoolean("od_enabled", false); |
There was a problem hiding this comment.
2. Disabled detection keeps textureview 🐞 Bug ➹ Performance
setupStandardVideoPlayer() selects the TextureView solely from the persisted flag before onResume() validates runtime and model availability. When that validation disables object detection, it returns without switching or restarting the renderer, leaving the expensive TextureView active for the entire session even though detection is off.
Agent Prompt
## Issue description
Startup can select the TextureView from a stale enabled preference and then automatically disable object detection without returning to the SurfaceView.
## Issue Context
This occurs when a previously enabled model is no longer available or the object-detection runtime is unsupported. The early validation failure clears `od_enabled` but does not execute the new renderer-mismatch restart branch.
## Fix Focus Areas
- app/src/main/java/com/openipc/pixelpilot/VideoActivity.java[389-400]
- app/src/main/java/com/openipc/pixelpilot/VideoActivity.java[1988-2001]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
@iflyhere both Qodo points hold up, and I have nothing to add beyond a bit of detail:
|
…the TextureView OpenIPC#108 replaced the main video SurfaceView with a TextureView so MediaPipe can grab frames via getBitmap(). That swap is unconditional, so every user pays for it even with object detection turned off: the video is no longer eligible for a hardware overlay plane and instead goes through the view hierarchy's GPU composition, which costs GPU time, power and about one frame of latency. The layout now carries both renderers and the active one is picked from the existing "od_enabled" preference: - object detection off (default) -> mainVideoSurface (SurfaceView), the pre-OpenIPC#108 behaviour - object detection on -> mainVideo (TextureView), unchanged Toggling detection in the menu swaps the renderer, which means the decoder needs a different surface. VideoPlayer.stopAndRemoveReceiverDecoder() also stops the UDP receiver and nothing restarts it, so a hot swap is not safe today; the toggle restarts the app instead, the same way the VR mode toggle already does. startObjectDetectionLoop() bails out if the TextureView is not the active renderer.
a3d4002 to
22e3015
Compare
…n runs Two fixes from review. setupStandardVideoPlayer() chose the renderer from od_enabled alone, but that preference is not the same thing as "detection is going to run". setObjectDetectionEnabled() in onResume turns it back off when the runtime or the selected model is missing, and returns before reaching the renderer swap - so a device that cannot do detection at all still spent the whole session on the TextureView, paying for GPU composition that nothing read from. It only corrected itself on the next launch, because by then the preference had been written back as false. The renderer now checks the runtime and the model too. Both extra calls are behind od_enabled, so nothing changes for anyone with detection off; when it is on, isObjectDetectionRuntimeSupported() loads a library that onResume was about to load a few milliseconds later anyway, and caches the result. The od_enabled write before the restart also used apply(). resetApp() calls System.exit(0) straight after finish(), so the pause/stop path that would flush an asynchronous write never runs, and the renderer picked on the next launch is read from exactly this value - a lost write means the app restarts into the renderer it was trying to leave. commit(), the same as the VR and low latency toggles in this file.
|
Both fixed and pushed, rebased onto master (so it now sits on top of #121). 1. The 2. The renderer now checks all three: videoUsesTextureView = prefs.getBoolean("od_enabled", false)
&& isObjectDetectionRuntimeSupported()
&& isSelectedObjectDetectionModelAvailable();You are right that it self-corrected on the next launch, since One correction to "both helpers just read prefs and files": Compile tested for arm64-v8a + armeabi-v7a. |
|
Thank you! merged. |
Note
Compile tested only (arm64-v8a + armeabi-v7a). Not yet flown. Object detection
itself is unchanged by this PR, but the renderer switch path deserves a check
on hardware.
The problem
#108 changed the primary renderer from a
SurfaceViewto aTextureView:That is needed for the feature —
TextureView.getBitmap()is how frames reachMediaPipe — but the swap is unconditional. Users with object detection off, i.e.
everybody by default, now get the
TextureViewpath too.The difference is not cosmetic. A
SurfaceViewgets its own layer and can bepromoted to a hardware overlay plane: the decoder writes into it and the display
controller scans it out. A
TextureViewis drawn as part of the view hierarchy,so every decoded frame has to go through GPU composition together with the rest
of the UI. That costs GPU time, power, and roughly one frame of extra latency,
on a receiver whose whole point is low latency.
The change
The layout carries both renderers and the active one is picked from the existing
od_enabledpreference:mainVideoSurface(SurfaceView)mainVideo(TextureView)Details:
setupStandardVideoPlayer()selects the view and records the choice invideoUsesTextureViewsetupVRVideoPlayers()hides both, as beforeonVideoRatioChanged()keeps the aspect ratio of the new view in syncstartObjectDetectionLoop()bails out unless theTextureViewis the activerenderer, so
getBitmap()is never called on the wrong viewWhy the toggle restarts the app
Switching detection on or off means the decoder needs a different surface.
VideoPlayer.stopAndRemoveReceiverDecoder()also callsstop(), which tearsdown the UDP receiver, and nothing restarts it — only
onResume()callsstart(). So a hot surface swap silently kills reception today. Rather thanrework the receiver lifecycle in this PR, the toggle calls
resetApp(), thesame thing the existing VR mode toggle does, and says so with a toast.
Fixing
VideoPlayer's receiver/decoder lifecycle so the surface can be swappedin place would be a good follow-up; it would also let VR mode be toggled without
a restart.
Part of a series of independent fixes found while building an immersive (OpenXR) mode on a
Quest 3, each standalone and mergeable in any order:
wirelessInfo()safeVideoPlayer/WfbNgLinktake aContext#113 and #116 are now confirmed on hardware (Quest 3, Horizon OS, Android 14).