Repository navigation
feat: MJPEG cells in multi-view (#132) - #139
Conversation
|
/review |
|
Device test on AG-216 (Aceline-1), CI build d3ea50c, 3×3 grid + U:
Note: in the cheap mode the rate reads 0.8–1.0 fps, because the next frame after 1 s arrives up to one frame interval (200 ms) late. |
Code Review by Qodo
1. MJPEG planning uses a guessed budget
|
…against the path, surface recreated after a layout pass, JPEG losses and UDP stalls counted, software JPEG off the hardware budget, software codecs skipped Co-Authored-By: Claude <noreply@anthropic.com>
|
/review |
|
Qodo findings 1–9 fixed in 9758984. Re-tested on AG-216 (build 9758984): MJPEG cell in the grid at full rate |
Thanks for the detailed validation. The retest covers the reported reconnect, MJPEG rendering, codec transition, and stability scenarios. The published review still shows |
|
Code review by qodo was updated up to the latest commit 9758984 |
…at a time picks the JPEG decoder, a stale video decoder is closed before MJPEG is drawn Co-Authored-By: Claude <noreply@anthropic.com>
|
Qodo round 2 → a3dab50:
|
|
/review |
Thanks for the round-two update. Based on your summary, the changes address findings 5, 6, and 7. Finding 2 remains an intentional design decision: queued frames are treated as delayed output, while input-buffer failures and prolonged decoder silence are handled separately. The published review still lists finding 2 as open until a new review updates its status. |
| synchronized (JpegCodec.class) { | ||
| dec = chosen != null ? chosen : find(); | ||
| boolean byName = dec == null; | ||
| if (byName) { | ||
| if (byNameFailed) throw new IllegalStateException("no JPEG decoder"); | ||
| dec = BY_NAME[0]; | ||
| } | ||
| try { | ||
| c = MediaCodec.createByCodecName(dec[0]); | ||
| } catch (Exception e) { | ||
| if (byName) byNameFailed = true; |
There was a problem hiding this comment.
1. Mjpeg cells exceed decoder instance limits 📎 Requirement gap ☼ Reliability
JpegCodec.decode() creates and starts each hardware JPEG decoder without acquiring or releasing a shared instance-budget slot. When multiple MJPEG cells exceed the device's declared limit, they independently attempt codec creation and fall back to software instead of reporting the applicable decoder limit or preserving the last-resort pause behavior.
Agent Prompt
## Issue description
Hardware MJPEG decoders bypass the shared instance-budget accounting used by H.264 and H.265. Multiple cells can therefore exceed the device's decoder limit, while failures are converted to per-cell software fallback instead of enforcing the applicable limit and notifying the planner.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/JpegCodec.java[135-160]
- android/app/src/main/java/com/openipc/decoder/JpegRenderer.java[50-67]
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[151-190]
## Recommended Fix
Acquire a shared `InstanceBudget` slot for the selected JPEG MIME before creating the codec, learn the codec's declared maximum instance count after creation, release the slot on every configure, start, surface, and stream failure, and release it when the renderer closes the codec. When acquisition fails, expose the JPEG decoder limit to the cell planner so it reports the applicable limit and reserves pausing for overload after reduced-rate decoding has also failed.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
Code review by qodo was updated up to the latest commit a3dab50 |
…t may be in it, one JPEG sink per cell Co-Authored-By: Claude <noreply@anthropic.com>
|
/review |
| try { | ||
| for (MediaCodecInfo ci : new MediaCodecList(MediaCodecList.ALL_CODECS) | ||
| .getCodecInfos()) { | ||
| if (ci.isEncoder()) continue; | ||
| if (ci.isEncoder() || softwareOnly(ci)) continue; |
There was a problem hiding this comment.
1. Mjpeg planning uses a guessed budget 📎 Requirement gap ➹ Performance
JpegCodec.find() selects a codec name and MIME type but never reads its declared video throughput or publishes it to MediaCodecManager.declaredBlocksPerSec. When a device relies on the hardware JPEG path, the planner therefore keeps the generic default budget instead of the selected decoder's 1920×1080 capacity, affecting promotion and capacity learning for every MJPEG cell.
Agent Prompt
## Issue description
The hardware JPEG path selects a decoder without querying its declared video capabilities, so multi-view planning cannot initialize its budget from the selected decoder's throughput as required by the capacity-learning model.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/JpegCodec.java[52-58]
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[219-241]
- android/app/src/main/java/com/openipc/decoder/Decoder.java[1048-1053]
## Recommended Fix
After selecting and successfully configuring the hardware JPEG codec, query its `VideoCapabilities` for 1920×1080 support and publish the resulting macroblocks-per-second value through the shared decoder-capacity mechanism, while preserving the existing fallback when capability information is unavailable.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
Code review by qodo was updated up to the latest commit 1c3fb86 |
|
Qodo round 4 (1c3fb86): findings 13–15 resolved. The two remaining requirement gaps are left on purpose:
|
Understood. Your rationale makes both remaining items intentional design choices rather than accidental omissions:
The published review still shows both as open; this comment explains why you are retaining them, but does not close them. |
|
Correction to my device reports above: on the AG-216 the hardware JPEG decoder is never used — as found in #135, Android 10 does not list What that means for the tests: the software/canvas path was exercised on the device — MJPEG cell in the grid (reduced 1 fps and full 5–7 fps, corner |
Co-Authored-By: Claude <noreply@anthropic.com>
#139) Co-Authored-By: Claude <noreply@anthropic.com>
…come from its JPEG assembler Co-Authored-By: Claude <noreply@anthropic.com>
…against the path, surface recreated after a layout pass, JPEG losses and UDP stalls counted, software JPEG off the hardware budget, software codecs skipped Co-Authored-By: Claude <noreply@anthropic.com>
…at a time picks the JPEG decoder, a stale video decoder is closed before MJPEG is drawn Co-Authored-By: Claude <noreply@anthropic.com>
…t may be in it, one JPEG sink per cell Co-Authored-By: Claude <noreply@anthropic.com>
feat: MJPEG cells in multi-view (#132)
Co-Authored-By: Claude <noreply@anthropic.com>
#139) Co-Authored-By: Claude <noreply@anthropic.com>
…come from its JPEG assembler Co-Authored-By: Claude <noreply@anthropic.com>
The TextureView's frames go through the app's own GL composition and window buffer; on a Mali-G31 TV box (Android 10) that buffer's queueBuffer stalled for seconds, growing with every grid/single switch (up to 14 s in 60 cycles). With a SurfaceView, as the grid cells use, the stalls are gone (#76). - Zoom and pan: the view is laid out zoom times the fitted size and moved by the pan; its surface keeps the fitted size (setFixedSize). Same gestures, clamping and double-tap reset as before, on every Android version. - Long-press screenshot and the debug page snapshot: PixelCopy on the snapshot thread (Snapshots.copy), the UI thread does not wait. Before Android 7 a screenshot says it is not available, the snapshot cell says "no picture". - MJPEG to H.264/H.265: the view is hidden and shown again for a new surface, as a grid cell does (#139); the decoder waits until surfaceCreated() and is closed in surfaceDestroyed(). - Version 2.09.
MJPEG cameras play in the grid instead of being paused with "MJPEG: single view only".
JpegRenderer: the MJPEG path of the single view (hardwarevideo/jpegdecoder where the platform exposes one, else BitmapFactory + lockCanvas) moved into one class, used by the single view and by every cell.JpegAssemblerand shows the frames on its SurfaceView from its decode thread.Part of #132.