diff --git a/ctrl/sync.sh b/ctrl/sync.sh index 9d5d1c0..4837c48 100755 --- a/ctrl/sync.sh +++ b/ctrl/sync.sh @@ -17,6 +17,7 @@ git -C "$PROJECT_DIR" ls-files --others --ignored --exclude-standard --directory rsync -avz --delete \ --exclude='.git/' \ + --exclude='data/' \ --exclude-from="$EXCLUDE_FILE" \ "$PROJECT_DIR/" \ "${REMOTE}:${REMOTE_PATH}" diff --git a/media/docs/index.html b/media/docs/index.html index 749fbb9..d5a83aa 100644 --- a/media/docs/index.html +++ b/media/docs/index.html @@ -130,8 +130,33 @@ Pipeline + +
Status
+ + State & regressions + + +

Select a diagram

diff --git a/media/docs/server-pipeline.dot b/media/docs/server-pipeline.dot index 4e1cd6a..9bbc0a3 100644 --- a/media/docs/server-pipeline.dot +++ b/media/docs/server-pipeline.dot @@ -68,4 +68,9 @@ digraph server_pipeline { // Python reads files aac_file -> audio_extract [label="reads" style=dashed] active_session -> python [label="discovers\nsession dir" style=dashed] + + // Known regression (2026-04-10) + regression [label="⚠ REGRESSED\n─────────────\nScene relay (separate pipe)\nbreaks 'one behind' flush.\ntry_send drops → decoder\ncorruption until keyframe.\n\nFix: move scene detection\ninto server ffmpeg as 3rd\noutput branch (10-scene-\ndetect-to-rust.md)" + shape=note fillcolor="#3d1e1e" color="#f38ba8" fontcolor="#f38ba8"] + scene_relay -> regression [style=dashed color="#f38ba8"] } diff --git a/media/docs/server-pipeline.svg b/media/docs/server-pipeline.svg index d157d03..bc5be7a 100644 --- a/media/docs/server-pipeline.svg +++ b/media/docs/server-pipeline.svg @@ -4,260 +4,283 @@ - - + + server_pipeline - + cluster_rust - -cht-server (Rust) + +cht-server (Rust) cluster_python - -Python (cht app) + +Python (cht app) net - -TCP :4447 -(WirePacket) + +TCP :4447 +(WirePacket) listener - -Listener -───────────── -TCP accept -reads WirePacket -routes by type: -  Video → ffmpeg + scene relay -  Audio → ADTS file -  Control → session lifecycle + +Listener +───────────── +TCP accept +reads WirePacket +routes by type: +  Video → ffmpeg + scene relay +  Audio → ADTS file +  Control → session lifecycle net->listener - - -WirePacket + + +WirePacket python - -Python GUI -(cht app) + +Python GUI +(cht app) ffmpeg_rec - -ffmpeg subprocess -───────────── -H.264 pipe:0 → 2 outputs: -  1. fMP4 (frag_keyframe) -  2. UDP :4445 (mpegts) + +ffmpeg subprocess +───────────── +H.264 pipe:0 → 2 outputs: +  1. fMP4 (frag_keyframe) +  2. UDP :4445 (mpegts) listener->ffmpeg_rec - - -H.264 video + + +H.264 video scene_relay - -Scene Relay -───────────── -Unix socket (scene.sock) -buffers latest keyframe -best-effort: drops if slow -100ms write timeout + +Scene Relay +───────────── +Unix socket (scene.sock) +buffers latest keyframe +best-effort: drops if slow +100ms write timeout listener->scene_relay - - -H.264 copy -+ keyframe flag + + +H.264 copy ++ keyframe flag audio_writer - -Audio Writer -───────────── -ADTS header + raw AAC -→ stream/audio.aac + +Audio Writer +───────────── +ADTS header + raw AAC +→ stream/audio.aac listener->audio_writer - - -AAC audio + + +AAC audio active_session - - - -active-session -───────────── -file at data/active-session -Python polls to discover -session dir + + + +active-session +───────────── +file at data/active-session +Python polls to discover +session dir listener->active_session - - -on SessionStart + + +on SessionStart fmp4 - -stream/ -recording_000.mp4 -(fragmented MP4) + +stream/ +recording_000.mp4 +(fragmented MP4) ffmpeg_rec->fmp4 - - -copy + + +copy udp_live - -UDP :4445 -(mpegts → mpv) + +UDP :4445 +(mpegts → mpv) ffmpeg_rec->udp_live - - -copy + + +copy scene_ffmpeg - -Scene Detector -───────────── -connects to scene.sock -pipes H.264 → ffmpeg: -  CUDA decode -  select=gt(scene,thresh) -  showinfo → timestamps -  MJPEG → JPEG frames + +Scene Detector +───────────── +connects to scene.sock +pipes H.264 → ffmpeg: +  CUDA decode +  select=gt(scene,thresh) +  showinfo → timestamps +  MJPEG → JPEG frames scene_relay->scene_ffmpeg - - -raw H.264 -(Unix socket) + + +raw H.264 +(Unix socket) + + + +regression + + + +⚠ REGRESSED +───────────── +Scene relay (separate pipe) +breaks 'one behind' flush. +try_send drops → decoder +corruption until keyframe. +Fix: move scene detection +into server ffmpeg as 3rd +output branch (10-scene- +detect-to-rust.md) + + + +scene_relay->regression + + aac_file - -stream/ -audio.aac -(ADTS-wrapped) + +stream/ +audio.aac +(ADTS-wrapped) audio_writer->aac_file - - + + active_session->python - - -discovers -session dir + + +discovers +session dir frames - -frames/ -index.json + *.jpg + +frames/ +index.json + *.jpg scene_ffmpeg->frames - - -JPEG on -scene change + + +JPEG on +scene change audio_extract - -Audio Extractor -───────────── -reads audio.aac -ffmpeg → 16kHz mono WAV -chunks + transcript WAVs + +Audio Extractor +───────────── +reads audio.aac +ffmpeg → 16kHz mono WAV +chunks + transcript WAVs audio_dir - -audio/ -chunk_*.wav -transcript_*.wav + +audio/ +chunk_*.wav +transcript_*.wav audio_extract->audio_dir - - + + transcriber - -Transcriber -───────────── -faster-whisper (CUDA) -segment grouping -slider: chunk size + lines/group + +Transcriber +───────────── +faster-whisper (CUDA) +segment grouping +slider: chunk size + lines/group aac_file->audio_extract - - -reads + + +reads audio_dir->transcriber - - -WAV chunks + + +WAV chunks