From 2585748c210a9f79f3072e457e826f1300ab2da9 Mon Sep 17 00:00:00 2001 From: kasperiio <101901857+kasperiio@users.noreply.github.com> Date: Sat, 5 Sep 2026 19:51:56 +0300 Subject: [PATCH 1/2] rtsp: send interleaved frames in one write, stamp per frame, add G.711 On a small SoC the RTSP sender spent more CPU on send() syscalls and on spinning over partial writes than on encoding. - Interleaved (TCP) packets are staged and written once per frame; a send() costs about 100 us here whatever its size. - The socket send buffer is raised to 512 KB, enough for one keyframe, so the sender no longer spins on EAGAIN for as long as the link takes to drain. - Partial writes wait in poll() instead of usleep(1000). - Timestamps are taken once per frame from the encoder's capture time. Stamping only the marker packet gave every earlier packet of a frame the previous frame's time, so receivers saw timestamps go backwards within a frame and players stalled and then raced to catch up. - G.711 A-law (payload type 8) as an alternative to MP3 for RTSP audio, which also lets the software MP3 encoder be skipped when nothing consumes MP3. - The MP4 muxer no longer builds moof/mdat when no HTTP client is connected, and resets its cached header when the stream configuration changes. - /api/rtsp and /api/onvif expose those settings, with the matching web UI. - bitbuf: copy with memcpy instead of a byte loop. Review fixes: - __tcp_flush_each() gated on track 0, so a client that set up only audio over interleaved TCP staged packets that were never flushed. - The RTSP audio codec is now latched when the server starts. Changing it through /api/rtsp used to switch the payload type and clock under sessions already negotiated for the other one; the API documented a restart but the media path read the value live. Both the API and the config parser now accept only pcma and mp3. - The 8 kHz resampling ratio is carried in 16.16 fixed point. Truncating srate / 8000 emitted 8820 samples a second at 44.1 kHz against an 8 kHz SDP and RTP clock, so G.711 ran fast and packets were 18.1 ms rather than 20. - G.711 timestamps advance by the samples actually sent rather than by millis(). - rtsp.audio_codec is parsed with a new bounded parse_param_value_n(); the unbounded parse_param_value() sprintf()s into the caller's buffer, which overflows the eight-byte field. Existing callers are unchanged. - /api/rtsp and /api/onvif escape the usernames and codec they echo back, which otherwise made the response unparseable for the web UI. --- res/index.html | 33 ++++++++++ src/app_config.c | 14 +++++ src/app_config.h | 1 + src/fmt/bitbuf.c | 4 +- src/fmt/moov.c | 3 +- src/fmt/mp4.c | 4 ++ src/hal/config.c | 21 ++++++- src/hal/config.h | 6 ++ src/main.c | 1 + src/media.c | 75 +++++++++++++++++++++- src/media.h | 2 + src/rtsp/rtp.c | 139 +++++++++++++++++++++++++++++++++++++++-- src/rtsp/rtsp.c | 22 ++++++- src/rtsp/rtsp.h | 5 ++ src/rtsp/rtsp_server.h | 1 + src/server.c | 120 +++++++++++++++++++++++++++++++++++ src/server.h | 1 + 17 files changed, 439 insertions(+), 13 deletions(-) diff --git a/res/index.html b/res/index.html index bf037ac0..78df54c9 100644 --- a/res/index.html +++ b/res/index.html @@ -372,6 +372,8 @@ apiRead('mjpeg'); apiRead('night'); apiRead('record'); + apiRead('rtsp'); + apiRead('onvif'); toggleRecord(document.getElementById('record_state')); apiRead('time'); @@ -676,6 +678,37 @@

Media

+

Network

+
+
+
+ RTSP + + + + + + + +
+ Apply + Port and codec changes take effect after a restart; use Save to keep them. +
+
+
+
+ ONVIF + + + + + +
+ Apply + Applies after a restart; use Save to keep it. +
+
+

OSD

-

Network

-
-
-
- RTSP - - - - - - - -
- Apply - Port and codec changes take effect after a restart; use Save to keep them. -
-
-
-
- ONVIF - - - - - -
- Apply - Applies after a restart; use Save to keep it. -
-
-

OSD

+ +