From 32128efd2f8d011d51bbfd0a7561f0c5adcbdd96 Mon Sep 17 00:00:00 2001 From: Domagoj Zecevic Date: Fri, 19 Jun 2026 11:08:04 +0200 Subject: [PATCH] use all cpu cores --- .claude/settings.local.json | 6 +++++- docker-compose-prod.yaml | 16 ++++++++++------ internal/video/stream.go | 6 ++++++ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index b75e843..fb06f67 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -46,7 +46,11 @@ "Bash(pkill -f \"cam-debug\")", "Bash(ffmpeg -loglevel error -i /home/dzecevic/repos/CamMonitor/footage/20260520/record/A260520_153838_153852.265 -c:v libx264 -preset ultrafast -crf 28 -pix_fmt yuv420p -an -movflags frag_keyframe+empty_moov -f mp4 /tmp/stream_h264.mp4)", "Bash(ffmpeg -loglevel error -i footage/20260520/record/A260520_153838_153852.265 -c:v libx264 -preset ultrafast -crf 28 -pix_fmt yuv420p -an -movflags frag_keyframe+empty_moov -f mp4 /tmp/stream_h264.mp4)", - "Bash(echo \"exit:$?\")" + "Bash(echo \"exit:$?\")", + "Bash(time ffmpeg -threads 1 -loglevel error -i footage/20260520/record/A260520_153838_153852.265 -c:v libx264 -preset ultrafast -crf 28 -pix_fmt yuv420p -an -movflags frag_keyframe+empty_moov -f mp4 /dev/null)", + "Bash(time ffmpeg -threads 0 -loglevel error -i footage/20260520/record/A260520_153838_153852.265 -c:v libx264 -preset ultrafast -crf 28 -pix_fmt yuv420p -an -movflags frag_keyframe+empty_moov -f mp4 /dev/null)", + "Bash(time ffmpeg -threads 1 -loglevel error -i footage/20260520/record/A260520_153838_153852.265 -c:v libx264 -preset ultrafast -crf 28 -pix_fmt yuv420p -an -movflags frag_keyframe+empty_moov -f mp4 -y /tmp/bench1.mp4)", + "Bash(time ffmpeg -threads 0 -loglevel error -i footage/20260520/record/A260520_153838_153852.265 -c:v libx264 -preset ultrafast -crf 28 -pix_fmt yuv420p -an -movflags frag_keyframe+empty_moov -f mp4 -y /tmp/bench_all.mp4)" ] } } diff --git a/docker-compose-prod.yaml b/docker-compose-prod.yaml index 18c953d..e935ad9 100644 --- a/docker-compose-prod.yaml +++ b/docker-compose-prod.yaml @@ -61,16 +61,20 @@ services: security_opt: - no-new-privileges:true - # Limit CPU and memory so a spike in thumbnail requests does not starve the - # host (tune to your Atom's actual capacity). + # CPU/memory limits — tuned for a 4-core Intel Atom running ffmpeg transcode. + # cpus limit: allow all 4 cores so the H.265→H.264 transcode can use + # multi-threaded decoding and encoding simultaneously. + # Lower this (e.g. "2.0") if other services share the host and you want + # to guarantee headroom for them. + # memory: 512M covers the Go process + one active ffmpeg transcode at 1080p. deploy: resources: limits: - cpus: "1.0" - memory: 256M + cpus: "4.0" + memory: 512M reservations: - cpus: "0.25" - memory: 64M + cpus: "1.0" + memory: 128M # Aggressive health-check so the orchestrator restarts a hung container fast. healthcheck: diff --git a/internal/video/stream.go b/internal/video/stream.go index b38698b..885175a 100644 --- a/internal/video/stream.go +++ b/internal/video/stream.go @@ -19,6 +19,10 @@ func Stream(w http.ResponseWriter, r *http.Request, absPath, ffmpegPath string) cmd := exec.Command( ffmpegPath, + // Use all CPU cores for decoding the H.265 input. + // This is a global ffmpeg option and must come before -i. + // "0" means auto-detect (= number of cores available to the process). + "-threads", "0", "-loglevel", "error", "-i", absPath, // Transcode H.265 → H.264 for universal browser support. @@ -33,6 +37,8 @@ func Stream(w http.ResponseWriter, r *http.Request, absPath, ffmpegPath string) // without this the picture looks washed out. // -an drops the audio track; security camera clips rarely carry useful // audio and stripping it halves the work for the encoder. + // libx264 picks its own thread count automatically (matching -threads 0 + // above), so no separate -x264-params threads= is needed. "-c:v", "libx264", "-preset", "ultrafast", "-crf", "28",