Repository navigation
feat: receive plain RTP from Majestic outgoing over UDP, unicast and multicast (#68) - #72
Conversation
f24733f to
f25e70e
Compare
|
/review |
Code Review by Qodo
1.
|
| @Test | ||
| public void receivesH264AndFollowsASwitchToH265() throws Exception { | ||
| int port = freePort(); | ||
| listen("udp://@:" + port); | ||
| send(port, 96, 3); | ||
| assertTrue(rec.latch.await(3, TimeUnit.SECONDS)); | ||
| assertEquals(1, rec.sdps.size()); | ||
| assertEquals(96, rec.sdps.get(0).videoPt); | ||
| assertFalse(rec.sdps.get(0).videoH265); | ||
| assertTrue(rec.statuses.contains("connected")); | ||
|
|
||
| // The camera switched to H.265: a new session description, then the packets | ||
| rec.latch = new CountDownLatch(2); | ||
| send(port, 97, 2); | ||
| assertTrue(rec.latch.await(3, TimeUnit.SECONDS)); | ||
| assertEquals(2, rec.sdps.size()); | ||
| assertTrue(rec.sdps.get(1).videoH265); | ||
| } |
There was a problem hiding this comment.
2. Required device playback is untested 📎 Requirement gap ☼ Reliability
RtpListenerTest parses a multicast address but receives only synthetic loopback unicast packets, while RtpListener.joinGroup() and MulticastLock.acquire() have no device or traffic verification. On group-A hardware, this leaves untested H.264/H.265 unicast and multicast playback in single and grid views, mid-stream joins, camera restarts, Wi-Fi lock lifetime, Ethernet handling, and Majestic multicast destination and TTL.
Agent Prompt
## Issue description
The PR adds loopback receiver tests and multicast join and lock handling, but lacks device and traffic verification of the required playback scenarios, Wi-Fi lock lifetime, Ethernet behavior, and Majestic multicast delivery and TTL.
## Fix Focus Areas
- android/app/src/test/java/com/openipc/decoder/RtpListenerTest.java[161-178]
- android/app/src/main/java/com/openipc/decoder/RtspSession.java[414-466]
- android/app/src/test/java/com/openipc/decoder/RtpListenerTest.java[161-165]
- android/app/src/main/java/com/openipc/decoder/RtpListener.java[238-264]
- android/app/src/main/java/com/openipc/decoder/RtpListener.java[160-165]
- android/app/src/test/java/com/openipc/decoder/RtpListenerTest.java[48-58]
## Recommended Fix
Execute and record group-A device tests for unicast and multicast H.264/H.265 playback in single and grid views, mid-stream joins, and recovery after camera restarts. Test a multicast slot over Wi-Fi and Ethernet, confirming that the multicast lock is acquired during playback and released when the session stops. Capture or otherwise verify Majestic outgoing traffic to establish its multicast destination and TTL, and add the observed behavior and a multicast receive test to the PR documentation or test coverage.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
Agreed that it needs the device; it is the "Device test" checklist in the PR description (Majestic outgoing unicast and multicast, H.264/H.265, single view and grid, Wi‑Fi and Ethernet, codec switch, camera restart). The test hardware is busy for now, so the PR stays a draft until that round is done; this thread stays open until then.
|
Code done, CI green, Qodo findings addressed. Waiting for the device round in #74. |
…multicast (#68) A slot URL of the form udp://[source@][address]:port[?codec=h264|h265&pt=N] (rtp:// works too, the syntax is VLC's) receives the stream a camera pushes, with no RTSP: Majestic "outgoing" destinations send RTP with payload type 96 (H.264) or 97 (H.265) and repeat the parameter sets before every IDR, so the stream can be joined at any time without an SDP. - udp://@:5600 listens on a port; udp://@239.1.2.3:5600 joins the group; udp://192.168.1.10@239.1.2.3:5600 accepts that sender only. - RtspSession runs it in place of the RTSP handshake, so the watchdog, reconnection and status are the same as for RTSP slots. - The codec follows the payload type and a switch between 96 and 97 on the camera is followed; codec= and pt= force them for other senders. - RtpListener uses a MulticastSocket (DatagramChannel.join needs API 24) and the reorder buffer of #67. Unicast ports are bound exclusively, so a second slot on the same port fails instead of receiving nothing. - Wi-Fi drivers drop multicast unless an app holds a MulticastLock: it is held while any slot receives a group (CHANGE_WIFI_MULTICAST_STATE).
- One reorder buffer per payload type: audio on the same port, with its own sequence numbers, no longer pushes video packets out as late ones. - Video is payload type 96 or 97 unless pt= names another; codec= alone only overrides the codec, so audio arriving first is not taken for video. - A quiet sender is reported (disconnected, then connected again with the next packet) and the port stays bound: a rebind only lost packets and, being counted as a played session, never backed off. - stop() and a starting listener clear the keepalive of an older RTSP connection, so the watchdog cannot send on it and close the listener. - android.hardware.wifi is not required: the multicast permission must not hide the app on Ethernet-only TV boxes (lint LeanbackUsesWifi).
On Linux a socket bound to the wildcard address receives every group joined on that port on the device, so two slots on different groups with the same port would mix their streams (found by the review of #73).
a2c5fd0 to
0378d9c
Compare
9bc9290 to
a00d6f2
Compare
feat: receive plain RTP from Majestic outgoing over UDP, unicast and multicast (#68)
Closes #68. Stacked on #70 (UDP for RTSP), whose reorder buffer it reuses.
What changes
A slot URL
udp://[source@][address]:port[?codec=h264|h265&pt=N]receives the stream a camera pushes, with no RTSP.rtp://works too, and the syntax is VLC's. This is what Majestic sends tooutgoingdestinations: RTP with payload type 96 (H.264) or 97 (H.265), with SPS/PPS (and VPS) repeated in band before every IDR. So the stream can be joined at any time without an SDP.udp://@:5600outgoing:udp://<decoder ip>:5600)udp://@239.1.2.3:5600udp://192.168.1.10@239.1.2.3:5600udp://@:5600?codec=h265&pt=100RtspSessionruns it in place of the RTSP handshake. The watchdog (no video for 8 s → rebind), reconnection with back-off and the status overlay are the same as for RTSP slots. The transport setting of feat: RTSP over UDP unicast, with fallback to TCP (#67) #70 does not apply.video0.codec) produces a new session description, and the decoder is recreated as for an RTSP codec change. Other payload types (audio on the same port, for example) are not taken for video.RtpListeneruses aMulticastSocket, becauseDatagramChannel.joinneeds API 24 and minSdk is 21. Unicast ports are bound exclusively, so a second slot on the same port fails visibly instead of receiving nothing. Multicast ports are shared, so several cells may show one group.MulticastLock. It is held while any slot receives a group (CHANGE_WIFI_MULTICAST_STATE, a normal permission).App.contextgives the lock an application context.Not in this PR: audio from
outgoing(the codec and PT are not described anywhere, so it needs its own setting), and choosing the network interface for a group (the default route is used).Tests
RtpListenerTest:stop()frees the port.Device test (when the hardware is free)
outgoing→udp://<AG-216>:5600, H.264 and H.265, single view and gridudp://239.x.x.x:5600), and receive it on the AG-216 (Ethernet) and over Wi‑Fi