Skip to content

Commit d0bc6ed

Browse files
committed
Performance minded optimizations
1 parent d56bd54 commit d0bc6ed

3 files changed

Lines changed: 51 additions & 55 deletions

File tree

src/core/main.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <sys/time.h>
1414
#include <sys/utsname.h>
1515
#include <sys/socket.h>
16+
#include <sys/prctl.h>
1617
#include <netinet/in.h>
1718
#include <arpa/inet.h>
1819
#include <limits.h>
@@ -500,6 +501,17 @@ bool is_restart_requested(void) {
500501
int main(int argc, char *argv[]) {
501502
int pid_fd = -1;
502503

504+
// Disable Transparent Huge Pages (THP) for this process.
505+
// The host kernel default of THP=always causes the kernel to promote 4 kB
506+
// anonymous pages to 2 MB huge pages, which dramatically inflates RSS for
507+
// processes like lightnvr that have many small, scattered allocations.
508+
// PR_SET_THP_DISABLE must be set per-process and cannot be inherited, so
509+
// we do it as the very first thing in main().
510+
if (prctl(PR_SET_THP_DISABLE, 1, 0, 0, 0) != 0) {
511+
// Non-fatal – older kernels (< 3.15) don't support this.
512+
fprintf(stderr, "Note: prctl(PR_SET_THP_DISABLE) not supported on this kernel\n");
513+
}
514+
503515
// Save argc/argv for potential restart
504516
saved_argc = argc;
505517
saved_argv = argv;

src/video/go2rtc/go2rtc_stream.c

Lines changed: 34 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -208,73 +208,55 @@ bool go2rtc_stream_register(const char *stream_id, const char *stream_url,
208208

209209
bool result;
210210

211-
// If audio recording is enabled, add FFmpeg transcoding sources
212-
// This creates a stream with multiple sources:
213-
// 1. Primary RTSP source (video pass-through, original audio)
214-
// 2. FFmpeg source that transcodes audio to AAC for MP4 recording compatibility
215-
// 3. FFmpeg source that transcodes audio to OPUS for WebRTC compatibility
216-
// NOTE: The FFmpeg sources are audio-only. Do NOT add #video=copy — that
217-
// would make FFmpeg open a second connection to the camera (or loop
218-
// through go2rtc's own RTSP), creating duplicate video producers and
219-
// causing RTSP 404 errors when the redundant producer fails. Video
220-
// is served directly from the primary RTSP source and does not need
221-
// FFmpeg's involvement.
211+
// Register the stream with go2rtc.
212+
//
213+
// Audio strategy: rely on go2rtc's built-in on-demand transcoding rather
214+
// than spawning persistent ffmpeg producer processes. When a WebRTC viewer
215+
// connects, go2rtc transcodes audio to OPUS in-process on the fly; no
216+
// long-lived ffmpeg process is needed just to keep OPUS available.
217+
//
218+
// When audio recording is enabled we still need a persistent AAC producer
219+
// so that the MP4 recording muxer gets an AAC audio track. In that case
220+
// we register two sources: primary RTSP + ffmpeg AAC. go2rtc will
221+
// transcode to OPUS for WebRTC viewers on demand from the AAC feed.
222+
//
223+
// NOTE: The FFmpeg AAC source is audio-only. Do NOT add #video=copy —
224+
// that would open a second connection to the camera, creating a
225+
// duplicate video producer and RTSP 404 errors. Video is served
226+
// directly from the primary RTSP source.
222227
if (record_audio) {
223-
log_info("Audio recording enabled for stream %s, adding FFmpeg audio transcoding sources", stream_id);
228+
log_info("Audio recording enabled for stream %s, adding FFmpeg AAC source for MP4 recording", stream_id);
224229

225230
// Build the FFmpeg AAC transcoding source URL for recording.
226-
// Audio-only: FFmpeg loops through go2rtc's RTSP to get the audio track,
227-
// transcodes it to AAC, and publishes back. No #video=copy needed.
231+
// Audio-only: FFmpeg reads from go2rtc's internal RTSP, transcodes to
232+
// AAC, and publishes back. go2rtc will transcode to OPUS for WebRTC
233+
// on demand without a separate persistent ffmpeg process.
228234
char ffmpeg_aac_source[URL_BUFFER_SIZE];
229235
snprintf(ffmpeg_aac_source, URL_BUFFER_SIZE, "ffmpeg:%s#audio=aac", stream_id);
230236

231-
// Build the FFmpeg OPUS transcoding source URL for WebRTC
232-
// Format: ffmpeg:stream_id#audio=opus
233-
// OPUS is required for WebRTC audio playback in browsers
234-
char ffmpeg_opus_source[URL_BUFFER_SIZE];
235-
snprintf(ffmpeg_opus_source, URL_BUFFER_SIZE, "ffmpeg:%s#audio=opus", stream_id);
236-
237-
// Register with all three sources
238-
const char *sources[3] = { modified_url, ffmpeg_aac_source, ffmpeg_opus_source };
239-
result = go2rtc_api_add_stream_multi(encoded_stream_id, sources, 3);
237+
const char *sources[2] = { modified_url, ffmpeg_aac_source };
238+
result = go2rtc_api_add_stream_multi(encoded_stream_id, sources, 2);
240239

241240
if (result) {
242-
log_info("Successfully registered stream with go2rtc (with AAC and OPUS audio transcoding): %s", encoded_stream_id);
241+
log_info("Successfully registered stream with go2rtc (with AAC audio for recording): %s", encoded_stream_id);
243242
} else {
244-
log_error("Failed to register stream with go2rtc (with audio transcoding): %s", encoded_stream_id);
245-
// Fall back to two sources (AAC only)
246-
log_warn("Falling back to AAC-only audio transcoding");
247-
const char *sources_aac[2] = { modified_url, ffmpeg_aac_source };
248-
result = go2rtc_api_add_stream_multi(encoded_stream_id, sources_aac, 2);
249-
if (!result) {
250-
// Fall back to single source registration
251-
log_warn("Falling back to single source registration without audio transcoding");
252-
result = go2rtc_api_add_stream(encoded_stream_id, modified_url);
253-
}
243+
log_error("Failed to register stream with go2rtc (with AAC audio): %s", encoded_stream_id);
244+
// Fall back to primary RTSP source only
245+
log_warn("Falling back to single source registration without audio transcoding");
246+
result = go2rtc_api_add_stream(encoded_stream_id, modified_url);
254247
}
255248
} else {
256-
// Even without audio recording, add OPUS transcoding for WebRTC audio playback
257-
// This allows users to hear camera audio in the live view
258-
log_info("Adding OPUS audio transcoding for WebRTC on stream %s", stream_id);
259-
260-
char ffmpeg_opus_source[URL_BUFFER_SIZE];
261-
snprintf(ffmpeg_opus_source, URL_BUFFER_SIZE, "ffmpeg:%s#audio=opus", stream_id);
249+
// No audio recording: register only the primary RTSP source.
250+
// go2rtc transcodes audio to OPUS for WebRTC viewers on demand
251+
// without requiring a persistent ffmpeg process.
252+
log_info("Registering stream %s with primary RTSP source only (on-demand OPUS transcoding by go2rtc)", stream_id);
262253

263-
const char *sources[2] = { modified_url, ffmpeg_opus_source };
264-
result = go2rtc_api_add_stream_multi(encoded_stream_id, sources, 2);
254+
result = go2rtc_api_add_stream(encoded_stream_id, modified_url);
265255

266256
if (result) {
267-
log_info("Successfully registered stream with go2rtc (with OPUS audio): %s", encoded_stream_id);
257+
log_info("Successfully registered stream with go2rtc: %s", encoded_stream_id);
268258
} else {
269-
log_warn("Failed to add OPUS audio, falling back to single source: %s", encoded_stream_id);
270-
// Fall back to single source registration
271-
result = go2rtc_api_add_stream(encoded_stream_id, modified_url);
272-
273-
if (result) {
274-
log_info("Successfully registered stream with go2rtc: %s", encoded_stream_id);
275-
} else {
276-
log_error("Failed to register stream with go2rtc: %s", encoded_stream_id);
277-
}
259+
log_error("Failed to register stream with go2rtc: %s", encoded_stream_id);
278260
}
279261
}
280262

src/web/libuv_server.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,16 @@ static http_server_handle_t libuv_server_init_internal(const http_server_config_
6262
return NULL;
6363
}
6464

65-
// Increase libuv's thread pool size for handler offloading.
65+
// Set libuv's thread pool size for handler offloading.
6666
// All HTTP handlers run on the thread pool via uv_queue_work, so the
6767
// default of 4 threads is too small — slow handlers (ONVIF discovery,
6868
// recording sync) would starve fast handlers (config reads, stream CRUD).
6969
// Must be set before the first uv_loop_init / uv_queue_work call.
70+
// 8 threads is sufficient for typical workloads (was 16, reduced to cut
71+
// per-thread stack RSS, especially important in memory-constrained pods).
7072
if (!getenv("UV_THREADPOOL_SIZE")) {
71-
setenv("UV_THREADPOOL_SIZE", "16", 1);
72-
log_info("libuv_server_init: Set UV_THREADPOOL_SIZE=16 for handler offloading");
73+
setenv("UV_THREADPOOL_SIZE", "8", 1);
74+
log_info("libuv_server_init: Set UV_THREADPOOL_SIZE=8 for handler offloading");
7375
}
7476

7577
libuv_server_t *server = safe_calloc(1, sizeof(libuv_server_t));

0 commit comments

Comments
 (0)