Skip to content

fix: streams above 1080p in the single view: decoder set up from the SPS (#166) - #220

Merged
compot-dev merged 15 commits into
v2from
fix/large-frames-166
Oct 20, 2026
Merged

compot-dev merged 15 commits into
v2from
fix/large-frames-166

Conversation

@compot-dev

@compot-dev compot-dev commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #166 (to be confirmed on the reporter's HY300).

Problem. On a Rockchip HY300 (Android 11, OMX.rk.video_decoder.avc/hevc) a 2304×1296 stream connects, the decoder starts and allocates buffers, but never outputs a frame. 1080p and 720p from the same camera play.

What test 1 showed. The first version of this PR announced the largest size the decoder declares as KEY_MAX_WIDTH/HEIGHT. On the HY300 the log of test build 1 has no pictures up to line: the Rockchip OMX decoders declare nothing above 1080p, so nothing was set and the decoder still started at 1280×720 (reports of 2026-10-19 in #166, for 2.14 and 2.17).

Change.

  • SpsParser reads the picture size (with the cropping / conformance window) from the H.264 and H.265 SPS. Tested with SPS from libx264 / libx265 (High, Main, Baseline, High 4:4:4, interlaced; 2304×1296, 1080p, 720p).
  • Single view: the decoder is created at the SPS (an H.265 VPS before it is kept and fed first) and configured for the stream's size; KEY_MAX_WIDTH/HEIGHT covers the stream and the largest declared size. A larger SPS mid-stream gets a new decoder.
  • A hardware decoder at a size it does not declare (above 1080p) is on probation: no frame within 5 s after its parameter sets → the stream goes to the software decoder (c2.android.*), logged as No frame from … : software decoder.
  • /stats.json (single): size only while a picture is shown (it kept the size of an earlier stream, noted by the reporter), new stream_size (from the SPS) and decoder. docs/debug.md updated.
  • The grid is unchanged: no SPS gating, no large buffers.

Version 2.18.2 (PATCH: a fix; it was 2.17.1 until Mirror, #246, merged first as 2.18.0, and the snapshot fix #248 took 2.18.1). Merged with v2 after #246: the mirror's GL path is also released on the paths this PR adds (decoder failure, replacing a decoder on probation).

  • Local Docker build as CI: testDebugUnitTest (360 tests), lintDebug (no new findings), assembleDebug

  • Aceline-1 (AG-216, Android 10), CI build 2.17.1 35f2bc8: single 2560×1920 H.265 (.184) at 20 fps, log stream 2560×1920, pictures up to 3840×2160, stats.json size/stream_size 2560x1920, decoder OMX.allwinner.video.decoder.hevc; 1080p H.264 (.248) and switching between them; grid unchanged (9 cells). The probation path cannot be reached here (the Allwinner decoders declare 4K).

  • Reporter: 2304×1296 H.264 and H.265 on the HY300 with hy300-166-test2

  • HY300 (sansarus, hy300-166-test2, 2026-10-20): 2560×1440 H.264 now shows a picture through c2.android.avc.decoder after the 5 s probation of OMX.rk.video_decoder.avc, at ~18 fps; heavy for that device (the software decoder sometimes falls behind and restarts), a lower substream plays smoothly on the hardware decoder.

  • Aceline-1, CI build 4046f6b (after the Qodo fixes): single H.265 2560×1920 and H.264 1080p, 3× Home → back (the size comes from the new SPS each time), grid; 0 FATAL EXCEPTION / ANR in. The fallback path itself cannot be reached there (the Allwinner decoders declare 4K); 0fe8807 changes only that path.

compot-dev added a commit that referenced this pull request Oct 16, 2026
# Conflicts:
#	android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java
…PS (#166)

The test build of PR #220 did not help on the Rockchip HY300: its OMX
decoders declare no size above 1080p, so KEY_MAX_WIDTH/HEIGHT was never
set. Now the single view waits for the SPS, reads the picture size from
it (SpsParser, H.264 and H.265, cropping included) and configures the
decoder for that size, KEY_MAX_* covering it. A larger SPS mid-stream
gets a new decoder. A hardware decoder at a size it does not declare
that gives no frame within 5 s after its parameter sets hands the
stream to the software decoder.

/stats.json: size only while a picture is shown (it kept the size of an
earlier stream), plus stream_size (from the SPS) and decoder.
@compot-dev compot-dev changed the title fix: streams above 1080p in the single view (#166) fix: streams above 1080p in the single view: decoder set up from the SPS (#166) Oct 19, 2026
Mirror (#246) merged first: its GL path is released on the new failure and
replace paths (failDecoder, dropDecoder); version 2.18.1, versionCode 44.
Version 2.18.2, versionCode 45 (2.18.1 went to the snapshot fix, #248).
@compot-dev
compot-dev marked this pull request as ready for review October 20, 2026 17:51
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Configure single-view decoders from SPS for streams above 1080p

🐞 Bug fix 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Configure single-view decoders from H.264/H.265 SPS dimensions so larger streams can display.
• Fall back to software when an undeclared-size hardware decoder produces no frame within five
 seconds.
• Correct live picture-size reporting, expose stream size and decoder, and document the 2.18.2
 release.
Diagram

graph TD
  N["Single-view NAL"] --> P["SPS parser"] --> S{"SPS received?"}
  S -->|"yes; parsed size or fallback"| H["Hardware decoder"] --> F{"Probation outcome?"}
  F -->|"frame or not applicable"| T["Single-view stats"]
  F -->|"no frame after 5s"| W["Software decoder"] --> T
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use software immediately for undeclared sizes
  • ➕ Avoids a five-second wait when hardware cannot produce a frame.
  • ➖ Consumes software-decoding capacity even when hardware can play the stream despite its declared limits.

Recommendation: Keep SPS-sized hardware decoding with a bounded probation period: capability declarations alone proved insufficient on the target device, but an undeclared size does not establish that hardware will fail. Validate the transition on the reporter's HY300 before treating the fix as confirmed.

Files changed (7) +551 / -47

Enhancement (1) +187 / -0
SpsParser.javaParse visible H.264 and H.265 SPS dimensions +187/-0

Parse visible H.264 and H.265 SPS dimensions

• Adds an SPS parser that removes emulation-prevention bytes and accounts for H.264 cropping and H.265 conformance windows. Returns no size for malformed, unsupported, or implausible input.

android/app/src/main/java/com/openipc/decoder/SpsParser.java

Bug fix (2) +266 / -44
Decoder.javaEnable large frames and report live single-view diagnostics +13/-3

Enable large frames and report live single-view diagnostics

• Opts the single-view decoder into SPS-based sizing without changing grid decoding. Reports picture size only while video is live and adds SPS stream size and active decoder name to single-view stats.

android/app/src/main/java/com/openipc/decoder/Decoder.java

MediaCodecManager.javaSize decoders from SPS and fall back when hardware stalls +253/-41

Size decoders from SPS and fall back when hardware stalls

• Waits for a single-view SPS before decoder creation, preserves an earlier H.265 VPS, configures stream and maximum picture dimensions, and replaces a decoder when the picture outgrows it. Adds five-second probation and software fallback for undeclared large hardware sizes, with decoder and GL cleanup on replacement or failure.

android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java

Tests (1) +85 / -0
SpsParserTest.javaCover SPS sizing and malformed input +85/-0

Cover SPS sizing and malformed input

• Tests representative H.264 and H.265 SPS dimensions, cropping, interlaced and 4:4:4 cases, invalid inputs, and emulation-prevention removal.

android/app/src/test/java/com/openipc/decoder/SpsParserTest.java

Documentation (2) +11 / -1
CHANGELOG.mdDocument the 2.18.2 large-stream fix +8/-0

Document the 2.18.2 large-stream fix

• Adds release notes for SPS-sized single-view decoding, timed software fallback, and the revised stats fields.

CHANGELOG.md

debug.mdDescribe updated single-view stats fields +3/-1

Describe updated single-view stats fields

• Clarifies that picture size appears only while video is live and documents the new stream_size and decoder fields.

docs/debug.md

Other (1) +2 / -2
build.gradleBump the Android app to 2.18.2 +2/-2

Bump the Android app to 2.18.2

• Increments versionCode from 44 to 45 and versionName from 2.18.1 to 2.18.2.

android/app/build.gradle

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📎 Requirement gaps (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Decoder errors prevent software fallback ✓ Resolved
Description
softwareFor is set only when probation expires; neither a configuration or start failure nor
failDecoder() marks the undeclared size for software. If hardware rejects that size during setup
or throws while receiving frames, each retry selects the same hardware decoder instead of reaching
the new fallback path.
Code

android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[R769-775]

+    private void failDecoder(MediaCodec codec, Exception e) {
+        decoder = null;
+        String slot = takeSlot();
+        GlMirror gl = takeMirror();
+        retry.onFailure(nowMs());
+        Log.e(TAG, "Codec exception, retry in "
+                + DecoderRetry.delayMs(retry.failures()) + " ms: " + e.getMessage());
Evidence
Software selection requires a matching softwareFor key. The probation-expiry branch is the only
path that writes it, while setup failures and decode failures release the codec and back off without
writing it.

android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[499-509]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[555-559]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[599-609]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[699-705]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[769-781]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Hardware errors before probation expires repeatedly select the failing decoder instead of triggering software fallback.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[555-559]
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[599-609]
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[769-781]
## Recommended Fix
When a hardware decoder fails setup or decoding at an undeclared large size, record that size for software selection before retrying. Preserve the existing handling for unrelated failures.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. A rejected decoder can run indefinitely ✓ Resolved
Description
createDecoder() falls through to hardware when softwareFor matches but the software instance
budget is full, while the matching key also disables probation for that hardware instance. When
another decoder occupies the software slot, the single view can return to hardware that already
produced no frame, with no further fallback deadline.
Code

android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[R500-503]

+        if (sized && sizeKey(type, width, height).equals(softwareFor)) {
+            // The hardware decoder gave no frame at this size: a software one, if it has room
+            String sw = softwareDecoder(type);
+            if (sw != null && InstanceBudget.tryAcquire(softwareSlot(type))) {
Evidence
A failed software-slot acquisition leaves softwareName null, allowing hardware acquisition on the
next condition. The matching softwareFor key then makes onProbation false.

android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[499-509]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[555-559]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[699-705]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[218-229]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A full software budget sends a known-bad stream back to hardware without probation.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[500-509]
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[555-559]
## Recommended Fix
If the software slot is unavailable for a size already marked as failed on hardware, defer decoder creation and retry when capacity is available, or explicitly re-arm a bounded hardware probation. Do not install that hardware decoder indefinitely.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. A later valid SPS cannot correct startup 🐞 Bug ≡ Correctness ⭐ New
Description
decodeFrame() sets spsSeen even when SpsParser.size() returns null, allowing createDecoder()
to configure the decoder at 1280×720. If the first SPS is unreadable and a later valid SPS fits
within confWidth—or confWidth remains zero—the later size does not recreate the decoder, and
probation is never armed for the incorrectly configured hardware decoder.
Code

android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[R691-696]

+                if (isSps) {
+                    spsSeen = true;
+                    if (spsSize != null) {
+                        streamWidth = spsSize[0];
+                        streamHeight = spsSize[1];
+                    }
Evidence
A null parse result still sets spsSeen; that flag permits creation, while sized remains false
and disables probation. Subsequent SPS handling recreates only when a configured maximum is positive
and exceeded.

android/app/src/main/java/com/openipc/decoder/SpsParser.java[21-30]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[496-501]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[564-575]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[690-717]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
An unreadable first SPS opens the decoder-creation gate without providing its size, and a later readable SPS may not cause reconfiguration.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[690-706]
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[711-717]
## Recommended Fix
For large-frame playback, require a successfully parsed size before creating the decoder, or explicitly recreate a decoder initially configured with an unknown size when a valid SPS arrives.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. A changed resolution can stall silently 🐞 Bug ☼ Reliability ⭐ New
Description
decodeFrame() replaces the decoder only when a new SPS exceeds confWidth or confHeight, while
probation is decided only when that decoder is created. If a stream changes to an undeclared
resolution above 1080p that still fits within its configured maximum, the existing decoder receives
that stream without a probation deadline or software fallback.
Code

android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[R711-712]

+            } else if (largeFrames && spsSize != null && confWidth > 0
+                    && (spsSize[0] > confWidth || spsSize[1] > confHeight)) {
Evidence
maxFrameSize sets a maximum independently of whether the codec declares the exact stream size. The
only probation assignment occurs at installation, and the in-session SPS branch tests only whether
that maximum was exceeded.

android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[320-345]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[561-575]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[648-654]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[711-725]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new SPS within the configured maximum can describe a size the current hardware decoder does not declare, but the existing decoder has no fallback timer.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[711-725]
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[571-575]
## Recommended Fix
When a parsed SPS changes the stream size, check support for that size on the current codec and either start a fresh probation period or replace the decoder so fallback remains available.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Delayed key frames trigger false fallback 🐞 Bug ≡ Correctness ⭐ New
Description
decodeFrame() starts probationUntilMs when the final parameter set arrives, rather than when the
first key frame is submitted. If that key frame arrives more than five seconds later, the expiry
check runs before feeding it and marks the hardware decoder as failed despite it never having
received a picture to decode.
Code

android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[R743-746]

+                    if ((configSeen & needed) == needed && awaitingConfig) {
+                        awaitingConfig = false;
+                        // The probation runs from the first key frame on
+                        if (probation) probationUntilMs = nowMs() + PROBATION_MS;
Evidence
Parameter-set completion arms the deadline, and the expiry branch precedes feeding the current NAL.
On expiry, markSoftware persists the fallback choice for that size.

android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[718-750]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[816-843]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Probation can expire before the hardware decoder receives its first key frame, incorrectly selecting software for that size.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[718-725]
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[742-750]
## Recommended Fix
Arm the five-second deadline after submitting the first key-frame NAL following the required parameter sets, rather than when parameter-set collection completes.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View review recommended (5)
6. Software fallback misses the next key frame ✓ Resolved
Description
decodeFrame() drops the NAL that expires probation but leaves spsSeen set, so the following NAL
can create the software decoder without waiting for a new SPS. If expiry happens on an SPS, the
decoder starts at the following PPS and cannot complete awaitingConfig before that burst's key
frame; it must wait for another parameter-set burst.
Code

android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[R719-722]

+                softwareFor = probationKey;
+                dropDecoder(codec);
+                // The next decoder starts at the next SPS, which comes with a key frame
+                return false;
Evidence
spsSeen permits creation on any subsequent NAL, while the new decoder only clears awaitingConfig
after receiving SPS and PPS (plus VPS for H.265). The probation drop does not reset that gate.

android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[687-704]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[715-745]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[803-811]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Probation expiry drops the decoder without resetting the SPS creation gate, allowing the software replacement to start after it has missed the parameter sets needed for the next key frame.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[701-704]
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[715-722]
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[728-745]
## Recommended Fix
On probation expiry, prevent replacement creation until a fresh SPS arrives. Preserve the stream dimensions needed to configure that replacement, and replay the stored H.265 VPS when feeding the fresh SPS.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Earlier failed sizes return to hardware ✓ Resolved
Description
softwareFor stores only one size key, so a probation failure at a second resolution overwrites the
first resolution's fallback decision. When the first resolution returns, createDecoder() selects
hardware again and can spend another five seconds on a decoder that previously gave no frame at that
size.
Code

android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[719]

+                softwareFor = probationKey;
Evidence
Both the software-selection check and probation guard compare against the single softwareFor
field; each expiry assigns a new key to it. Alternating between two failed resolutions therefore
repeats hardware probation for the first.

android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[80-90]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[503-520]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[568-572]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[715-720]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A second failed resolution replaces the sole remembered software-fallback key, allowing an earlier failed resolution to enter hardware probation again.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[87-88]
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[503-505]
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[569-572]
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[716-719]
## Recommended Fix
Track failed hardware size keys in a bounded set rather than a single string, and use membership checks for software selection and probation. Update the configuration- and decode-failure paths that also record failed keys.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. A size change misdirects fallback ✓ Resolved
Description
decodeFrame() overwrites streamWidth and streamHeight on every SPS, but probation expiry uses
those current values rather than the size at which the decoder entered probation. If a smaller SPS
arrives before any output frame, the smaller size is marked for software while the size that failed
on hardware remains eligible for another five-second attempt.
Code

android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[R700-703]

+                Log.w(TAG, "No frame from " + decoderName + " at " + streamWidth + "×"
+                        + streamHeight + " in " + PROBATION_MS / 1000 + " s: software decoder");
+                softwareFor = sizeKey(codecH265 ? "video/hevc" : "video/avc",
+                        streamWidth, streamHeight);
Evidence
The creation-time size determines whether probation starts, but later SPS units replace the size
fields. A smaller SPS does not trigger recreation, leaving expiry to record the newer size.

android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[489-494]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[555-559]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[672-677]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[692-703]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
An SPS size change can cause probation to mark a different size from the one being tested.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[630-635]
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[672-677]
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[699-705]
## Recommended Fix
Store the MIME and picture size when probation starts and use that immutable key on expiry. Re-evaluate or end probation when an SPS changes the size being decoded.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Stopped streams retain their old size ✓ Resolved
Description
stream_size is published whenever streamWidth and streamHeight are set, but disabling the
decoder does not clear those fields or spsSeen. When single-view playback stops without destroying
its surface, stats can describe the previous camera as the current stream, and a subsequent session
can create a decoder using that old size before receiving its own SPS.
Code

android/app/src/main/java/com/openipc/decoder/Decoder.java[R1632-1633]

+            int[] streamSize = videoDecoder.streamSize();
+            if (streamSize != null) single.put("stream_size", streamSize[0] + "x" + streamSize[1]);
Evidence
stopStreams() disables decoding, whereas forgetStream() is called only by
closeDecoderLocked(). Disabling retains the SPS gate and size fields; the new stats field reads
those fields without a live-session check.

android/app/src/main/java/com/openipc/decoder/Decoder.java[1623-1635]
android/app/src/main/java/com/openipc/decoder/Decoder.java[2515-2537]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[380-398]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[431-459]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[672-687]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Stopping single-view playback leaves its SPS size available to stats and the next decoder creation.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/Decoder.java[1630-1635]
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[380-398]
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[452-459]
## Recommended Fix
Reset the saved SPS size, `spsSeen`, and retained parameter sets at the stream-stop boundary, with synchronization against decoding. Publish `stream_size` only when it describes the current session.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


10. Malformed streams report a false size ✓ Resolved
Description
SpsParser calculates H.264 picture dimensions and both codecs’ crop offsets with unchecked 32-bit
arithmetic before valid() checks the result. A malformed SPS can wrap a huge width into an
accepted small value—for example, a width code of 2²⁸ becomes 16 pixels—so that false size reaches
decoder configuration and stream_size.
Code

android/app/src/main/java/com/openipc/decoder/SpsParser.java[R69-70]

+        int widthMbs = b.ue() + 1;
+        int heightUnits = b.ue() + 1;
Evidence
ue() can supply a value whose subsequent multiply wraps in an int; valid() sees only the
wrapped result. The accepted dimensions are stored as stream size and used to construct the
decoder's video format.

android/app/src/main/java/com/openipc/decoder/SpsParser.java[69-84]
android/app/src/main/java/com/openipc/decoder/SpsParser.java[117-134]
android/app/src/main/java/com/openipc/decoder/SpsParser.java[174-180]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[489-494]
android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[662-677]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Malformed SPS dimension or crop values can overflow into apparently valid picture sizes.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/SpsParser.java[69-84]
- android/app/src/main/java/com/openipc/decoder/SpsParser.java[117-134]
- android/app/src/main/java/com/openipc/decoder/SpsParser.java[174-180]
## Recommended Fix
Perform dimension and crop arithmetic in `long`, reject out-of-range unsigned Exp-Golomb values and crop offsets that exceed the picture, and convert to `int` only after all bounds checks pass. Add malformed-SPS tests for wrapped dimensions and crops.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
❌ Tickets: failed to load — Ticket collection could not establish complete evidence
Review mode: Auto: 🧠 Deep: Substantial decoder, SPS parsing, fallback, lifecycle, and format logic spans independent paths.

Grey Divider

Tip of the day
💡 Did you know, you can open the rule or relationship behind a finding in one click

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java Outdated
Comment thread android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java Outdated
Comment thread android/app/src/main/java/com/openipc/decoder/Decoder.java
Comment thread android/app/src/main/java/com/openipc/decoder/SpsParser.java Outdated
…use, SPS bounds (#166)

- A hardware decoder that fails setup or decoding at a size it does not
  declare marks that size for the software decoder, like a probation that
  ran out.
- A size marked for software waits for a free software decoder instead of
  going back to the hardware one that showed nothing.
- The probation remembers the size it started at; a later SPS does not
  change which size is marked.
- A pause forgets the stream's size and parameter sets: the next stream
  sends its own SPS before a decoder is made.
- SpsParser computes sizes and crops in long and rejects codes that would
  wrap round; tests for malformed SPS.
@compot-dev

Copy link
Copy Markdown
Collaborator Author

/review

Comment thread android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java Outdated
Comment thread android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java Outdated
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 4046f6b

…SPS (#166)

softwareFor holds the last 8 sizes the hardware decoder gave no frame for,
not only the latest. Marking a size makes the next decoder wait for a new
SPS, so it gets the whole parameter set before the next key frame.
@compot-dev

Copy link
Copy Markdown
Collaborator Author

/review

@compot-dev
compot-dev merged commit 848c77a into v2 Oct 20, 2026
7 checks passed
@compot-dev
compot-dev deleted the fix/large-frames-166 branch October 20, 2026 18:16
Comment on lines +691 to +696
if (isSps) {
spsSeen = true;
if (spsSize != null) {
streamWidth = spsSize[0];
streamHeight = spsSize[1];
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

3. A later valid sps cannot correct startup 🐞 Bug ≡ Correctness

decodeFrame() sets spsSeen even when SpsParser.size() returns null, allowing createDecoder()
to configure the decoder at 1280×720. If the first SPS is unreadable and a later valid SPS fits
within confWidth—or confWidth remains zero—the later size does not recreate the decoder, and
probation is never armed for the incorrectly configured hardware decoder.
Agent Prompt
## Issue description
An unreadable first SPS opens the decoder-creation gate without providing its size, and a later readable SPS may not cause reconfiguration.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[690-706]
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[711-717]
## Recommended Fix
For large-frame playback, require a successfully parsed size before creating the decoder, or explicitly recreate a decoder initially configured with an unknown size when a valid SPS arrives.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +711 to +712
} else if (largeFrames && spsSize != null && confWidth > 0
&& (spsSize[0] > confWidth || spsSize[1] > confHeight)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

4. A changed resolution can stall silently 🐞 Bug ☼ Reliability

decodeFrame() replaces the decoder only when a new SPS exceeds confWidth or confHeight, while
probation is decided only when that decoder is created. If a stream changes to an undeclared
resolution above 1080p that still fits within its configured maximum, the existing decoder receives
that stream without a probation deadline or software fallback.
Agent Prompt
## Issue description
A new SPS within the configured maximum can describe a size the current hardware decoder does not declare, but the existing decoder has no fallback timer.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[711-725]
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[571-575]
## Recommended Fix
When a parsed SPS changes the stream size, check support for that size on the current codec and either start a fresh probation period or replace the decoder so fallback remains available.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +743 to +746
if ((configSeen & needed) == needed && awaitingConfig) {
awaitingConfig = false;
// The probation runs from the first key frame on
if (probation) probationUntilMs = nowMs() + PROBATION_MS;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

5. Delayed key frames trigger false fallback 🐞 Bug ≡ Correctness

decodeFrame() starts probationUntilMs when the final parameter set arrives, rather than when the
first key frame is submitted. If that key frame arrives more than five seconds later, the expiry
check runs before feeding it and marks the hardware decoder as failed despite it never having
received a picture to decode.
Agent Prompt
## Issue description
Probation can expire before the hardware decoder receives its first key frame, incorrectly selecting software for that size.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[718-725]
- android/app/src/main/java/com/openipc/decoder/MediaCodecManager.java[742-750]
## Recommended Fix
Arm the five-second deadline after submitting the first key-frame NAL following the required parameter sets, rather than when parameter-set collection completes.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 0fe8807

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Snapshot: a turned single-view picture is stretched to the 16:9 cell bug with detection video size at 264 and 265

1 participant