update docs

This commit is contained in:
2026-05-06 11:51:43 -03:00
parent c8bb6c7581
commit 946234eb9e
16 changed files with 1723 additions and 502 deletions

View File

@@ -1,21 +0,0 @@
#!/bin/bash
# Re-render all Graphviz diagrams to SVG.
# Run this after each phase when .dot files are updated.
# Usage: ./docs.sh
set -euo pipefail
DOCS_DIR="$(cd "$(dirname "$0")/../docs" && pwd)"
if ! command -v dot &>/dev/null; then
echo "graphviz not found — install with: sudo apt install graphviz" >&2
exit 1
fi
for f in "$DOCS_DIR"/*.dot; do
svg="${f%.dot}.svg"
echo "==> $(basename "$f")$(basename "$svg")"
dot -Tsvg "$f" -o "$svg"
done
echo "==> done. Serving at http://localhost:9099 (ctrl-c to stop)"
cd "$DOCS_DIR" && python3 -m http.server 9099

View File

@@ -1,66 +0,0 @@
// Client pipeline data flow
// Sender machine (Wayland, VAAPI GPU)
digraph client_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" labelfontname="monospace"]
// Hardware
drm [label="/dev/dri/card0\n(KMS scanout)" shape=cylinder fillcolor="#1e3a2f" color="#a6e3a1"]
vaapi [label="/dev/dri/renderD128\n(VAAPI)" shape=cylinder fillcolor="#1e3a2f" color="#a6e3a1"]
pulse [label="PulseAudio\n─────────────\nmonitor: default_sink.monitor\nmic: default-source" shape=cylinder fillcolor="#1e3a2f" color="#a6e3a1"]
net [label="TCP :4447\nmcrndeb" shape=parallelogram fillcolor="#1e2a3e" color="#89b4fa"]
subgraph cluster_main {
label="main thread (tokio async)" fontcolor="#a6adc8" color="#45475a" fontname="monospace"
wait_server [label="wait_for_server\n─────────────\nretry connect / 2s\nCtrl-C to cancel" fillcolor="#2d2038" color="#cba6f7"]
session_start [label="session_start\n─────────────\nid: YYYYMMDD_HHMMSS\nvideo + audio params" fillcolor="#2d2038" color="#cba6f7"]
mux [label="select!\npkt_rx | keepalive | ctrl-c" fillcolor="#2d2038" color="#cba6f7"]
write [label="BufWriter\nwrite_packet()" fillcolor="#1e2d3e" color="#89b4fa"]
shutdown [label="Shutdown\n─────────────\npipeline.stop() (5s timeout)\nSessionStop (2s timeout)\nsingle Ctrl-C" fillcolor="#2d2038" color="#cba6f7"]
}
subgraph cluster_subprocess {
label="Subprocess backend (default)" fontcolor="#a6adc8" color="#45475a" fontname="monospace"
ffmpeg_cli [label="ffmpeg subprocess\n─────────────\nkmsgrab → VAAPI h264\n+ PulseAudio inputs:\n amix(monitor, mic)\noutput: NUT pipe" fillcolor="#1e2d3e" color="#89b4fa"]
demux [label="NUT Demuxer\n─────────────\nffmpeg-next in-process\nfinds video + audio streams\nsends EncodedPacket\n { data, pts, media_type }" fillcolor="#1e2d3e" color="#89b4fa"]
chan [label="mpsc::channel(64)\nEncodedPacket" shape=parallelogram fillcolor="#2d2038" color="#cba6f7"]
}
subgraph cluster_direct {
label="VaapiDirect backend (experimental)" fontcolor="#6c7086" color="#45475a" fontname="monospace" style=dashed
capture [label="KmsCapture\n─────────────\nDRM_PRIME frames" fillcolor="#2d1e1e" color="#f38ba8"]
encoder [label="VaapiEncoder\n─────────────\nhwmap → scale_vaapi\nh264_vaapi QP=20" fillcolor="#2d1e1e" color="#f38ba8"]
}
// Flow — subprocess
drm -> ffmpeg_cli [label="kmsgrab"]
vaapi -> ffmpeg_cli [label="h264_vaapi"]
pulse -> ffmpeg_cli [label="-f pulse\nmonitor + mic"]
ffmpeg_cli -> demux [label="NUT pipe\n(stdout)"]
demux -> chan [label="EncodedPacket\n(Video or Audio)"]
// Flow — direct (dashed, experimental)
drm -> capture [style=dashed]
vaapi -> encoder [style=dashed]
capture -> encoder [style=dashed label="DRM_PRIME"]
encoder -> chan [style=dashed]
// Flow — main
chan -> mux
wait_server -> session_start
session_start -> write
mux -> write [label="WirePacket\nVideo | Audio"]
write -> net
mux -> shutdown [label="Ctrl-C or\nchannel closed"]
// Types note
types [label="WirePacket types\n─────────────\nVideo: H.264 NALUs + keyframe flag\nAudio: AAC frames\nControl: SessionStart/Stop/Keepalive"
shape=note fillcolor="#2a2a3e" color="#585b70"]
}

View File

@@ -1,264 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 14.1.2 (0)
-->
<!-- Title: client_pipeline Pages: 1 -->
<svg width="1291pt" height="1237pt"
viewBox="0.00 0.00 1291.00 1237.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(43.2 1194.19)">
<title>client_pipeline</title>
<polygon fill="#1e1e2e" stroke="none" points="-43.2,43.2 -43.2,-1194.19 1248.2,-1194.19 1248.2,43.2 -43.2,43.2"/>
<g id="clust1" class="cluster">
<title>cluster_main</title>
<polygon fill="#1e1e2e" stroke="#45475a" points="703,-132.56 703,-566.04 1197,-566.04 1197,-132.56 703,-132.56"/>
<text xml:space="preserve" text-anchor="middle" x="950" y="-548.74" font-family="monospace" font-size="14.00" fill="#a6adc8">main thread (tokio async)</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_subprocess</title>
<polygon fill="#1e1e2e" stroke="#45475a" points="313,-429.87 313,-971.85 695,-971.85 695,-429.87 313,-429.87"/>
<text xml:space="preserve" text-anchor="middle" x="504" y="-954.55" font-family="monospace" font-size="14.00" fill="#a6adc8">Subprocess backend (default)</text>
</g>
<g id="clust3" class="cluster">
<title>cluster_direct</title>
<polygon fill="#1e1e2e" stroke="#45475a" stroke-dasharray="5,2" points="8,-635.79 8,-945.97 305,-945.97 305,-635.79 8,-635.79"/>
<text xml:space="preserve" text-anchor="middle" x="156.5" y="-928.67" font-family="monospace" font-size="14.00" fill="#6c7086">VaapiDirect backend (experimental)</text>
</g>
<!-- drm -->
<g id="node1" class="node">
<title>drm</title>
<path fill="#1e3a2f" stroke="#a6e3a1" d="M258.75,-1120.79C258.75,-1124.37 224.8,-1127.27 183,-1127.27 141.2,-1127.27 107.25,-1124.37 107.25,-1120.79 107.25,-1120.79 107.25,-1062.54 107.25,-1062.54 107.25,-1058.97 141.2,-1056.07 183,-1056.07 224.8,-1056.07 258.75,-1058.97 258.75,-1062.54 258.75,-1062.54 258.75,-1120.79 258.75,-1120.79"/>
<path fill="none" stroke="#a6e3a1" d="M258.75,-1120.79C258.75,-1117.22 224.8,-1114.32 183,-1114.32 141.2,-1114.32 107.25,-1117.22 107.25,-1120.79"/>
<text xml:space="preserve" text-anchor="middle" x="183" y="-1095.62" font-family="monospace" font-size="14.00" fill="#cdd6f4">/dev/dri/card0</text>
<text xml:space="preserve" text-anchor="middle" x="183" y="-1078.37" font-family="monospace" font-size="14.00" fill="#cdd6f4">(KMS scanout)</text>
</g>
<!-- ffmpeg_cli -->
<g id="node10" class="node">
<title>ffmpeg_cli</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="529.5,-938.6 328.5,-938.6 328.5,-817.82 529.5,-817.82 529.5,-938.6"/>
<text xml:space="preserve" text-anchor="middle" x="429" y="-916.66" font-family="monospace" font-size="14.00" fill="#cdd6f4">ffmpeg subprocess</text>
<text xml:space="preserve" text-anchor="middle" x="429" y="-899.41" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="429" y="-882.16" font-family="monospace" font-size="14.00" fill="#cdd6f4">kmsgrab → VAAPI h264</text>
<text xml:space="preserve" text-anchor="middle" x="429" y="-864.91" font-family="monospace" font-size="14.00" fill="#cdd6f4">+ PulseAudio inputs:</text>
<text xml:space="preserve" text-anchor="middle" x="429" y="-847.66" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;amix(monitor, mic)</text>
<text xml:space="preserve" text-anchor="middle" x="429" y="-830.41" font-family="monospace" font-size="14.00" fill="#cdd6f4">output: NUT pipe</text>
</g>
<!-- drm&#45;&gt;ffmpeg_cli -->
<g id="edge1" class="edge">
<title>drm&#45;&gt;ffmpeg_cli</title>
<path fill="none" stroke="#585b70" d="M223.62,-1055.75C258.09,-1026.12 308.54,-982.76 350.81,-946.42"/>
<polygon fill="#585b70" stroke="#585b70" points="352.97,-949.18 358.27,-940.01 348.41,-943.87 352.97,-949.18"/>
<text xml:space="preserve" text-anchor="middle" x="334.88" y="-992.42" font-family="monospace" font-size="14.00" fill="#a6adc8">kmsgrab</text>
</g>
<!-- capture -->
<g id="node13" class="node">
<title>capture</title>
<polygon fill="#2d1e1e" stroke="#f38ba8" points="267,-912.72 99,-912.72 99,-843.69 267,-843.69 267,-912.72"/>
<text xml:space="preserve" text-anchor="middle" x="183" y="-890.78" font-family="monospace" font-size="14.00" fill="#cdd6f4">KmsCapture</text>
<text xml:space="preserve" text-anchor="middle" x="183" y="-873.53" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="183" y="-856.28" font-family="monospace" font-size="14.00" fill="#cdd6f4">DRM_PRIME frames</text>
</g>
<!-- drm&#45;&gt;capture -->
<g id="edge6" class="edge">
<title>drm&#45;&gt;capture</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M183,-1055.75C183,-1019.85 183,-963.78 183,-924.49"/>
<polygon fill="#585b70" stroke="#585b70" points="186.5,-924.64 183,-914.64 179.5,-924.64 186.5,-924.64"/>
</g>
<!-- vaapi -->
<g id="node2" class="node">
<title>vaapi</title>
<path fill="#1e3a2f" stroke="#a6e3a1" d="M469.38,-1120.79C469.38,-1124.37 426.18,-1127.27 373,-1127.27 319.82,-1127.27 276.62,-1124.37 276.62,-1120.79 276.62,-1120.79 276.62,-1062.54 276.62,-1062.54 276.62,-1058.97 319.82,-1056.07 373,-1056.07 426.18,-1056.07 469.38,-1058.97 469.38,-1062.54 469.38,-1062.54 469.38,-1120.79 469.38,-1120.79"/>
<path fill="none" stroke="#a6e3a1" d="M469.38,-1120.79C469.38,-1117.22 426.18,-1114.32 373,-1114.32 319.82,-1114.32 276.62,-1117.22 276.62,-1120.79"/>
<text xml:space="preserve" text-anchor="middle" x="373" y="-1095.62" font-family="monospace" font-size="14.00" fill="#cdd6f4">/dev/dri/renderD128</text>
<text xml:space="preserve" text-anchor="middle" x="373" y="-1078.37" font-family="monospace" font-size="14.00" fill="#cdd6f4">(VAAPI)</text>
</g>
<!-- vaapi&#45;&gt;ffmpeg_cli -->
<g id="edge2" class="edge">
<title>vaapi&#45;&gt;ffmpeg_cli</title>
<path fill="none" stroke="#585b70" d="M382.25,-1055.75C389.85,-1027.05 400.86,-985.46 410.29,-949.86"/>
<polygon fill="#585b70" stroke="#585b70" points="413.6,-951.04 412.77,-940.48 406.83,-949.25 413.6,-951.04"/>
<text xml:space="preserve" text-anchor="middle" x="442.25" y="-992.42" font-family="monospace" font-size="14.00" fill="#a6adc8">h264_vaapi</text>
</g>
<!-- encoder -->
<g id="node14" class="node">
<title>encoder</title>
<polygon fill="#2d1e1e" stroke="#f38ba8" points="297.38,-730.07 104.62,-730.07 104.62,-643.79 297.38,-643.79 297.38,-730.07"/>
<text xml:space="preserve" text-anchor="middle" x="201" y="-708.13" font-family="monospace" font-size="14.00" fill="#cdd6f4">VaapiEncoder</text>
<text xml:space="preserve" text-anchor="middle" x="201" y="-690.88" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="201" y="-673.63" font-family="monospace" font-size="14.00" fill="#cdd6f4">hwmap → scale_vaapi</text>
<text xml:space="preserve" text-anchor="middle" x="201" y="-656.38" font-family="monospace" font-size="14.00" fill="#cdd6f4">h264_vaapi QP=20</text>
</g>
<!-- vaapi&#45;&gt;encoder -->
<g id="edge7" class="edge">
<title>vaapi&#45;&gt;encoder</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M354.14,-1055.66C335.27,-1020.61 309,-971.85 309,-971.85 309,-971.85 272,-765.32 272,-765.32 272,-765.32 260.89,-753.21 247.47,-738.58"/>
<polygon fill="#585b70" stroke="#585b70" points="250.19,-736.37 240.85,-731.36 245.03,-741.1 250.19,-736.37"/>
</g>
<!-- pulse -->
<g id="node3" class="node">
<title>pulse</title>
<path fill="#1e3a2f" stroke="#a6e3a1" d="M762.62,-1140.2C762.62,-1146.15 700.94,-1150.99 625,-1150.99 549.06,-1150.99 487.38,-1146.15 487.38,-1140.2 487.38,-1140.2 487.38,-1043.13 487.38,-1043.13 487.38,-1037.18 549.06,-1032.35 625,-1032.35 700.94,-1032.35 762.62,-1037.18 762.62,-1043.13 762.62,-1043.13 762.62,-1140.2 762.62,-1140.2"/>
<path fill="none" stroke="#a6e3a1" d="M762.62,-1140.2C762.62,-1134.25 700.94,-1129.41 625,-1129.41 549.06,-1129.41 487.38,-1134.25 487.38,-1140.2"/>
<text xml:space="preserve" text-anchor="middle" x="625" y="-1112.87" font-family="monospace" font-size="14.00" fill="#cdd6f4">PulseAudio</text>
<text xml:space="preserve" text-anchor="middle" x="625" y="-1095.62" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="625" y="-1078.37" font-family="monospace" font-size="14.00" fill="#cdd6f4">monitor: default_sink.monitor</text>
<text xml:space="preserve" text-anchor="middle" x="625" y="-1061.12" font-family="monospace" font-size="14.00" fill="#cdd6f4">mic: default&#45;source</text>
</g>
<!-- pulse&#45;&gt;ffmpeg_cli -->
<g id="edge3" class="edge">
<title>pulse&#45;&gt;ffmpeg_cli</title>
<path fill="none" stroke="#585b70" d="M570.66,-1032.05C546.33,-1005.79 517.48,-974.67 492.19,-947.38"/>
<polygon fill="#585b70" stroke="#585b70" points="494.86,-945.11 485.49,-940.16 489.73,-949.87 494.86,-945.11"/>
<text xml:space="preserve" text-anchor="middle" x="603.94" y="-1001.05" font-family="monospace" font-size="14.00" fill="#a6adc8">&#45;f pulse</text>
<text xml:space="preserve" text-anchor="middle" x="603.94" y="-983.8" font-family="monospace" font-size="14.00" fill="#a6adc8">monitor + mic</text>
</g>
<!-- net -->
<g id="node4" class="node">
<title>net</title>
<polygon fill="#1e2a3e" stroke="#89b4fa" points="1195.3,-103.56 1013.47,-103.56 966.7,0 1148.53,0 1195.3,-103.56"/>
<text xml:space="preserve" text-anchor="middle" x="1081" y="-55.73" font-family="monospace" font-size="14.00" fill="#cdd6f4">TCP :4447</text>
<text xml:space="preserve" text-anchor="middle" x="1081" y="-38.48" font-family="monospace" font-size="14.00" fill="#cdd6f4">mcrndeb</text>
</g>
<!-- wait_server -->
<g id="node5" class="node">
<title>wait_server</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="1180.25,-532.79 995.75,-532.79 995.75,-446.51 1180.25,-446.51 1180.25,-532.79"/>
<text xml:space="preserve" text-anchor="middle" x="1088" y="-510.85" font-family="monospace" font-size="14.00" fill="#cdd6f4">wait_for_server</text>
<text xml:space="preserve" text-anchor="middle" x="1088" y="-493.6" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="1088" y="-476.35" font-family="monospace" font-size="14.00" fill="#cdd6f4">retry connect / 2s</text>
<text xml:space="preserve" text-anchor="middle" x="1088" y="-459.1" font-family="monospace" font-size="14.00" fill="#cdd6f4">Ctrl&#45;C to cancel</text>
</g>
<!-- session_start -->
<g id="node6" class="node">
<title>session_start</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="1188.5,-400.87 987.5,-400.87 987.5,-314.59 1188.5,-314.59 1188.5,-400.87"/>
<text xml:space="preserve" text-anchor="middle" x="1088" y="-378.93" font-family="monospace" font-size="14.00" fill="#cdd6f4">session_start</text>
<text xml:space="preserve" text-anchor="middle" x="1088" y="-361.68" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="1088" y="-344.43" font-family="monospace" font-size="14.00" fill="#cdd6f4">id: YYYYMMDD_HHMMSS</text>
<text xml:space="preserve" text-anchor="middle" x="1088" y="-327.18" font-family="monospace" font-size="14.00" fill="#cdd6f4">video + audio params</text>
</g>
<!-- wait_server&#45;&gt;session_start -->
<g id="edge11" class="edge">
<title>wait_server&#45;&gt;session_start</title>
<path fill="none" stroke="#585b70" d="M1088,-446.11C1088,-435.45 1088,-423.88 1088,-412.75"/>
<polygon fill="#585b70" stroke="#585b70" points="1091.5,-412.88 1088,-402.88 1084.5,-412.88 1091.5,-412.88"/>
</g>
<!-- write -->
<g id="node8" class="node">
<title>write</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="1156.75,-218.22 1005.25,-218.22 1005.25,-166.43 1156.75,-166.43 1156.75,-218.22"/>
<text xml:space="preserve" text-anchor="middle" x="1081" y="-196.27" font-family="monospace" font-size="14.00" fill="#cdd6f4">BufWriter</text>
<text xml:space="preserve" text-anchor="middle" x="1081" y="-179.02" font-family="monospace" font-size="14.00" fill="#cdd6f4">write_packet()</text>
</g>
<!-- session_start&#45;&gt;write -->
<g id="edge12" class="edge">
<title>session_start&#45;&gt;write</title>
<path fill="none" stroke="#585b70" d="M1086.65,-314.12C1085.85,-289 1085,-262.09 1085,-262.09 1085,-262.09 1084.08,-246.3 1083.12,-229.85"/>
<polygon fill="#585b70" stroke="#585b70" points="1086.63,-229.83 1082.55,-220.05 1079.64,-230.24 1086.63,-229.83"/>
</g>
<!-- mux -->
<g id="node7" class="node">
<title>mux</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="969.38,-383.62 710.62,-383.62 710.62,-331.84 969.38,-331.84 969.38,-383.62"/>
<text xml:space="preserve" text-anchor="middle" x="840" y="-361.68" font-family="monospace" font-size="14.00" fill="#cdd6f4">select!</text>
<text xml:space="preserve" text-anchor="middle" x="840" y="-344.43" font-family="monospace" font-size="14.00" fill="#cdd6f4">pkt_rx | keepalive | ctrl&#45;c</text>
</g>
<!-- mux&#45;&gt;write -->
<g id="edge13" class="edge">
<title>mux&#45;&gt;write</title>
<path fill="none" stroke="#585b70" d="M888.44,-331.4C919.87,-314.96 955,-296.59 955,-296.59 955,-296.59 973.75,-262.09 973.75,-262.09 973.75,-262.09 1003.87,-242.78 1032.19,-224.62"/>
<polygon fill="#585b70" stroke="#585b70" points="1033.87,-227.7 1040.4,-219.36 1030.09,-221.81 1033.87,-227.7"/>
<text xml:space="preserve" text-anchor="middle" x="1027.38" y="-283.29" font-family="monospace" font-size="14.00" fill="#a6adc8">WirePacket</text>
<text xml:space="preserve" text-anchor="middle" x="1027.38" y="-266.04" font-family="monospace" font-size="14.00" fill="#a6adc8">Video | Audio</text>
</g>
<!-- shutdown -->
<g id="node9" class="node">
<title>shutdown</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="978.5,-244.09 711.5,-244.09 711.5,-140.56 978.5,-140.56 978.5,-244.09"/>
<text xml:space="preserve" text-anchor="middle" x="845" y="-222.15" font-family="monospace" font-size="14.00" fill="#cdd6f4">Shutdown</text>
<text xml:space="preserve" text-anchor="middle" x="845" y="-204.9" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="845" y="-187.65" font-family="monospace" font-size="14.00" fill="#cdd6f4">pipeline.stop() (5s timeout)</text>
<text xml:space="preserve" text-anchor="middle" x="845" y="-170.4" font-family="monospace" font-size="14.00" fill="#cdd6f4">SessionStop (2s timeout)</text>
<text xml:space="preserve" text-anchor="middle" x="845" y="-153.15" font-family="monospace" font-size="14.00" fill="#cdd6f4">single Ctrl&#45;C</text>
</g>
<!-- mux&#45;&gt;shutdown -->
<g id="edge15" class="edge">
<title>mux&#45;&gt;shutdown</title>
<path fill="none" stroke="#585b70" d="M839.79,-331.57C839.65,-315.09 839.5,-296.59 839.5,-296.59 839.5,-296.59 839.5,-262.09 839.5,-262.09 839.5,-262.09 839.69,-259.69 840,-255.79"/>
<polygon fill="#585b70" stroke="#585b70" points="843.49,-256.14 840.8,-245.89 836.51,-255.58 843.49,-256.14"/>
<text xml:space="preserve" text-anchor="middle" x="897.25" y="-283.29" font-family="monospace" font-size="14.00" fill="#a6adc8">Ctrl&#45;C or</text>
<text xml:space="preserve" text-anchor="middle" x="897.25" y="-266.04" font-family="monospace" font-size="14.00" fill="#a6adc8">channel closed</text>
</g>
<!-- write&#45;&gt;net -->
<g id="edge14" class="edge">
<title>write&#45;&gt;net</title>
<path fill="none" stroke="#585b70" d="M1081,-166.24C1081,-151.9 1081,-133.24 1081,-115.2"/>
<polygon fill="#585b70" stroke="#585b70" points="1084.5,-115.31 1081,-105.31 1077.5,-115.31 1084.5,-115.31"/>
</g>
<!-- demux -->
<g id="node11" class="node">
<title>demux</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="603.38,-747.32 344.62,-747.32 344.62,-626.54 603.38,-626.54 603.38,-747.32"/>
<text xml:space="preserve" text-anchor="middle" x="474" y="-725.38" font-family="monospace" font-size="14.00" fill="#cdd6f4">NUT Demuxer</text>
<text xml:space="preserve" text-anchor="middle" x="474" y="-708.13" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="474" y="-690.88" font-family="monospace" font-size="14.00" fill="#cdd6f4">ffmpeg&#45;next in&#45;process</text>
<text xml:space="preserve" text-anchor="middle" x="474" y="-673.63" font-family="monospace" font-size="14.00" fill="#cdd6f4">finds video + audio streams</text>
<text xml:space="preserve" text-anchor="middle" x="474" y="-656.38" font-family="monospace" font-size="14.00" fill="#cdd6f4">sends EncodedPacket</text>
<text xml:space="preserve" text-anchor="middle" x="474" y="-639.13" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;{ data, pts, media_type }</text>
</g>
<!-- ffmpeg_cli&#45;&gt;demux -->
<g id="edge4" class="edge">
<title>ffmpeg_cli&#45;&gt;demux</title>
<path fill="none" stroke="#585b70" d="M443.24,-817.32C447.65,-798.76 452.56,-778.13 457.13,-758.89"/>
<polygon fill="#585b70" stroke="#585b70" points="460.52,-759.77 459.43,-749.23 453.71,-758.15 460.52,-759.77"/>
<text xml:space="preserve" text-anchor="middle" x="488.53" y="-786.52" font-family="monospace" font-size="14.00" fill="#a6adc8">NUT pipe</text>
<text xml:space="preserve" text-anchor="middle" x="488.53" y="-769.27" font-family="monospace" font-size="14.00" fill="#a6adc8">(stdout)</text>
</g>
<!-- chan -->
<g id="node12" class="node">
<title>chan</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="686.73,-541.43 396.05,-541.43 321.27,-437.87 611.95,-437.87 686.73,-541.43"/>
<text xml:space="preserve" text-anchor="middle" x="504" y="-493.6" font-family="monospace" font-size="14.00" fill="#cdd6f4">mpsc::channel(64)</text>
<text xml:space="preserve" text-anchor="middle" x="504" y="-476.35" font-family="monospace" font-size="14.00" fill="#cdd6f4">EncodedPacket</text>
</g>
<!-- demux&#45;&gt;chan -->
<g id="edge5" class="edge">
<title>demux&#45;&gt;chan</title>
<path fill="none" stroke="#585b70" d="M483.16,-626.28C486.73,-603.05 490.82,-576.42 494.43,-552.94"/>
<polygon fill="#585b70" stroke="#585b70" points="497.87,-553.61 495.93,-543.19 490.95,-552.55 497.87,-553.61"/>
<text xml:space="preserve" text-anchor="middle" x="556.8" y="-595.24" font-family="monospace" font-size="14.00" fill="#a6adc8">EncodedPacket</text>
<text xml:space="preserve" text-anchor="middle" x="556.8" y="-577.99" font-family="monospace" font-size="14.00" fill="#a6adc8">(Video or Audio)</text>
</g>
<!-- chan&#45;&gt;mux -->
<g id="edge10" class="edge">
<title>chan&#45;&gt;mux</title>
<path fill="none" stroke="#585b70" d="M617.29,-444.85C665.69,-426.13 720.89,-404.79 764.23,-388.03"/>
<polygon fill="#585b70" stroke="#585b70" points="765.22,-391.4 773.28,-384.53 762.69,-384.87 765.22,-391.4"/>
</g>
<!-- capture&#45;&gt;encoder -->
<g id="edge8" class="edge">
<title>capture&#45;&gt;encoder</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M186.22,-843.37C188.9,-815.12 192.78,-774.33 195.89,-741.63"/>
<polygon fill="#585b70" stroke="#585b70" points="199.35,-742.23 196.81,-731.94 192.38,-741.56 199.35,-742.23"/>
<text xml:space="preserve" text-anchor="middle" x="230.74" y="-777.89" font-family="monospace" font-size="14.00" fill="#a6adc8">DRM_PRIME</text>
</g>
<!-- encoder&#45;&gt;chan -->
<g id="edge9" class="edge">
<title>encoder&#45;&gt;chan</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M267.12,-643.32C310.45,-615.39 367.62,-578.54 414.94,-548.05"/>
<polygon fill="#585b70" stroke="#585b70" points="416.79,-551.02 423.3,-542.66 413,-545.13 416.79,-551.02"/>
</g>
<!-- types -->
<g id="node15" class="node">
<title>types</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="1107.5,-1143.43 780.5,-1143.43 780.5,-1039.9 1113.5,-1039.9 1113.5,-1137.43 1107.5,-1143.43"/>
<polyline fill="none" stroke="#585b70" points="1107.5,-1143.43 1107.5,-1137.43"/>
<polyline fill="none" stroke="#585b70" points="1113.5,-1137.43 1107.5,-1137.43"/>
<text xml:space="preserve" text-anchor="middle" x="947" y="-1121.49" font-family="monospace" font-size="14.00" fill="#cdd6f4">WirePacket types</text>
<text xml:space="preserve" text-anchor="middle" x="947" y="-1104.24" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="947" y="-1086.99" font-family="monospace" font-size="14.00" fill="#cdd6f4">Video: &#160;&#160;H.264 NALUs + keyframe flag</text>
<text xml:space="preserve" text-anchor="middle" x="947" y="-1069.74" font-family="monospace" font-size="14.00" fill="#cdd6f4">Audio: &#160;&#160;AAC frames</text>
<text xml:space="preserve" text-anchor="middle" x="947" y="-1052.49" font-family="monospace" font-size="14.00" fill="#cdd6f4">Control: SessionStart/Stop/Keepalive</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -1,53 +0,0 @@
// Cargo workspace crate dependency graph
digraph crates {
graph [fontname="monospace" bgcolor="#1e1e2e" pad="0.5"]
node [fontname="monospace" fontcolor="#cdd6f4" style=filled shape=box
fillcolor="#313244" color="#585b70" margin="0.2,0.1"]
edge [color="#585b70" fontname="monospace" fontcolor="#a6adc8"]
// External
ffmpeg_next [label="ffmpeg-next 8\n(client: NUT demux)" shape=component fillcolor="#1e3a2f" color="#a6e3a1"]
tokio [label="tokio 1 (full)\n(async runtime)" shape=component fillcolor="#1e2a3e" color="#89b4fa"]
serde [label="serde / serde_json" shape=component fillcolor="#2a2a3e" color="#cba6f7"]
tracing [label="tracing\ntracing-subscriber" shape=component fillcolor="#2a2a3e" color="#cba6f7"]
anyhow [label="anyhow" shape=component fillcolor="#2a2a3e" color="#cba6f7"]
libc_crate [label="libc 0.2" shape=component fillcolor="#2a2a3e" color="#cba6f7"]
nix_crate [label="nix 0.29\n(signal, process)" shape=component fillcolor="#2a2a3e" color="#cba6f7"]
// Workspace crates
common [label="cht-common\n─────────────\nprotocol.rs (WirePacket framing)\n PacketType: Video|Audio|Control\n ControlMessage: Start|Stop|...\nlogging.rs (tracing init)"
fillcolor="#2d2038" color="#cba6f7"]
client [label="cht-client [sender, Wayland]\n─────────────────────────────\nbackends/subprocess.rs ffmpeg CLI + PulseAudio\n NUT demux → EncodedPacket\nbackends/mod.rs Backend enum\ncapture.rs KmsCapture (direct backend)\nencoder.rs VaapiEncoder + MediaType\npipeline.rs capture→encode thread\nmain.rs wait_for_server, transport,\n YYYYMMDD_HHMMSS session IDs"
fillcolor="#1e2d3e" color="#89b4fa"]
server [label="cht-server [receiver, mcrndeb]\n─────────────────────────────\nmain.rs TCP listener\n routes Video/Audio/Control\nsession.rs ffmpeg subprocess:\n fMP4 + UDP relay\n ADTS audio writer\n Scene relay (Unix socket)\n keyframe buffering"
fillcolor="#1e2d3e" color="#89b4fa"]
// Deps
client -> common
server -> common
common -> serde
common -> tokio
common -> tracing
common -> anyhow
client -> ffmpeg_next
client -> tokio
client -> tracing
client -> anyhow
client -> libc_crate
client -> nix_crate
server -> tokio
server -> tracing
server -> anyhow
// Legend
subgraph cluster_legend {
label="Legend" fontcolor="#a6adc8" color="#585b70" fontname="monospace"
l1 [label="implemented" fillcolor="#1e2d3e" color="#89b4fa" shape=box]
l3 [label="external crate" fillcolor="#1e3a2f" color="#a6e3a1" shape=component]
}
}

View File

@@ -1,222 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 14.1.2 (0)
-->
<!-- Title: crates Pages: 1 -->
<svg width="1507pt" height="498pt"
viewBox="0.00 0.00 1507.00 498.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(36 461.7)">
<title>crates</title>
<polygon fill="#1e1e2e" stroke="none" points="-36,36 -36,-461.7 1470.82,-461.7 1470.82,36 -36,36"/>
<g id="clust1" class="cluster">
<title>cluster_legend</title>
<polygon fill="#1e1e2e" stroke="#585b70" points="1128.82,-306.25 1128.82,-383.5 1426.82,-383.5 1426.82,-306.25 1128.82,-306.25"/>
<text xml:space="preserve" text-anchor="middle" x="1277.82" y="-366.2" font-family="monospace" font-size="14.00" fill="#a6adc8">Legend</text>
</g>
<!-- ffmpeg_next -->
<g id="node1" class="node">
<title>ffmpeg_next</title>
<polygon fill="#1e3a2f" stroke="#a6e3a1" points="518.6,-168.3 333.05,-168.3 333.05,-164.3 329.05,-164.3 329.05,-160.3 333.05,-160.3 333.05,-127.4 329.05,-127.4 329.05,-123.4 333.05,-123.4 333.05,-119.4 518.6,-119.4 518.6,-168.3"/>
<polyline fill="none" stroke="#a6e3a1" points="333.05,-164.3 337.05,-164.3 337.05,-160.3 333.05,-160.3"/>
<polyline fill="none" stroke="#a6e3a1" points="333.05,-127.4 337.05,-127.4 337.05,-123.4 333.05,-123.4"/>
<text xml:space="preserve" text-anchor="middle" x="425.82" y="-147.8" font-family="monospace" font-size="14.00" fill="#cdd6f4">ffmpeg&#45;next 8</text>
<text xml:space="preserve" text-anchor="middle" x="425.82" y="-130.55" font-family="monospace" font-size="14.00" fill="#cdd6f4">(client: NUT demux)</text>
</g>
<!-- tokio -->
<g id="node2" class="node">
<title>tokio</title>
<polygon fill="#1e2a3e" stroke="#89b4fa" points="642.1,-48.9 489.55,-48.9 489.55,-44.9 485.55,-44.9 485.55,-40.9 489.55,-40.9 489.55,-8 485.55,-8 485.55,-4 489.55,-4 489.55,0 642.1,0 642.1,-48.9"/>
<polyline fill="none" stroke="#89b4fa" points="489.55,-44.9 493.55,-44.9 493.55,-40.9 489.55,-40.9"/>
<polyline fill="none" stroke="#89b4fa" points="489.55,-8 493.55,-8 493.55,-4 489.55,-4"/>
<text xml:space="preserve" text-anchor="middle" x="565.82" y="-28.4" font-family="monospace" font-size="14.00" fill="#cdd6f4">tokio 1 &#160;(full)</text>
<text xml:space="preserve" text-anchor="middle" x="565.82" y="-11.15" font-family="monospace" font-size="14.00" fill="#cdd6f4">(async runtime)</text>
</g>
<!-- serde -->
<g id="node3" class="node">
<title>serde</title>
<polygon fill="#2a2a3e" stroke="#cba6f7" points="837.47,-42.45 660.17,-42.45 660.17,-38.45 656.17,-38.45 656.17,-34.45 660.17,-34.45 660.17,-14.45 656.17,-14.45 656.17,-10.45 660.17,-10.45 660.17,-6.45 837.47,-6.45 837.47,-42.45"/>
<polyline fill="none" stroke="#cba6f7" points="660.17,-38.45 664.17,-38.45 664.17,-34.45 660.17,-34.45"/>
<polyline fill="none" stroke="#cba6f7" points="660.17,-14.45 664.17,-14.45 664.17,-10.45 660.17,-10.45"/>
<text xml:space="preserve" text-anchor="middle" x="748.82" y="-19.78" font-family="monospace" font-size="14.00" fill="#cdd6f4">serde / serde_json</text>
</g>
<!-- tracing -->
<g id="node4" class="node">
<title>tracing</title>
<polygon fill="#2a2a3e" stroke="#cba6f7" points="1032.47,-48.9 855.17,-48.9 855.17,-44.9 851.17,-44.9 851.17,-40.9 855.17,-40.9 855.17,-8 851.17,-8 851.17,-4 855.17,-4 855.17,0 1032.47,0 1032.47,-48.9"/>
<polyline fill="none" stroke="#cba6f7" points="855.17,-44.9 859.17,-44.9 859.17,-40.9 855.17,-40.9"/>
<polyline fill="none" stroke="#cba6f7" points="855.17,-8 859.17,-8 859.17,-4 855.17,-4"/>
<text xml:space="preserve" text-anchor="middle" x="943.82" y="-28.4" font-family="monospace" font-size="14.00" fill="#cdd6f4">tracing</text>
<text xml:space="preserve" text-anchor="middle" x="943.82" y="-11.15" font-family="monospace" font-size="14.00" fill="#cdd6f4">tracing&#45;subscriber</text>
</g>
<!-- anyhow -->
<g id="node5" class="node">
<title>anyhow</title>
<polygon fill="#2a2a3e" stroke="#cba6f7" points="1128.97,-42.45 1050.67,-42.45 1050.67,-38.45 1046.67,-38.45 1046.67,-34.45 1050.67,-34.45 1050.67,-14.45 1046.67,-14.45 1046.67,-10.45 1050.67,-10.45 1050.67,-6.45 1128.97,-6.45 1128.97,-42.45"/>
<polyline fill="none" stroke="#cba6f7" points="1050.67,-38.45 1054.67,-38.45 1054.67,-34.45 1050.67,-34.45"/>
<polyline fill="none" stroke="#cba6f7" points="1050.67,-14.45 1054.67,-14.45 1054.67,-10.45 1050.67,-10.45"/>
<text xml:space="preserve" text-anchor="middle" x="1089.82" y="-19.78" font-family="monospace" font-size="14.00" fill="#cdd6f4">anyhow</text>
</g>
<!-- libc_crate -->
<g id="node6" class="node">
<title>libc_crate</title>
<polygon fill="#2a2a3e" stroke="#cba6f7" points="128.22,-161.85 33.42,-161.85 33.42,-157.85 29.42,-157.85 29.42,-153.85 33.42,-153.85 33.42,-133.85 29.42,-133.85 29.42,-129.85 33.42,-129.85 33.42,-125.85 128.22,-125.85 128.22,-161.85"/>
<polyline fill="none" stroke="#cba6f7" points="33.42,-157.85 37.42,-157.85 37.42,-153.85 33.42,-153.85"/>
<polyline fill="none" stroke="#cba6f7" points="33.42,-133.85 37.42,-133.85 37.42,-129.85 33.42,-129.85"/>
<text xml:space="preserve" text-anchor="middle" x="80.82" y="-139.18" font-family="monospace" font-size="14.00" fill="#cdd6f4">libc 0.2</text>
</g>
<!-- nix_crate -->
<g id="node7" class="node">
<title>nix_crate</title>
<polygon fill="#2a2a3e" stroke="#cba6f7" points="315.35,-168.3 146.3,-168.3 146.3,-164.3 142.3,-164.3 142.3,-160.3 146.3,-160.3 146.3,-127.4 142.3,-127.4 142.3,-123.4 146.3,-123.4 146.3,-119.4 315.35,-119.4 315.35,-168.3"/>
<polyline fill="none" stroke="#cba6f7" points="146.3,-164.3 150.3,-164.3 150.3,-160.3 146.3,-160.3"/>
<polyline fill="none" stroke="#cba6f7" points="146.3,-127.4 150.3,-127.4 150.3,-123.4 146.3,-123.4"/>
<text xml:space="preserve" text-anchor="middle" x="230.82" y="-147.8" font-family="monospace" font-size="14.00" fill="#cdd6f4">nix 0.29</text>
<text xml:space="preserve" text-anchor="middle" x="230.82" y="-130.55" font-family="monospace" font-size="14.00" fill="#cdd6f4">(signal, process)</text>
</g>
<!-- common -->
<g id="node8" class="node">
<title>common</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="1004.72,-202.8 612.92,-202.8 612.92,-84.9 1004.72,-84.9 1004.72,-202.8"/>
<text xml:space="preserve" text-anchor="middle" x="808.82" y="-182.3" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht&#45;common</text>
<text xml:space="preserve" text-anchor="middle" x="808.82" y="-165.05" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="808.82" y="-147.8" font-family="monospace" font-size="14.00" fill="#cdd6f4">protocol.rs &#160;(WirePacket framing)</text>
<text xml:space="preserve" text-anchor="middle" x="808.82" y="-130.55" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;PacketType: Video|Audio|Control</text>
<text xml:space="preserve" text-anchor="middle" x="808.82" y="-113.3" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;ControlMessage: Start|Stop|...</text>
<text xml:space="preserve" text-anchor="middle" x="808.82" y="-96.05" font-family="monospace" font-size="14.00" fill="#cdd6f4">logging.rs &#160;&#160;(tracing init)</text>
</g>
<!-- common&#45;&gt;tokio -->
<g id="edge4" class="edge">
<title>common&#45;&gt;tokio</title>
<path fill="none" stroke="#585b70" d="M688.04,-84.49C666.33,-74.01 644.59,-63.5 625.59,-54.33"/>
<polygon fill="#585b70" stroke="#585b70" points="627.23,-51.23 616.7,-50.03 624.18,-57.53 627.23,-51.23"/>
</g>
<!-- common&#45;&gt;serde -->
<g id="edge3" class="edge">
<title>common&#45;&gt;serde</title>
<path fill="none" stroke="#585b70" d="M779,-84.49C773.36,-73.46 767.71,-62.4 762.85,-52.89"/>
<polygon fill="#585b70" stroke="#585b70" points="766.1,-51.55 758.43,-44.24 759.86,-54.74 766.1,-51.55"/>
</g>
<!-- common&#45;&gt;tracing -->
<g id="edge5" class="edge">
<title>common&#45;&gt;tracing</title>
<path fill="none" stroke="#585b70" d="M875.93,-84.49C886.87,-74.98 897.83,-65.45 907.65,-56.91"/>
<polygon fill="#585b70" stroke="#585b70" points="909.93,-59.57 915.17,-50.36 905.33,-54.29 909.93,-59.57"/>
</g>
<!-- common&#45;&gt;anyhow -->
<g id="edge6" class="edge">
<title>common&#45;&gt;anyhow</title>
<path fill="none" stroke="#585b70" d="M958.02,-84.53C986.02,-73.07 1014.93,-60.87 1041.82,-48.9 1042.75,-48.49 1043.69,-48.07 1044.63,-47.64"/>
<polygon fill="#585b70" stroke="#585b70" points="1045.86,-50.93 1053.46,-43.54 1042.92,-44.58 1045.86,-50.93"/>
</g>
<!-- client -->
<g id="node9" class="node">
<title>client</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="650.6,-425.7 201.05,-425.7 201.05,-238.8 650.6,-238.8 650.6,-425.7"/>
<text xml:space="preserve" text-anchor="middle" x="425.82" y="-405.2" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht&#45;client &#160;[sender, Wayland]</text>
<text xml:space="preserve" text-anchor="middle" x="425.82" y="-387.95" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────────────────────</text>
<text xml:space="preserve" text-anchor="middle" x="425.82" y="-370.7" font-family="monospace" font-size="14.00" fill="#cdd6f4">backends/subprocess.rs &#160;ffmpeg CLI + PulseAudio</text>
<text xml:space="preserve" text-anchor="middle" x="425.82" y="-353.45" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;NUT demux → EncodedPacket</text>
<text xml:space="preserve" text-anchor="middle" x="425.82" y="-336.2" font-family="monospace" font-size="14.00" fill="#cdd6f4">backends/mod.rs &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Backend enum</text>
<text xml:space="preserve" text-anchor="middle" x="425.82" y="-318.95" font-family="monospace" font-size="14.00" fill="#cdd6f4">capture.rs &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;KmsCapture (direct backend)</text>
<text xml:space="preserve" text-anchor="middle" x="425.82" y="-301.7" font-family="monospace" font-size="14.00" fill="#cdd6f4">encoder.rs &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;VaapiEncoder + MediaType</text>
<text xml:space="preserve" text-anchor="middle" x="425.82" y="-284.45" font-family="monospace" font-size="14.00" fill="#cdd6f4">pipeline.rs &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;capture→encode thread</text>
<text xml:space="preserve" text-anchor="middle" x="425.82" y="-267.2" font-family="monospace" font-size="14.00" fill="#cdd6f4">main.rs &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;wait_for_server, transport,</text>
<text xml:space="preserve" text-anchor="middle" x="425.82" y="-249.95" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;YYYYMMDD_HHMMSS session IDs</text>
</g>
<!-- client&#45;&gt;ffmpeg_next -->
<g id="edge7" class="edge">
<title>client&#45;&gt;ffmpeg_next</title>
<path fill="none" stroke="#585b70" d="M425.82,-238.32C425.82,-217.48 425.82,-196.62 425.82,-179.92"/>
<polygon fill="#585b70" stroke="#585b70" points="429.32,-180.06 425.82,-170.06 422.32,-180.06 429.32,-180.06"/>
</g>
<!-- client&#45;&gt;tokio -->
<g id="edge8" class="edge">
<title>client&#45;&gt;tokio</title>
<path fill="none" stroke="#585b70" d="M200.68,-283.69C122.23,-262.21 47.96,-234.5 24.82,-202.8 -6.06,-160.47 -10.15,-123.92 24.82,-84.9 54.48,-51.81 332.63,-35.25 478.09,-28.79"/>
<polygon fill="#585b70" stroke="#585b70" points="477.84,-32.31 487.67,-28.38 477.53,-25.32 477.84,-32.31"/>
</g>
<!-- client&#45;&gt;tracing -->
<g id="edge9" class="edge">
<title>client&#45;&gt;tracing</title>
<path fill="none" stroke="#585b70" d="M501.29,-238.42C510.41,-226.56 519.45,-214.49 527.82,-202.8 564.14,-152.13 551.43,-118.68 603.82,-84.9 692.54,-27.7 736.68,-66.94 843.59,-49.32"/>
<polygon fill="#585b70" stroke="#585b70" points="844.12,-52.78 853.34,-47.56 842.88,-45.89 844.12,-52.78"/>
</g>
<!-- client&#45;&gt;anyhow -->
<g id="edge10" class="edge">
<title>client&#45;&gt;anyhow</title>
<path fill="none" stroke="#585b70" d="M651.04,-262.97C684.64,-254.07 719,-245.67 751.82,-238.8 809.35,-226.75 967.08,-238.42 1013.82,-202.8 1061.51,-166.46 1079.67,-94.24 1086.28,-54.08"/>
<polygon fill="#585b70" stroke="#585b70" points="1089.73,-54.62 1087.75,-44.22 1082.81,-53.59 1089.73,-54.62"/>
</g>
<!-- client&#45;&gt;libc_crate -->
<g id="edge11" class="edge">
<title>client&#45;&gt;libc_crate</title>
<path fill="none" stroke="#585b70" d="M200.61,-241.57C178.39,-229.68 156.73,-216.73 136.82,-202.8 123.98,-193.81 111.65,-181.59 101.85,-170.69"/>
<polygon fill="#585b70" stroke="#585b70" points="104.76,-168.71 95.56,-163.46 99.48,-173.3 104.76,-168.71"/>
</g>
<!-- client&#45;&gt;nix_crate -->
<g id="edge12" class="edge">
<title>client&#45;&gt;nix_crate</title>
<path fill="none" stroke="#585b70" d="M328.61,-238.32C305.11,-215.86 281.6,-193.38 263.52,-176.1"/>
<polygon fill="#585b70" stroke="#585b70" points="266.35,-173.97 256.7,-169.59 261.51,-179.03 266.35,-173.97"/>
</g>
<!-- client&#45;&gt;common -->
<g id="edge1" class="edge">
<title>client&#45;&gt;common</title>
<path fill="none" stroke="#585b70" d="M616.76,-238.32C637.67,-228.15 658.58,-217.97 678.5,-208.28"/>
<polygon fill="#585b70" stroke="#585b70" points="680.01,-211.44 687.47,-203.91 676.94,-205.14 680.01,-211.44"/>
</g>
<!-- server -->
<g id="node10" class="node">
<title>server</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="1119.22,-417.07 760.42,-417.07 760.42,-247.43 1119.22,-247.43 1119.22,-417.07"/>
<text xml:space="preserve" text-anchor="middle" x="939.82" y="-396.57" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht&#45;server &#160;[receiver, mcrndeb]</text>
<text xml:space="preserve" text-anchor="middle" x="939.82" y="-379.32" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────────────────────</text>
<text xml:space="preserve" text-anchor="middle" x="939.82" y="-362.07" font-family="monospace" font-size="14.00" fill="#cdd6f4">main.rs &#160;&#160;&#160;&#160;&#160;&#160;TCP listener</text>
<text xml:space="preserve" text-anchor="middle" x="939.82" y="-344.82" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;routes Video/Audio/Control</text>
<text xml:space="preserve" text-anchor="middle" x="939.82" y="-327.57" font-family="monospace" font-size="14.00" fill="#cdd6f4">session.rs &#160;&#160;&#160;ffmpeg subprocess:</text>
<text xml:space="preserve" text-anchor="middle" x="939.82" y="-310.32" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;fMP4 + UDP relay</text>
<text xml:space="preserve" text-anchor="middle" x="939.82" y="-293.07" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;ADTS audio writer</text>
<text xml:space="preserve" text-anchor="middle" x="939.82" y="-275.82" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Scene relay (Unix socket)</text>
<text xml:space="preserve" text-anchor="middle" x="939.82" y="-258.57" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;keyframe buffering</text>
</g>
<!-- server&#45;&gt;tokio -->
<g id="edge13" class="edge">
<title>server&#45;&gt;tokio</title>
<path fill="none" stroke="#585b70" d="M760.26,-270.87C686.61,-244.58 615.68,-216.64 603.82,-202.8 569.66,-162.92 563.88,-99.75 564,-60.55"/>
<polygon fill="#585b70" stroke="#585b70" points="567.5,-60.78 564.18,-50.72 560.5,-60.65 567.5,-60.78"/>
</g>
<!-- server&#45;&gt;tracing -->
<g id="edge14" class="edge">
<title>server&#45;&gt;tracing</title>
<path fill="none" stroke="#585b70" d="M996.34,-247.25C1003.55,-232.95 1009.8,-217.84 1013.82,-202.8 1027.36,-152.18 1035.83,-132.46 1013.82,-84.9 1008.85,-74.15 1000.87,-64.64 992.05,-56.55"/>
<polygon fill="#585b70" stroke="#585b70" points="994.46,-53.99 984.56,-50.21 989.93,-59.34 994.46,-53.99"/>
</g>
<!-- server&#45;&gt;anyhow -->
<g id="edge15" class="edge">
<title>server&#45;&gt;anyhow</title>
<path fill="none" stroke="#585b70" d="M1054.86,-247.12C1066.85,-233.63 1077.35,-218.8 1084.82,-202.8 1107.37,-154.53 1101.74,-90.54 1095.71,-54.1"/>
<polygon fill="#585b70" stroke="#585b70" points="1099.17,-53.57 1093.95,-44.35 1092.28,-54.81 1099.17,-53.57"/>
</g>
<!-- server&#45;&gt;common -->
<g id="edge2" class="edge">
<title>server&#45;&gt;common</title>
<path fill="none" stroke="#585b70" d="M880.65,-247.05C872.49,-235.44 864.2,-223.64 856.29,-212.39"/>
<polygon fill="#585b70" stroke="#585b70" points="859.23,-210.49 850.62,-204.32 853.5,-214.51 859.23,-210.49"/>
</g>
<!-- l1 -->
<g id="node11" class="node">
<title>l1</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="1256.6,-350.25 1137.05,-350.25 1137.05,-314.25 1256.6,-314.25 1256.6,-350.25"/>
<text xml:space="preserve" text-anchor="middle" x="1196.82" y="-327.57" font-family="monospace" font-size="14.00" fill="#cdd6f4">implemented</text>
</g>
<!-- l3 -->
<g id="node12" class="node">
<title>l3</title>
<polygon fill="#1e3a2f" stroke="#a6e3a1" points="1418.97,-350.25 1274.67,-350.25 1274.67,-346.25 1270.67,-346.25 1270.67,-342.25 1274.67,-342.25 1274.67,-322.25 1270.67,-322.25 1270.67,-318.25 1274.67,-318.25 1274.67,-314.25 1418.97,-314.25 1418.97,-350.25"/>
<polyline fill="none" stroke="#a6e3a1" points="1274.67,-346.25 1278.67,-346.25 1278.67,-342.25 1274.67,-342.25"/>
<polyline fill="none" stroke="#a6e3a1" points="1274.67,-322.25 1278.67,-322.25 1278.67,-318.25 1274.67,-318.25"/>
<text xml:space="preserve" text-anchor="middle" x="1346.82" y="-327.57" font-family="monospace" font-size="14.00" fill="#cdd6f4">external crate</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,193 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Media Transport — Architecture</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
display: flex;
height: 100vh;
font-family: monospace;
background: #1e1e2e;
color: #cdd6f4;
}
nav {
width: 220px;
min-width: 220px;
background: #181825;
border-right: 1px solid #313244;
display: flex;
flex-direction: column;
padding: 1rem 0;
}
nav h1 {
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.1em;
color: #6c7086;
padding: 0 1rem 0.75rem;
border-bottom: 1px solid #313244;
margin-bottom: 0.5rem;
}
nav a {
display: block;
padding: 0.5rem 1rem;
color: #cdd6f4;
text-decoration: none;
font-size: 0.85rem;
border-left: 3px solid transparent;
transition: background 0.1s, border-color 0.1s;
}
nav a:hover { background: #313244; }
nav a.active { border-left-color: #89b4fa; color: #89b4fa; background: #1e2d3e; }
nav .subtitle {
font-size: 0.7rem;
color: #6c7086;
padding: 0 1rem;
margin-top: 0.25rem;
}
nav .phase-badge {
font-size: 0.65rem;
color: #a6e3a1;
float: right;
}
nav .section {
font-size: 0.65rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: #6c7086;
padding: 1rem 1rem 0.25rem;
}
main {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
header {
padding: 0.75rem 1.25rem;
background: #181825;
border-bottom: 1px solid #313244;
display: flex;
align-items: baseline;
gap: 0.75rem;
}
header h2 { font-size: 0.95rem; }
header .desc { font-size: 0.75rem; color: #6c7086; }
.viewer {
flex: 1;
overflow: auto;
padding: 1.5rem;
display: flex;
align-items: flex-start;
justify-content: center;
background: #1e1e2e;
}
.viewer object,
.viewer img {
max-width: 100%;
border-radius: 6px;
box-shadow: 0 4px 24px rgba(0,0,0,0.5);
}
.placeholder {
color: #6c7086;
font-size: 0.85rem;
margin-top: 4rem;
}
</style>
</head>
<body>
<nav>
<h1>Media Transport</h1>
<div class="section">Workspace</div>
<a href="#" data-svg="crates.svg" data-title="Crate Dependency Graph" data-desc="Workspace members and external deps">
Crate graph
</a>
<div class="section">Client (sender)</div>
<a href="#" data-svg="client-pipeline.svg" data-title="Client Pipeline" data-desc="KMS capture + PulseAudio → VAAPI H.264 + AAC → TCP transport">
Pipeline
</a>
<div class="section">Server (receiver)</div>
<a href="#" data-svg="server-pipeline.svg" data-title="Server Pipeline" data-desc="fMP4 recording, UDP live relay, scene detection (UDS → Python), audio extraction">
Pipeline
</a>
<div class="section">Status</div>
<a href="#" data-svg="" data-title="Current State (2026-04-10)" data-desc="Architecture status and known regressions"
onclick="event.preventDefault(); document.querySelectorAll('nav a').forEach(l=>l.classList.remove('active')); this.classList.add('active'); document.getElementById('title').textContent=this.dataset.title; document.getElementById('desc').textContent=this.dataset.desc; document.getElementById('viewer').innerHTML=document.getElementById('status-content').innerHTML; return false;">
State &amp; regressions
</a>
</nav>
<template id="status-content">
<div style="max-width:720px; font-size:0.85rem; line-height:1.6; color:#cdd6f4">
<h3 style="color:#f38ba8; margin-bottom:0.5rem">Scene detection regressed</h3>
<p>In the Python-only pipeline, scene detection was a branch of the <b>same ffmpeg process</b> that records (fMP4 + UDP relay + CUDA decode + select filter). The flush trick worked because all outputs shared one decoder.</p>
<p style="margin-top:0.5rem">After Rust took over transport, scene detection became a <b>separate ffmpeg</b> fed via <code>scene.sock</code> Unix socket relay. Different buffering semantics broke the "one behind" flush fix, and <code>try_send</code> drops cause decoder corruption until the next keyframe.</p>
<h3 style="color:#a6e3a1; margin:1rem 0 0.5rem">Working fallback</h3>
<p>The Python-only path (<code>StreamRecorder</code> + <code>SessionProcessor</code>) still exists. <code>lifecycle.start(rust_transport=False)</code> bypasses Rust transport entirely. Plan: restore this as the default, keep Rust opt-in.</p>
<h3 style="color:#89b4fa; margin:1rem 0 0.5rem">What Rust transport got right</h3>
<ul style="padding-left:1.2rem">
<li>Connect time: 20s → 3s</li>
<li>Session reload: 1-2s</li>
<li>Custom framed protocol with reconnection support</li>
<li>Clean fMP4 recording + UDP live relay</li>
</ul>
<h3 style="color:#cba6f7; margin:1rem 0 0.5rem">Next: scene detection back into server ffmpeg</h3>
<p>Add scene detection as a third output of the Rust server's ffmpeg command (decode + select filter + MJPEG pipe) instead of relaying raw H.264 to a separate process. See <code>def/10-scene-detect-to-rust.md</code>.</p>
</div>
</template>
<main>
<header>
<h2 id="title">Select a diagram</h2>
<span class="desc" id="desc"></span>
</header>
<div class="viewer" id="viewer">
<p class="placeholder">← pick a diagram from the sidebar</p>
</div>
</main>
<script>
const viewer = document.getElementById('viewer');
const titleEl = document.getElementById('title');
const descEl = document.getElementById('desc');
document.querySelectorAll('nav a').forEach(link => {
link.addEventListener('click', e => {
e.preventDefault();
document.querySelectorAll('nav a').forEach(l => l.classList.remove('active'));
link.classList.add('active');
titleEl.textContent = link.dataset.title;
descEl.textContent = link.dataset.desc;
// Use <object> so SVG internal text/links work
viewer.innerHTML = `<object type="image/svg+xml" data="${link.dataset.svg}"></object>`;
});
});
// Auto-select first
document.querySelector('nav a').click();
</script>
</body>
</html>

View File

@@ -1,76 +0,0 @@
// 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]
// 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"]
}

View File

@@ -1,286 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 14.1.2 (0)
-->
<!-- Title: server_pipeline Pages: 1 -->
<svg width="1677pt" height="1243pt"
viewBox="0.00 0.00 1677.00 1243.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(43.2 1200.16)">
<title>server_pipeline</title>
<polygon fill="#1e1e2e" stroke="none" points="-43.2,43.2 -43.2,-1200.16 1633.33,-1200.16 1633.33,43.2 -43.2,43.2"/>
<g id="clust1" class="cluster">
<title>cluster_rust</title>
<polygon fill="#1e1e2e" stroke="#a6e3a1" points="284.12,-622.34 284.12,-1010.15 1314.12,-1010.15 1314.12,-622.34 284.12,-622.34"/>
<text xml:space="preserve" text-anchor="middle" x="799.12" y="-992.85" font-family="monospace" font-size="14.00" fill="#a6e3a1">cht&#45;server (Rust)</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_python</title>
<polygon fill="#1e1e2e" stroke="#cba6f7" points="754.12,-114.28 754.12,-310.81 1582.12,-310.81 1582.12,-114.28 754.12,-114.28"/>
<text xml:space="preserve" text-anchor="middle" x="1168.12" y="-293.51" font-family="monospace" font-size="14.00" fill="#cba6f7">Python (cht app)</text>
</g>
<!-- net -->
<g id="node1" class="node">
<title>net</title>
<polygon fill="#1e2a3e" stroke="#89b4fa" points="915.09,-1156.96 692.44,-1156.96 635.16,-1053.4 857.81,-1053.4 915.09,-1156.96"/>
<text xml:space="preserve" text-anchor="middle" x="775.12" y="-1109.13" font-family="monospace" font-size="14.00" fill="#cdd6f4">TCP :4447</text>
<text xml:space="preserve" text-anchor="middle" x="775.12" y="-1091.88" font-family="monospace" font-size="14.00" fill="#cdd6f4">(WirePacket)</text>
</g>
<!-- listener -->
<g id="node3" class="node">
<title>listener</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="916.88,-976.9 633.38,-976.9 633.38,-821.62 916.88,-821.62 916.88,-976.9"/>
<text xml:space="preserve" text-anchor="middle" x="775.12" y="-954.96" font-family="monospace" font-size="14.00" fill="#cdd6f4">Listener</text>
<text xml:space="preserve" text-anchor="middle" x="775.12" y="-937.71" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="775.12" y="-920.46" font-family="monospace" font-size="14.00" fill="#cdd6f4">TCP accept</text>
<text xml:space="preserve" text-anchor="middle" x="775.12" y="-903.21" font-family="monospace" font-size="14.00" fill="#cdd6f4">reads WirePacket</text>
<text xml:space="preserve" text-anchor="middle" x="775.12" y="-885.96" font-family="monospace" font-size="14.00" fill="#cdd6f4">routes by type:</text>
<text xml:space="preserve" text-anchor="middle" x="775.12" y="-868.71" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;Video → ffmpeg + scene relay</text>
<text xml:space="preserve" text-anchor="middle" x="775.12" y="-851.46" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;Audio → ADTS file</text>
<text xml:space="preserve" text-anchor="middle" x="775.12" y="-834.21" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;Control → session lifecycle</text>
</g>
<!-- net&#45;&gt;listener -->
<g id="edge1" class="edge">
<title>net&#45;&gt;listener</title>
<path fill="none" stroke="#585b70" d="M775.12,-1052.99C775.12,-1033.5 775.12,-1010.68 775.12,-988.72"/>
<polygon fill="#585b70" stroke="#585b70" points="778.63,-988.85 775.13,-978.85 771.63,-988.85 778.63,-988.85"/>
<text xml:space="preserve" text-anchor="middle" x="816.38" y="-1022.1" font-family="monospace" font-size="14.00" fill="#a6adc8">WirePacket</text>
</g>
<!-- python -->
<g id="node2" class="node">
<title>python</title>
<polygon fill="#2a2a3e" stroke="#cba6f7" points="1361.98,-508.73 1166.55,-508.73 1116.27,-405.17 1311.7,-405.17 1361.98,-508.73"/>
<text xml:space="preserve" text-anchor="middle" x="1239.12" y="-460.9" font-family="monospace" font-size="14.00" fill="#cdd6f4">Python GUI</text>
<text xml:space="preserve" text-anchor="middle" x="1239.12" y="-443.65" font-family="monospace" font-size="14.00" fill="#cdd6f4">(cht app)</text>
</g>
<!-- ffmpeg_rec -->
<g id="node4" class="node">
<title>ffmpeg_rec</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="534.25,-742.49 292,-742.49 292,-638.96 534.25,-638.96 534.25,-742.49"/>
<text xml:space="preserve" text-anchor="middle" x="413.12" y="-720.55" font-family="monospace" font-size="14.00" fill="#cdd6f4">ffmpeg subprocess</text>
<text xml:space="preserve" text-anchor="middle" x="413.12" y="-703.3" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="413.12" y="-686.05" font-family="monospace" font-size="14.00" fill="#cdd6f4">H.264 pipe:0 → 2 outputs:</text>
<text xml:space="preserve" text-anchor="middle" x="413.12" y="-668.8" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;1. fMP4 (frag_keyframe)</text>
<text xml:space="preserve" text-anchor="middle" x="413.12" y="-651.55" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;2. UDP :4445 (mpegts)</text>
</g>
<!-- listener&#45;&gt;ffmpeg_rec -->
<g id="edge2" class="edge">
<title>listener&#45;&gt;ffmpeg_rec</title>
<path fill="none" stroke="#585b70" d="M640.07,-821.21C597.84,-797.12 552.04,-770.98 512.71,-748.55"/>
<polygon fill="#585b70" stroke="#585b70" points="514.59,-745.59 504.17,-743.67 511.12,-751.67 514.59,-745.59"/>
<text xml:space="preserve" text-anchor="middle" x="650.35" y="-781.69" font-family="monospace" font-size="14.00" fill="#a6adc8">H.264 video</text>
</g>
<!-- scene_relay -->
<g id="node5" class="node">
<title>scene_relay</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="802.38,-751.12 551.88,-751.12 551.88,-630.34 802.38,-630.34 802.38,-751.12"/>
<text xml:space="preserve" text-anchor="middle" x="677.12" y="-729.18" font-family="monospace" font-size="14.00" fill="#cdd6f4">Scene Relay</text>
<text xml:space="preserve" text-anchor="middle" x="677.12" y="-711.93" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="677.12" y="-694.68" font-family="monospace" font-size="14.00" fill="#cdd6f4">Unix socket (scene.sock)</text>
<text xml:space="preserve" text-anchor="middle" x="677.12" y="-677.43" font-family="monospace" font-size="14.00" fill="#cdd6f4">buffers latest keyframe</text>
<text xml:space="preserve" text-anchor="middle" x="677.12" y="-660.18" font-family="monospace" font-size="14.00" fill="#cdd6f4">best&#45;effort: drops if slow</text>
<text xml:space="preserve" text-anchor="middle" x="677.12" y="-642.93" font-family="monospace" font-size="14.00" fill="#cdd6f4">100ms write timeout</text>
</g>
<!-- listener&#45;&gt;scene_relay -->
<g id="edge3" class="edge">
<title>listener&#45;&gt;scene_relay</title>
<path fill="none" stroke="#585b70" d="M738.63,-821.36C729.36,-801.82 719.45,-780.93 710.35,-761.75"/>
<polygon fill="#585b70" stroke="#585b70" points="713.63,-760.49 706.18,-752.96 707.3,-763.5 713.63,-760.49"/>
<text xml:space="preserve" text-anchor="middle" x="790.94" y="-790.32" font-family="monospace" font-size="14.00" fill="#a6adc8">H.264 copy</text>
<text xml:space="preserve" text-anchor="middle" x="790.94" y="-773.07" font-family="monospace" font-size="14.00" fill="#a6adc8">+ keyframe flag</text>
</g>
<!-- audio_writer -->
<g id="node6" class="node">
<title>audio_writer</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="1029.75,-733.87 820.5,-733.87 820.5,-647.59 1029.75,-647.59 1029.75,-733.87"/>
<text xml:space="preserve" text-anchor="middle" x="925.12" y="-711.93" font-family="monospace" font-size="14.00" fill="#cdd6f4">Audio Writer</text>
<text xml:space="preserve" text-anchor="middle" x="925.12" y="-694.68" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="925.12" y="-677.43" font-family="monospace" font-size="14.00" fill="#cdd6f4">ADTS header + raw AAC</text>
<text xml:space="preserve" text-anchor="middle" x="925.12" y="-660.18" font-family="monospace" font-size="14.00" fill="#cdd6f4">→ stream/audio.aac</text>
</g>
<!-- listener&#45;&gt;audio_writer -->
<g id="edge4" class="edge">
<title>listener&#45;&gt;audio_writer</title>
<path fill="none" stroke="#585b70" d="M841.7,-821.42C850.82,-810.9 857.12,-803.62 857.12,-803.62 857.12,-803.62 875.59,-773.23 893.28,-744.13"/>
<polygon fill="#585b70" stroke="#585b70" points="896.23,-746.01 898.44,-735.64 890.25,-742.37 896.23,-746.01"/>
<text xml:space="preserve" text-anchor="middle" x="914.34" y="-781.69" font-family="monospace" font-size="14.00" fill="#a6adc8">AAC audio</text>
</g>
<!-- active_session -->
<g id="node7" class="node">
<title>active_session</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="1300.5,-742.49 1047.75,-742.49 1047.75,-638.96 1306.5,-638.96 1306.5,-736.49 1300.5,-742.49"/>
<polyline fill="none" stroke="#585b70" points="1300.5,-742.49 1300.5,-736.49"/>
<polyline fill="none" stroke="#585b70" points="1306.5,-736.49 1300.5,-736.49"/>
<text xml:space="preserve" text-anchor="middle" x="1177.12" y="-720.55" font-family="monospace" font-size="14.00" fill="#cdd6f4">active&#45;session</text>
<text xml:space="preserve" text-anchor="middle" x="1177.12" y="-703.3" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="1177.12" y="-686.05" font-family="monospace" font-size="14.00" fill="#cdd6f4">file at data/active&#45;session</text>
<text xml:space="preserve" text-anchor="middle" x="1177.12" y="-668.8" font-family="monospace" font-size="14.00" fill="#cdd6f4">Python polls to discover</text>
<text xml:space="preserve" text-anchor="middle" x="1177.12" y="-651.55" font-family="monospace" font-size="14.00" fill="#cdd6f4">session dir</text>
</g>
<!-- listener&#45;&gt;active_session -->
<g id="edge5" class="edge">
<title>listener&#45;&gt;active_session</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M917.14,-825.3C966.5,-799.94 1021.08,-771.9 1067.43,-748.09"/>
<polygon fill="#585b70" stroke="#585b70" points="1068.85,-751.29 1076.14,-743.61 1065.65,-745.07 1068.85,-751.29"/>
<text xml:space="preserve" text-anchor="middle" x="1085.83" y="-781.69" font-family="monospace" font-size="14.00" fill="#a6adc8">on SessionStart</text>
</g>
<!-- fmp4 -->
<g id="node11" class="node">
<title>fmp4</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="176.25,-491.46 173.25,-495.46 152.25,-495.46 149.25,-491.46 0,-491.46 0,-422.44 176.25,-422.44 176.25,-491.46"/>
<text xml:space="preserve" text-anchor="middle" x="88.12" y="-469.52" font-family="monospace" font-size="14.00" fill="#cdd6f4">stream/</text>
<text xml:space="preserve" text-anchor="middle" x="88.12" y="-452.27" font-family="monospace" font-size="14.00" fill="#cdd6f4">recording_000.mp4</text>
<text xml:space="preserve" text-anchor="middle" x="88.12" y="-435.02" font-family="monospace" font-size="14.00" fill="#cdd6f4">(fragmented MP4)</text>
</g>
<!-- ffmpeg_rec&#45;&gt;fmp4 -->
<g id="edge7" class="edge">
<title>ffmpeg_rec&#45;&gt;fmp4</title>
<path fill="none" stroke="#585b70" d="M323.34,-638.58C259.54,-602.23 185.12,-559.84 185.12,-559.84 185.12,-559.84 154.9,-528.09 128.2,-500.04"/>
<polygon fill="#585b70" stroke="#585b70" points="130.9,-497.8 121.47,-492.97 125.83,-502.63 130.9,-497.8"/>
<text xml:space="preserve" text-anchor="middle" x="288.5" y="-590.41" font-family="monospace" font-size="14.00" fill="#a6adc8">copy</text>
</g>
<!-- udp_live -->
<g id="node12" class="node">
<title>udp_live</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="508.19,-508.73 258.33,-508.73 194.06,-405.17 443.92,-405.17 508.19,-508.73"/>
<text xml:space="preserve" text-anchor="middle" x="351.12" y="-460.9" font-family="monospace" font-size="14.00" fill="#cdd6f4">UDP :4445</text>
<text xml:space="preserve" text-anchor="middle" x="351.12" y="-443.65" font-family="monospace" font-size="14.00" fill="#cdd6f4">(mpegts → mpv)</text>
</g>
<!-- ffmpeg_rec&#45;&gt;udp_live -->
<g id="edge8" class="edge">
<title>ffmpeg_rec&#45;&gt;udp_live</title>
<path fill="none" stroke="#585b70" d="M399.44,-638.58C390.06,-603.51 377.57,-556.8 367.63,-519.67"/>
<polygon fill="#585b70" stroke="#585b70" points="371.13,-519.19 365.16,-510.43 364.37,-521 371.13,-519.19"/>
<text xml:space="preserve" text-anchor="middle" x="407.8" y="-590.41" font-family="monospace" font-size="14.00" fill="#a6adc8">copy</text>
</g>
<!-- scene_ffmpeg -->
<g id="node8" class="node">
<title>scene_ffmpeg</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="1004.25,-277.56 762,-277.56 762,-122.28 1004.25,-122.28 1004.25,-277.56"/>
<text xml:space="preserve" text-anchor="middle" x="883.12" y="-255.62" font-family="monospace" font-size="14.00" fill="#cdd6f4">Scene Detector</text>
<text xml:space="preserve" text-anchor="middle" x="883.12" y="-238.37" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="883.12" y="-221.12" font-family="monospace" font-size="14.00" fill="#cdd6f4">connects to scene.sock</text>
<text xml:space="preserve" text-anchor="middle" x="883.12" y="-203.87" font-family="monospace" font-size="14.00" fill="#cdd6f4">pipes H.264 → ffmpeg:</text>
<text xml:space="preserve" text-anchor="middle" x="883.12" y="-186.62" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;CUDA decode</text>
<text xml:space="preserve" text-anchor="middle" x="883.12" y="-169.37" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;select=gt(scene,thresh)</text>
<text xml:space="preserve" text-anchor="middle" x="883.12" y="-152.12" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;showinfo → timestamps</text>
<text xml:space="preserve" text-anchor="middle" x="883.12" y="-134.87" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;MJPEG → JPEG frames</text>
</g>
<!-- scene_relay&#45;&gt;scene_ffmpeg -->
<g id="edge6" class="edge">
<title>scene_relay&#45;&gt;scene_ffmpeg</title>
<path fill="none" stroke="#a6e3a1" d="M620.55,-630.11C588.15,-595.96 553.88,-559.84 553.88,-559.84 553.88,-559.84 553.88,-354.06 553.88,-354.06 553.88,-354.06 659.95,-304.72 751.68,-262.06"/>
<polygon fill="#a6e3a1" stroke="#a6e3a1" points="752.96,-265.32 760.55,-257.93 750.01,-258.97 752.96,-265.32"/>
<text xml:space="preserve" text-anchor="middle" x="607.5" y="-460.9" font-family="monospace" font-size="14.00" fill="#a6adc8">raw H.264</text>
<text xml:space="preserve" text-anchor="middle" x="607.5" y="-443.65" font-family="monospace" font-size="14.00" fill="#a6adc8">(Unix socket)</text>
</g>
<!-- regression -->
<g id="node16" class="node">
<title>regression</title>
<polygon fill="#3d1e1e" stroke="#f38ba8" points="922.5,-559.84 669.75,-559.84 669.75,-354.06 928.5,-354.06 928.5,-553.84 922.5,-559.84"/>
<polyline fill="none" stroke="#f38ba8" points="922.5,-559.84 922.5,-553.84"/>
<polyline fill="none" stroke="#f38ba8" points="928.5,-553.84 922.5,-553.84"/>
<text xml:space="preserve" text-anchor="middle" x="799.12" y="-537.9" font-family="monospace" font-size="14.00" fill="#f38ba8">⚠ REGRESSED</text>
<text xml:space="preserve" text-anchor="middle" x="799.12" y="-520.65" font-family="monospace" font-size="14.00" fill="#f38ba8">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="799.12" y="-503.4" font-family="monospace" font-size="14.00" fill="#f38ba8">Scene relay (separate pipe)</text>
<text xml:space="preserve" text-anchor="middle" x="799.12" y="-486.15" font-family="monospace" font-size="14.00" fill="#f38ba8">breaks &#39;one behind&#39; flush.</text>
<text xml:space="preserve" text-anchor="middle" x="799.12" y="-468.9" font-family="monospace" font-size="14.00" fill="#f38ba8">try_send drops → decoder</text>
<text xml:space="preserve" text-anchor="middle" x="799.12" y="-451.65" font-family="monospace" font-size="14.00" fill="#f38ba8">corruption until keyframe.</text>
<text xml:space="preserve" text-anchor="middle" x="799.12" y="-418.4" font-family="monospace" font-size="14.00" fill="#f38ba8">Fix: move scene detection</text>
<text xml:space="preserve" text-anchor="middle" x="799.12" y="-401.15" font-family="monospace" font-size="14.00" fill="#f38ba8">into server ffmpeg as 3rd</text>
<text xml:space="preserve" text-anchor="middle" x="799.12" y="-383.9" font-family="monospace" font-size="14.00" fill="#f38ba8">output branch (10&#45;scene&#45;</text>
<text xml:space="preserve" text-anchor="middle" x="799.12" y="-366.65" font-family="monospace" font-size="14.00" fill="#f38ba8">detect&#45;to&#45;rust.md)</text>
</g>
<!-- scene_relay&#45;&gt;regression -->
<g id="edge15" class="edge">
<title>scene_relay&#45;&gt;regression</title>
<path fill="none" stroke="#f38ba8" stroke-dasharray="5,2" d="M708.54,-630.04C718.19,-611.71 729.14,-590.91 740.01,-570.26"/>
<polygon fill="#f38ba8" stroke="#f38ba8" points="743.07,-571.97 744.63,-561.49 736.87,-568.71 743.07,-571.97"/>
</g>
<!-- aac_file -->
<g id="node13" class="node">
<title>aac_file</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="1097.88,-491.46 1094.88,-495.46 1073.88,-495.46 1070.88,-491.46 946.38,-491.46 946.38,-422.44 1097.88,-422.44 1097.88,-491.46"/>
<text xml:space="preserve" text-anchor="middle" x="1022.12" y="-469.52" font-family="monospace" font-size="14.00" fill="#cdd6f4">stream/</text>
<text xml:space="preserve" text-anchor="middle" x="1022.12" y="-452.27" font-family="monospace" font-size="14.00" fill="#cdd6f4">audio.aac</text>
<text xml:space="preserve" text-anchor="middle" x="1022.12" y="-435.02" font-family="monospace" font-size="14.00" fill="#cdd6f4">(ADTS&#45;wrapped)</text>
</g>
<!-- audio_writer&#45;&gt;aac_file -->
<g id="edge9" class="edge">
<title>audio_writer&#45;&gt;aac_file</title>
<path fill="none" stroke="#585b70" d="M942.92,-647.22C960.12,-606.1 986.09,-544.05 1003.56,-502.32"/>
<polygon fill="#585b70" stroke="#585b70" points="1006.7,-503.88 1007.33,-493.3 1000.24,-501.18 1006.7,-503.88"/>
</g>
<!-- active_session&#45;&gt;python -->
<g id="edge14" class="edge">
<title>active_session&#45;&gt;python</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M1190.81,-638.58C1200.19,-603.51 1212.68,-556.8 1222.62,-519.67"/>
<polygon fill="#585b70" stroke="#585b70" points="1225.88,-521 1225.09,-510.43 1219.12,-519.19 1225.88,-521"/>
<text xml:space="preserve" text-anchor="middle" x="1251.64" y="-599.04" font-family="monospace" font-size="14.00" fill="#a6adc8">discovers</text>
<text xml:space="preserve" text-anchor="middle" x="1251.64" y="-581.79" font-family="monospace" font-size="14.00" fill="#a6adc8">session dir</text>
</g>
<!-- frames -->
<g id="node14" class="node">
<title>frames</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="975.38,-51.78 972.38,-55.78 951.38,-55.78 948.38,-51.78 790.88,-51.78 790.88,0 975.38,0 975.38,-51.78"/>
<text xml:space="preserve" text-anchor="middle" x="883.12" y="-29.84" font-family="monospace" font-size="14.00" fill="#cdd6f4">frames/</text>
<text xml:space="preserve" text-anchor="middle" x="883.12" y="-12.59" font-family="monospace" font-size="14.00" fill="#cdd6f4">index.json + *.jpg</text>
</g>
<!-- scene_ffmpeg&#45;&gt;frames -->
<g id="edge10" class="edge">
<title>scene_ffmpeg&#45;&gt;frames</title>
<path fill="none" stroke="#585b70" d="M883.12,-121.96C883.12,-101.46 883.12,-80.27 883.12,-63.11"/>
<polygon fill="#585b70" stroke="#585b70" points="886.63,-63.45 883.13,-53.45 879.63,-63.45 886.63,-63.45"/>
<text xml:space="preserve" text-anchor="middle" x="932.62" y="-90.98" font-family="monospace" font-size="14.00" fill="#a6adc8">JPEG on</text>
<text xml:space="preserve" text-anchor="middle" x="932.62" y="-73.73" font-family="monospace" font-size="14.00" fill="#a6adc8">scene change</text>
</g>
<!-- audio_extract -->
<g id="node9" class="node">
<title>audio_extract</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="1256.12,-251.69 1022.12,-251.69 1022.12,-148.16 1256.12,-148.16 1256.12,-251.69"/>
<text xml:space="preserve" text-anchor="middle" x="1139.12" y="-229.75" font-family="monospace" font-size="14.00" fill="#cdd6f4">Audio Extractor</text>
<text xml:space="preserve" text-anchor="middle" x="1139.12" y="-212.5" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="1139.12" y="-195.25" font-family="monospace" font-size="14.00" fill="#cdd6f4">reads audio.aac</text>
<text xml:space="preserve" text-anchor="middle" x="1139.12" y="-178" font-family="monospace" font-size="14.00" fill="#cdd6f4">ffmpeg → 16kHz mono WAV</text>
<text xml:space="preserve" text-anchor="middle" x="1139.12" y="-160.75" font-family="monospace" font-size="14.00" fill="#cdd6f4">chunks + transcript WAVs</text>
</g>
<!-- audio_dir -->
<g id="node15" class="node">
<title>audio_dir</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="1548.12,-491.46 1545.12,-495.46 1524.12,-495.46 1521.12,-491.46 1380.12,-491.46 1380.12,-422.44 1548.12,-422.44 1548.12,-491.46"/>
<text xml:space="preserve" text-anchor="middle" x="1464.12" y="-469.52" font-family="monospace" font-size="14.00" fill="#cdd6f4">audio/</text>
<text xml:space="preserve" text-anchor="middle" x="1464.12" y="-452.27" font-family="monospace" font-size="14.00" fill="#cdd6f4">chunk_*.wav</text>
<text xml:space="preserve" text-anchor="middle" x="1464.12" y="-435.02" font-family="monospace" font-size="14.00" fill="#cdd6f4">transcript_*.wav</text>
</g>
<!-- audio_extract&#45;&gt;audio_dir -->
<g id="edge11" class="edge">
<title>audio_extract&#45;&gt;audio_dir</title>
<path fill="none" stroke="#585b70" d="M1197.7,-252C1230.6,-280.7 1265.12,-310.81 1265.12,-310.81 1265.12,-310.81 1371.12,-354.06 1371.12,-354.06 1371.12,-354.06 1400.1,-385.81 1425.7,-413.86"/>
<polygon fill="#585b70" stroke="#585b70" points="1422.81,-415.88 1432.14,-420.9 1427.98,-411.16 1422.81,-415.88"/>
</g>
<!-- transcriber -->
<g id="node10" class="node">
<title>transcriber</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="1574.12,-251.69 1274.12,-251.69 1274.12,-148.16 1574.12,-148.16 1574.12,-251.69"/>
<text xml:space="preserve" text-anchor="middle" x="1424.12" y="-229.75" font-family="monospace" font-size="14.00" fill="#cdd6f4">Transcriber</text>
<text xml:space="preserve" text-anchor="middle" x="1424.12" y="-212.5" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="1424.12" y="-195.25" font-family="monospace" font-size="14.00" fill="#cdd6f4">faster&#45;whisper (CUDA)</text>
<text xml:space="preserve" text-anchor="middle" x="1424.12" y="-178" font-family="monospace" font-size="14.00" fill="#cdd6f4">segment grouping</text>
<text xml:space="preserve" text-anchor="middle" x="1424.12" y="-160.75" font-family="monospace" font-size="14.00" fill="#cdd6f4">slider: chunk size + lines/group</text>
</g>
<!-- aac_file&#45;&gt;audio_extract -->
<g id="edge13" class="edge">
<title>aac_file&#45;&gt;audio_extract</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M1037.66,-422.08C1056.29,-381.48 1087.87,-312.64 1110.86,-262.52"/>
<polygon fill="#585b70" stroke="#585b70" points="1114.01,-264.06 1115,-253.51 1107.65,-261.14 1114.01,-264.06"/>
<text xml:space="preserve" text-anchor="middle" x="1104.76" y="-322.76" font-family="monospace" font-size="14.00" fill="#a6adc8">reads</text>
</g>
<!-- audio_dir&#45;&gt;transcriber -->
<g id="edge12" class="edge">
<title>audio_dir&#45;&gt;transcriber</title>
<path fill="none" stroke="#585b70" d="M1458.81,-422.08C1452.48,-381.73 1441.78,-313.5 1433.94,-263.47"/>
<polygon fill="#585b70" stroke="#585b70" points="1437.4,-262.96 1432.39,-253.63 1430.48,-264.05 1437.4,-262.96"/>
<text xml:space="preserve" text-anchor="middle" x="1485.38" y="-322.76" font-family="monospace" font-size="14.00" fill="#a6adc8">WAV chunks</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 24 KiB