// Server pipeline — current implementation // Receiver machine (mcrndeb: X11, RTX 3080, NVDEC) digraph server_pipeline { graph [fontname="monospace" bgcolor="#1e1e2e" rankdir=TB pad="0.6" splines=polyline] node [fontname="monospace" fontcolor="#cdd6f4" style=filled shape=box fillcolor="#313244" color="#585b70" margin="0.25,0.12"] edge [color="#585b70" fontname="monospace" fontcolor="#a6adc8"] net [label="TCP :4447\n(WirePacket)" shape=parallelogram fillcolor="#1e2a3e" color="#89b4fa"] python [label="Python GUI\n(cht app)" shape=parallelogram fillcolor="#2a2a3e" color="#cba6f7"] subgraph cluster_rust { label="cht-server (Rust)" fontcolor="#a6e3a1" color="#a6e3a1" fontname="monospace" listener [label="Listener\n─────────────\nTCP accept\nreads WirePacket\nroutes by type:\n Video → ffmpeg + scene relay\n Audio → ADTS file\n Control → session lifecycle" fillcolor="#1e2d3e" color="#89b4fa"] ffmpeg_rec [label="ffmpeg subprocess\n─────────────\nH.264 pipe:0 → 2 outputs:\n 1. fMP4 (frag_keyframe)\n 2. UDP :4445 (mpegts)" fillcolor="#1e2d3e" color="#89b4fa"] scene_relay [label="Scene Relay\n─────────────\nUnix socket (scene.sock)\nbuffers latest keyframe\nbest-effort: drops if slow\n100ms write timeout" fillcolor="#1e2d3e" color="#89b4fa"] audio_writer [label="Audio Writer\n─────────────\nADTS header + raw AAC\n→ stream/audio.aac" fillcolor="#1e2d3e" color="#89b4fa"] active_session [label="active-session\n─────────────\nfile at data/active-session\nPython polls to discover\nsession dir" shape=note fillcolor="#2a2a3e" color="#585b70"] } subgraph cluster_python { label="Python (cht app)" fontcolor="#cba6f7" color="#cba6f7" fontname="monospace" scene_ffmpeg [label="Scene Detector\n─────────────\nconnects to scene.sock\npipes H.264 → ffmpeg:\n CUDA decode\n select=gt(scene,thresh)\n showinfo → timestamps\n MJPEG → JPEG frames" fillcolor="#2d2038" color="#cba6f7"] audio_extract [label="Audio Extractor\n─────────────\nreads audio.aac\nffmpeg → 16kHz mono WAV\nchunks + transcript WAVs" fillcolor="#2d2038" color="#cba6f7"] transcriber [label="Transcriber\n─────────────\nfaster-whisper (CUDA)\nsegment grouping\nslider: chunk size + lines/group" fillcolor="#2d2038" color="#cba6f7"] } // Flow — Rust server net -> listener [label="WirePacket"] listener -> ffmpeg_rec [label="H.264 video"] listener -> scene_relay [label="H.264 copy\n+ keyframe flag"] listener -> audio_writer [label="AAC audio"] listener -> active_session [style=dashed label="on SessionStart"] // Flow — Python scene detection scene_relay -> scene_ffmpeg [label="raw H.264\n(Unix socket)" color="#a6e3a1"] // Outputs fmp4 [label="stream/\nrecording_000.mp4\n(fragmented MP4)" shape=folder fillcolor="#2a2a3e" color="#585b70"] udp_live [label="UDP :4445\n(mpegts → mpv)" shape=parallelogram fillcolor="#2a2a3e" color="#585b70"] aac_file [label="stream/\naudio.aac\n(ADTS-wrapped)" shape=folder fillcolor="#2a2a3e" color="#585b70"] frames [label="frames/\nindex.json + *.jpg" shape=folder fillcolor="#2a2a3e" color="#585b70"] audio_dir [label="audio/\nchunk_*.wav\ntranscript_*.wav" shape=folder fillcolor="#2a2a3e" color="#585b70"] ffmpeg_rec -> fmp4 [label="copy"] ffmpeg_rec -> udp_live [label="copy"] audio_writer -> aac_file scene_ffmpeg -> frames [label="JPEG on\nscene change"] audio_extract -> audio_dir audio_dir -> transcriber [label="WAV chunks"] // Python reads files aac_file -> audio_extract [label="reads" style=dashed] active_session -> python [label="discovers\nsession dir" style=dashed] }