Skip to content

udp:// receive thread at the audio priority: no drops in a loaded grid (#253) - #254

Merged
compot-dev merged 3 commits into
v2from
perf/udp-grid-drops-253
Oct 22, 2026
Merged

compot-dev merged 3 commits into
v2from
perf/udp-grid-drops-253

Conversation

@compot-dev

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

Copy link
Copy Markdown
Collaborator

Fixes #253.

The udp:// receive thread from #252 ran at Thread.MAX_PRIORITY, which is nice −8 on Android. In a loaded 3×3 grid it still waited for the CPU long enough for a key-frame burst to fill the socket buffer (rmem_max caps it at 512 KiB). Now it runs at Process.THREAD_PRIORITY_URGENT_AUDIO (nice −19).

Measured

Test box with 4 cores, 3×3 grid of 8 cameras plus the U cell, load average 19–26. The U cell gets an H.265 1080p push from Majestic at 8 Mbit/s (~700 datagrams/s). One build with the receive variant switched at run time, three 4-minute runs per variant, interleaved. Per second, from /stats.json → cells[U].udp:

Receive thread rtp_lost socket_drops ring_dropped
MAX_PRIORITY + DatagramPacket (2.18.3) 49.5 (60 / 54 / 35) 27.9 3.05
URGENT_AUDIO + DatagramPacket (this PR) 5.2 (10 / 3.3 / 1.8) 0.05 0
MAX_PRIORITY + DatagramChannel 18.7 — 0
URGENT_AUDIO + DatagramChannel 10.4 (24 / 3.5 / 3.4) — 0

The rest of rtp_lost is lost on the network. Over one minute the camera sent 43 442 UDP datagrams (OutDatagrams, no SndbufErrors), the app received 42 791, and the box kernel counted no RcvbufErrors.

Ideas 2 and 3 from #253 are left out:

  • DatagramChannel into a direct buffer: no gain over the priority alone.
  • Native recvmmsg() through JNI: built and ready to measure, but with nothing left to drop in the box it would only add the NDK to the build.

Checks

  • testDebugUnitTest (371 tests), lintDebug and the release build pass locally in the Android build container.
  • Version 2.18.4 (versionCode 47), CHANGELOG.md section added.
  • The CI build of e93848e on the test box in the grid: see the comment below.

…d grid (#253)

Thread.MAX_PRIORITY is nice -8. In the 3x3 grid (load ~24 on the 4-core
test box) that thread still waited long enough for a key-frame burst to
fill the socket buffer. THREAD_PRIORITY_URGENT_AUDIO (nice -19), H.265
1080p at 8 Mbit/s, three 4-minute runs each: rtp_lost 49.5/s -> 5.2/s,
socket_drops 27.9/s -> 0.05/s, ring_dropped 3.05/s -> 0. The rest is lost
on the network: the box kernel drops nothing.
@compot-dev compot-dev added the in progress Being worked on right now (open PR) label Oct 22, 2026
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Prioritize UDP reception to prevent drops in loaded grids

🐞 Bug fix ⚙️ Configuration changes 📝 Documentation 🕐 10-20 Minutes

Grey Divider

AI Description

• Run the UDP receive thread at Android’s urgent-audio priority to reduce socket-buffer overflows
 under load.
• Keep the existing socket and receive ring; measurements showed no benefit from switching to
 DatagramChannel.
• Document the fix and release it as version 2.18.4.
Diagram

graph TD
  S[("UDP socket")] --> R["Receive thread"] --> Q["Datagram ring"] --> H["RTP processing"]
  P["Android Process API"] -->|"urgent-audio priority"| R
Loading
High-Level Assessment

Keep the targeted priority change and existing DatagramPacket receive path. The reported loaded-grid measurements show near-zero socket drops with urgent-audio priority; DatagramChannel showed no further benefit, while JNI recvmmsg would add build complexity without a demonstrated need.

Files changed (3) +19 / -5

Bug fix (1) +11 / -3
RtpListener.javaSet UDP reader to urgent-audio priority +11/-3

Set UDP reader to urgent-audio priority

• Sets the receive thread’s Android priority at the start of its read loop instead of using Java’s MAX_PRIORITY when creating the thread. Logs a warning and continues receiving if the priority call fails.

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

Documentation (1) +6 / -0
CHANGELOG.mdDocument the UDP receive fix in 2.18.4 +6/-0

Document the UDP receive fix in 2.18.4

• Adds a release note explaining that higher receive-thread priority prevents key-frame bursts from overflowing the socket buffer in a loaded grid.

CHANGELOG.md

Other (1) +2 / -2
build.gradleBump Android app version to 2.18.4 +2/-2

Bump Android app version to 2.18.4

• Increments versionCode from 46 to 47 and versionName from 2.18.3 to 2.18.4.

android/app/build.gradle

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

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

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Priority failures increase UDP drops ✓ Resolved
Description
readLoop() catches a failure to set urgent-audio priority, but removing
reader.setPriority(Thread.MAX_PRIORITY) leaves the receive thread at its default priority instead
of the previous elevated priority. On a device that rejects the new setting, a loaded grid can
therefore delay socket reads even more than before and overflow the receive buffer during key-frame
bursts.
Code

android/app/src/main/java/com/openipc/decoder/RtpListener.java[L286-287]

-        // Above the decoder and the UI: a stalled receive is a lost packet
-        reader.setPriority(Thread.MAX_PRIORITY);
Evidence
The new priority-setting call explicitly allows failure and continues receiving; the reader is
started without another priority assignment. The class documents why delayed reads can fill the
socket buffer.

android/app/src/main/java/com/openipc/decoder/RtpListener.java[243-249]
android/app/src/main/java/com/openipc/decoder/RtpListener.java[292-296]
android/app/src/main/java/com/openipc/decoder/RtpListener.java[36-41]

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

## Issue description
If urgent-audio priority cannot be set, the UDP reader now runs at its default priority rather than the previously configured maximum Java thread priority.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/RtpListener.java[243-249]
- android/app/src/main/java/com/openipc/decoder/RtpListener.java[293-296]
## Recommended Fix
Set the reader's Java priority to `Thread.MAX_PRIORITY` before starting it, then attempt to raise its Android thread priority inside `readLoop()` as currently done. If that attempt fails, the reader retains the previous elevated priority.

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


Grey Divider

Tip of the day
💡 Did you know, you can ask Qodo to dismiss a finding you disagree with, with your reason on record

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

…is refused (#253)

Qodo on #254: Thread.MAX_PRIORITY is set again before start, so a system
that refuses THREAD_PRIORITY_URGENT_AUDIO leaves it where 2.18.3 had it.
@compot-dev
compot-dev force-pushed the perf/udp-grid-drops-253 branch from 1b17d42 to e93848e Compare October 22, 2026 08:20
@compot-dev

Copy link
Copy Markdown
Collaborator Author

/review

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

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit e93848e

@compot-dev

Copy link
Copy Markdown
Collaborator Author

CI build of e93848e (2.18.4) on the test box, same 3×3 grid and push (H.265 1080p at 8 Mbit/s, load average 23–26), three 4-minute runs. Per second, from /stats.json → cells[U].udp:

Build received rtp_lost socket_drops ring_dropped
2.18.3, runs from the PR description 643–669 49.5 (60 / 54 / 35) 27.9 3.05
2.18.4 CI 689–691 12.0 (12.4 / 11.0 / 12.5) 0.84 (1.15 / 0.54 / 0.84) 0

The receive thread Quad-8-udp runs at nice −19. The kernel now drops under 1 datagram/s. Most of the remaining rtp_lost doesn't reach the box at all: the gap is between what the camera sends and what the box kernel receives.

@compot-dev
compot-dev merged commit 9cf9d0d into v2 Oct 22, 2026
7 checks passed
@compot-dev
compot-dev deleted the perf/udp-grid-drops-253 branch October 22, 2026 08:37
@compot-dev compot-dev removed the in progress Being worked on right now (open PR) label Oct 22, 2026
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.

UDP push in a loaded grid: the kernel socket buffer still overflows on key-frame bursts

1 participant