Repository navigation
fix: Quad cells render to SurfaceView (#61) - #63
Conversation
With a TextureView every decoded frame raises a SurfaceTexture frame-available
callback, which the framework delivers on a native thread attached to the VM for
each call ("JNISurfaceTexture"). With four streams this allocated about 1.2 MB/s
of garbage and caused a blocking young GC every ~7 s. A SurfaceView hands frames
straight to the compositor: no Java callbacks and one composition pass less.
The single view keeps its TextureView, which pinch-zoom needs. SurfaceHolder
callbacks are added, not replaced, so stop() removes the cell's callback.
|
/review |
Code Review by Qodo
1. Quad playback still allocates
|
| // SurfaceView, not TextureView (#61): with a TextureView every decoded frame raises a | ||
| // frame-available callback that the framework delivers by attaching a native thread to | ||
| // the VM, which allocated about 1 MB/s of garbage with four streams | ||
| view.getHolder().addCallback(holderCallback); |
There was a problem hiding this comment.
1. Quad playback still allocates 📎 Requirement gap ➹ Performance
The new SurfaceView path leaves QuadCell.startThreads() and the decoder/audio hot paths unchanged, including the repeated timed queue poll. During four-stream playback, the supplied measurement still records 7–10 KB/s per decoder thread and 5 KB/s in audio, so Quad playback does not meet the allocation-free hot-path criterion.
Agent Prompt
## Issue description
Changing the Quad renderer to `SurfaceView` removes the framework frame-available callback allocations, but the decoder and audio paths still retain measured hot-path allocations during four-stream playback. Profile the updated path and remove or pool the remaining allocations so playback does not rely on repeated garbage collection.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/QuadCell.java[106-109]
- android/app/src/main/java/com/openipc/decoder/QuadCell.java[132-150]
- android/app/src/main/java/com/openipc/decoder/QuadCell.java[184-205]
## Recommended Fix
Run allocation profiling during four-stream Quad playback after the `SurfaceView` change, then eliminate the remaining decoder, queue, and audio hot-path allocations by reusing buffers and avoiding per-iteration temporary objects. Verify that large video frames and audio buffers remain within the existing pool and that blocking young garbage collections no longer recur because of Quad playback.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
A recreated SurfaceView surface needs a decoder configured for it, and nothing may render into the old one after surfaceDestroyed() returns. The decode thread creates the new decoder once the next surface is there (Qodo).
|
Thanks.
/review |
|
Device test of the CI build
3× Home → resume: same process, a stable 45 threads, 4 sessions and 4 decoders each time, no crash or ANR (one extra decoder start is the close/recreate on the surface reset). The black overlays and the AUDIO badge still draw above the video. |
fix: Quad cells render to SurfaceView (#61)
Fixes #61.
Measurement (AG-216, Android 10, Quad with four cameras). A diagnostic build logged the allocations of each app thread (
Debug.getThreadAllocSize, not part of this PR):Quad-N-dec×4poll(5 ms)Quad-N-net×4quad-audioThat is about 45 KB/s in total, yet the process freed about 1.2 MB/s (9 young GCs per minute, ~8 MB each, 0 large objects). Every GC was triggered by one of the four
JNISurfaceTexturethreads. With aTextureView, each decoded frame raises aSurfaceTextureframe-available callback, and the framework delivers it on a native thread that it attaches to the VM for the call. With 4 × 25 fps that is about 100 attaches per second.Change: Quad cells use
SurfaceView. Frames go straight to the compositor, with no Java callbacks and one composition pass less. The black overlays and the AUDIO badge are normal views drawn above the surface, so they work as before. The single view keeps itsTextureView, which pinch-zoom needs. BecauseSurfaceHoldercallbacks are added rather than replaced,stop()removes the cell's callback.Device results with the CI build will follow.