From 455bcc08221e1706eac49244bccc88796f9126f6 Mon Sep 17 00:00:00 2001 From: Compot Developer Date: Thu, 22 Oct 2026 08:15:42 +0000 Subject: [PATCH 1/3] fix: udp:// receive thread at the audio priority, no drops in a loaded 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. --- .../main/java/com/openipc/decoder/RtpListener.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/android/app/src/main/java/com/openipc/decoder/RtpListener.java b/android/app/src/main/java/com/openipc/decoder/RtpListener.java index df1a597..1712e86 100644 --- a/android/app/src/main/java/com/openipc/decoder/RtpListener.java +++ b/android/app/src/main/java/com/openipc/decoder/RtpListener.java @@ -9,6 +9,7 @@ import android.content.Context; import android.net.wifi.WifiManager; +import android.os.Process; import android.util.Log; import java.io.BufferedReader; @@ -35,7 +36,9 @@ *

* An app cannot get a socket buffer above {@code rmem_max} (256 KiB on many boxes), which is * about one 1080p key frame, so a thread of its own does nothing but read the socket into a - * ring; reordering, assembly and the decoder never hold up the receive (#251). + * ring; reordering, assembly and the decoder never hold up the receive (#251). It runs at the + * audio priority: in a loaded grid a thread at the display priority still waited long enough for + * a key-frame burst to fill the socket buffer (#253). */ class RtpListener implements Closeable { private static final String TAG = "OpenIPCDecoder"; @@ -237,6 +240,13 @@ boolean h265(int pt) { * four bytes, copied from one large buffer: datagrams are short, slots stay small. */ private void readLoop() { + // Nice -19, above the decoder and the UI (Thread.MAX_PRIORITY is only -8): a stalled + // receive is a lost datagram (#253) + try { + Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO); + } catch (RuntimeException e) { + Log.w(TAG, tag + ": UDP receive thread keeps its priority: " + e.getMessage()); + } byte[] buf = new byte[65536]; DatagramPacket p = new DatagramPacket(buf, buf.length); InetAddress source = url.source; @@ -283,8 +293,6 @@ void run(UdpReceiver.Condition keepGoing, Handler handler) throws IOException { RtpReorder.Sink sink = handler::onPacket; Thread reader = new Thread(this::readLoop, tag + "-udp"); reader.setDaemon(true); - // Above the decoder and the UI: a stalled receive is a lost packet - reader.setPriority(Thread.MAX_PRIORITY); reader.start(); try { while (!closed && keepGoing.holds()) { From 2167fc8769961de5247598376fd56c428207ac6c Mon Sep 17 00:00:00 2001 From: Compot Developer Date: Thu, 22 Oct 2026 08:20:00 +0000 Subject: [PATCH 2/3] fix: the udp:// receive thread keeps nice -8 when the audio priority 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. --- android/app/src/main/java/com/openipc/decoder/RtpListener.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/android/app/src/main/java/com/openipc/decoder/RtpListener.java b/android/app/src/main/java/com/openipc/decoder/RtpListener.java index 1712e86..4b0c45b 100644 --- a/android/app/src/main/java/com/openipc/decoder/RtpListener.java +++ b/android/app/src/main/java/com/openipc/decoder/RtpListener.java @@ -293,6 +293,9 @@ void run(UdpReceiver.Condition keepGoing, Handler handler) throws IOException { RtpReorder.Sink sink = handler::onPacket; Thread reader = new Thread(this::readLoop, tag + "-udp"); reader.setDaemon(true); + // Nice -8 before the thread starts; readLoop() raises it to -19 and keeps this when the + // system refuses (Qodo on #254) + reader.setPriority(Thread.MAX_PRIORITY); reader.start(); try { while (!closed && keepGoing.holds()) { From e93848edf39776931f6515ae04c9bf7c497e06c8 Mon Sep 17 00:00:00 2001 From: Compot Developer Date: Thu, 22 Oct 2026 08:15:42 +0000 Subject: [PATCH 3/3] 2.18.4: udp:// receive thread at the audio priority (#253) --- CHANGELOG.md | 6 ++++++ android/app/build.gradle | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c52eb83..2b3e271 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ Versions after 2.17 are `MAJOR.MINOR.PATCH` (2.17 is followed by 2.17.1 or 2.18. `docs/testing.md`, "Versions and releases"); the headings below keep the numbers as they were released. +## 2.18.4 — 2026-10-22 + +- `udp://` slots and the U cell: the receive thread runs at the audio priority, so a push no + longer loses datagrams in a loaded 3×3 grid; before, key-frame bursts still overflowed the + socket buffer while the thread waited for the CPU (#253). + ## 2.18.3 — 2026-10-21 - `udp://` slots and the U cell: a thread of its own reads the socket into a ring of 1024 diff --git a/android/app/build.gradle b/android/app/build.gradle index de0d418..728f651 100755 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -23,8 +23,8 @@ android { applicationId "com.openipc.decoder" minSdk 21 targetSdk 36 - versionCode 46 - versionName "2.18.3" + versionCode 47 + versionName "2.18.4" buildConfigField "String", "GIT_HASH", "\"${getGitHash()}\"" }