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

53
docs/graphs/crates.dot Normal file
View File

@@ -0,0 +1,53 @@
// Mitus — Rust transport workspace (media/) 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, mcrn]\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]
}
}

222
docs/graphs/crates.svg Normal file
View File

@@ -0,0 +1,222 @@
<?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, mcrn]</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>

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,86 @@
// Mitus — Python transport pipeline (default mode, --python or no flag)
// Sender bash script wraps ffmpeg CLI; receiver runs ffmpeg in-process via Python.
digraph python_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"]
// Hardware / OS
drm [label="/dev/dri/card0\n(KMS scanout)" shape=cylinder fillcolor="#1e3a2f" color="#a6e3a1"]
pulse [label="PulseAudio\n─────────────\nmonitor: default sink\nmic: webcam (C922)" shape=cylinder fillcolor="#1e3a2f" color="#a6e3a1"]
net [label="TCP :4444\nmpegts" shape=parallelogram fillcolor="#1e2a3e" color="#89b4fa"]
subgraph cluster_sender {
label="Sender — sender/stream_av.sh" fontcolor="#a6adc8" color="#45475a" fontname="monospace"
watchdog [label="watchdog loop\n─────────────\nffmpeg restart on stall\n(total_size or frame stuck > 10s)\nimmediate restart on\nDRM plane format change"
fillcolor="#2d2038" color="#cba6f7"]
ffmpeg_send [label="ffmpeg CLI\n─────────────\nkmsgrab → hwmap=vaapi\nscale_vaapi 1920x1080 nv12\nh264_vaapi (qp=20, gop=30, no B-frames)\namix(monitor, mic) → aac 128k\nmpegts → TCP"
fillcolor="#1e2d3e" color="#89b4fa"]
}
subgraph cluster_recorder {
label="StreamRecorder — cht/stream/recorder.py" fontcolor="#a6adc8" color="#45475a" fontname="monospace"
ffmpeg_recv [label="ffmpeg listener\n─────────────\nlisten=1 on TCP :4444\n→ 2 outputs:\n fragmented MP4 (recording_*.mp4)\n UDP :4445 (mpegts → mpv)\n stdout pipe (showinfo)"
fillcolor="#1e2d3e" color="#89b4fa"]
scene_pipe [label="scene-detect parser\n─────────────\nreads stdout pipe\nshowinfo → scene timestamps\nemits raw_frame(jpeg, ts)"
fillcolor="#2d2038" color="#cba6f7"]
}
subgraph cluster_processor {
label="SessionProcessor — cht/stream/processor.py" fontcolor="#a6adc8" color="#45475a" fontname="monospace"
frame_writer [label="frame writer\n─────────────\nwrites JPEG to frames/\nappends to index.json\nfires on_new_frames(ts, path)"
fillcolor="#2d2038" color="#cba6f7"]
audio_extract [label="audio extractor\n─────────────\npolls fMP4 for new audio\nffmpeg → 16 kHz mono WAV\nchunks for transcription"
fillcolor="#2d2038" color="#cba6f7"]
tracker [label="RecordingTracker\n─────────────\nffprobe duration\nsums segments\nfeeds timeline UI"
fillcolor="#2d2038" color="#cba6f7"]
}
transcriber [label="TranscriberEngine\n─────────────\ncht/transcriber/engine.py\nfaster-whisper (CUDA)\ngrouped segments → transcript.json"
fillcolor="#2d2038" color="#cba6f7"]
gui [label="Mitus GUI (GTK4)\n─────────────\nMonitor (mpv UDP)\nScrub bar · Frames · Transcript\nAgent input/output"
fillcolor="#2d2038" color="#cba6f7"]
// Outputs
fmp4 [label="stream/\nrecording_*.mp4" shape=folder fillcolor="#2a2a3e" color="#585b70"]
udp [label="UDP :4445\n→ mpv" shape=parallelogram fillcolor="#2a2a3e" color="#585b70"]
frames [label="frames/\nindex.json + *.jpg" shape=folder fillcolor="#2a2a3e" color="#585b70"]
audio [label="audio/\nchunk_*.wav" shape=folder fillcolor="#2a2a3e" color="#585b70"]
txt [label="transcript.json" shape=folder fillcolor="#2a2a3e" color="#585b70"]
// Flow — sender
drm -> ffmpeg_send [label="kmsgrab"]
pulse -> ffmpeg_send [label="-f pulse"]
watchdog -> ffmpeg_send [style=dashed label="restart"]
ffmpeg_send -> net
// Flow — recorder
net -> ffmpeg_recv [label="mpegts"]
ffmpeg_recv -> fmp4
ffmpeg_recv -> udp
ffmpeg_recv -> scene_pipe [label="stdout"]
udp -> gui [label="live\nmonitor"]
// Flow — processor
scene_pipe -> frame_writer [label="raw_frame"]
frame_writer -> frames
fmp4 -> audio_extract [label="poll" style=dashed]
audio_extract -> audio
audio -> transcriber [label="WAV"]
transcriber -> txt
fmp4 -> tracker [label="ffprobe" style=dashed]
tracker -> gui [label="duration"]
// Flow — GUI
frames -> gui
txt -> gui
}

View File

@@ -0,0 +1,308 @@
<?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: python_pipeline Pages: 1 -->
<svg width="1067pt" height="1624pt"
viewBox="0.00 0.00 1067.00 1624.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 1580.56)">
<title>python_pipeline</title>
<polygon fill="#1e1e2e" stroke="none" points="-43.2,43.2 -43.2,-1580.56 1024.25,-1580.56 1024.25,43.2 -43.2,43.2"/>
<g id="clust1" class="cluster">
<title>cluster_sender</title>
<polygon fill="#1e1e2e" stroke="#45475a" points="159.75,-1176.05 159.75,-1529.36 533.75,-1529.36 533.75,-1176.05 159.75,-1176.05"/>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-1512.06" font-family="monospace" font-size="14.00" fill="#a6adc8">Sender — sender/stream_av.sh</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_recorder</title>
<polygon fill="#1e1e2e" stroke="#45475a" points="174.75,-664.16 174.75,-1000.24 512.75,-1000.24 512.75,-664.16 174.75,-664.16"/>
<text xml:space="preserve" text-anchor="middle" x="343.75" y="-982.94" font-family="monospace" font-size="14.00" fill="#a6adc8">StreamRecorder — cht/stream/recorder.py</text>
</g>
<g id="clust3" class="cluster">
<title>cluster_processor</title>
<polygon fill="#1e1e2e" stroke="#45475a" points="135.75,-484.12 135.75,-628.9 873.75,-628.9 873.75,-484.12 135.75,-484.12"/>
<text xml:space="preserve" text-anchor="middle" x="504.75" y="-611.6" font-family="monospace" font-size="14.00" fill="#a6adc8">SessionProcessor — cht/stream/processor.py</text>
</g>
<!-- drm -->
<g id="node1" class="node">
<title>drm</title>
<path fill="#1e3a2f" stroke="#a6e3a1" d="M151.5,-1464.85C151.5,-1468.42 117.55,-1471.32 75.75,-1471.32 33.95,-1471.32 0,-1468.42 0,-1464.85 0,-1464.85 0,-1406.59 0,-1406.59 0,-1403.02 33.95,-1400.12 75.75,-1400.12 117.55,-1400.12 151.5,-1403.02 151.5,-1406.59 151.5,-1406.59 151.5,-1464.85 151.5,-1464.85"/>
<path fill="none" stroke="#a6e3a1" d="M151.5,-1464.85C151.5,-1461.27 117.55,-1458.37 75.75,-1458.37 33.95,-1458.37 0,-1461.27 0,-1464.85"/>
<text xml:space="preserve" text-anchor="middle" x="75.75" y="-1439.67" font-family="monospace" font-size="14.00" fill="#cdd6f4">/dev/dri/card0</text>
<text xml:space="preserve" text-anchor="middle" x="75.75" y="-1422.42" font-family="monospace" font-size="14.00" fill="#cdd6f4">(KMS scanout)</text>
</g>
<!-- ffmpeg_send -->
<g id="node5" class="node">
<title>ffmpeg_send</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="525.62,-1322.08 167.88,-1322.08 167.88,-1184.05 525.62,-1184.05 525.62,-1322.08"/>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-1300.14" font-family="monospace" font-size="14.00" fill="#cdd6f4">ffmpeg CLI</text>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-1282.89" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-1265.64" font-family="monospace" font-size="14.00" fill="#cdd6f4">kmsgrab → hwmap=vaapi</text>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-1248.39" font-family="monospace" font-size="14.00" fill="#cdd6f4">scale_vaapi 1920x1080 nv12</text>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-1231.14" font-family="monospace" font-size="14.00" fill="#cdd6f4">h264_vaapi (qp=20, gop=30, no B&#45;frames)</text>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-1213.89" font-family="monospace" font-size="14.00" fill="#cdd6f4">amix(monitor, mic) → aac 128k</text>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-1196.64" font-family="monospace" font-size="14.00" fill="#cdd6f4">mpegts → TCP</text>
</g>
<!-- drm&#45;&gt;ffmpeg_send -->
<g id="edge1" class="edge">
<title>drm&#45;&gt;ffmpeg_send</title>
<path fill="none" stroke="#585b70" d="M122.29,-1400.17C139.89,-1387.11 155.75,-1375.33 155.75,-1375.33 155.75,-1375.33 189.94,-1353.62 229.39,-1328.57"/>
<polygon fill="#585b70" stroke="#585b70" points="231.13,-1331.62 237.69,-1323.31 227.37,-1325.71 231.13,-1331.62"/>
<text xml:space="preserve" text-anchor="middle" x="235.85" y="-1344.03" font-family="monospace" font-size="14.00" fill="#a6adc8">kmsgrab</text>
</g>
<!-- pulse -->
<g id="node2" class="node">
<title>pulse</title>
<path fill="#1e3a2f" stroke="#a6e3a1" d="M751.38,-1484.25C751.38,-1490.2 704.48,-1495.04 646.75,-1495.04 589.02,-1495.04 542.12,-1490.2 542.12,-1484.25 542.12,-1484.25 542.12,-1387.19 542.12,-1387.19 542.12,-1381.24 589.02,-1376.4 646.75,-1376.4 704.48,-1376.4 751.38,-1381.24 751.38,-1387.19 751.38,-1387.19 751.38,-1484.25 751.38,-1484.25"/>
<path fill="none" stroke="#a6e3a1" d="M751.38,-1484.25C751.38,-1478.3 704.48,-1473.47 646.75,-1473.47 589.02,-1473.47 542.12,-1478.3 542.12,-1484.25"/>
<text xml:space="preserve" text-anchor="middle" x="646.75" y="-1456.92" font-family="monospace" font-size="14.00" fill="#cdd6f4">PulseAudio</text>
<text xml:space="preserve" text-anchor="middle" x="646.75" y="-1439.67" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="646.75" y="-1422.42" font-family="monospace" font-size="14.00" fill="#cdd6f4">monitor: default sink</text>
<text xml:space="preserve" text-anchor="middle" x="646.75" y="-1405.17" font-family="monospace" font-size="14.00" fill="#cdd6f4">mic: webcam (C922)</text>
</g>
<!-- pulse&#45;&gt;ffmpeg_send -->
<g id="edge2" class="edge">
<title>pulse&#45;&gt;ffmpeg_send</title>
<path fill="none" stroke="#585b70" d="M555.11,-1379.54C528.25,-1363.36 498.43,-1345.41 469.95,-1328.25"/>
<polygon fill="#585b70" stroke="#585b70" points="472.01,-1325.41 461.64,-1323.25 468.4,-1331.41 472.01,-1325.41"/>
<text xml:space="preserve" text-anchor="middle" x="547.71" y="-1344.03" font-family="monospace" font-size="14.00" fill="#a6adc8">&#45;f pulse</text>
</g>
<!-- net -->
<g id="node3" class="node">
<title>net</title>
<polygon fill="#1e2a3e" stroke="#89b4fa" points="461.05,-1147.05 279.22,-1147.05 232.45,-1043.49 414.28,-1043.49 461.05,-1147.05"/>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-1099.22" font-family="monospace" font-size="14.00" fill="#cdd6f4">TCP :4444</text>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-1081.97" font-family="monospace" font-size="14.00" fill="#cdd6f4">mpegts</text>
</g>
<!-- ffmpeg_recv -->
<g id="node6" class="node">
<title>ffmpeg_recv</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="505,-966.99 188.5,-966.99 188.5,-828.96 505,-828.96 505,-966.99"/>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-945.05" font-family="monospace" font-size="14.00" fill="#cdd6f4">ffmpeg listener</text>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-927.8" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-910.55" font-family="monospace" font-size="14.00" fill="#cdd6f4">listen=1 on TCP :4444</text>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-893.3" font-family="monospace" font-size="14.00" fill="#cdd6f4">→ 2 outputs:</text>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-876.05" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;fragmented MP4 (recording_*.mp4)</text>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-858.8" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;UDP :4445 (mpegts → mpv)</text>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-841.55" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;stdout pipe (showinfo)</text>
</g>
<!-- net&#45;&gt;ffmpeg_recv -->
<g id="edge5" class="edge">
<title>net&#45;&gt;ffmpeg_recv</title>
<path fill="none" stroke="#585b70" d="M346.75,-1043.21C346.75,-1023.48 346.75,-1000.44 346.75,-978.64"/>
<polygon fill="#585b70" stroke="#585b70" points="350.25,-978.88 346.75,-968.88 343.25,-978.88 350.25,-978.88"/>
<text xml:space="preserve" text-anchor="middle" x="371.5" y="-1012.19" font-family="monospace" font-size="14.00" fill="#a6adc8">mpegts</text>
</g>
<!-- watchdog -->
<g id="node4" class="node">
<title>watchdog</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="500.88,-1496.11 192.62,-1496.11 192.62,-1375.33 500.88,-1375.33 500.88,-1496.11"/>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-1474.17" font-family="monospace" font-size="14.00" fill="#cdd6f4">watchdog loop</text>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-1456.92" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-1439.67" font-family="monospace" font-size="14.00" fill="#cdd6f4">ffmpeg restart on stall</text>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-1422.42" font-family="monospace" font-size="14.00" fill="#cdd6f4">(total_size or frame stuck &gt; 10s)</text>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-1405.17" font-family="monospace" font-size="14.00" fill="#cdd6f4">immediate restart on</text>
<text xml:space="preserve" text-anchor="middle" x="346.75" y="-1387.92" font-family="monospace" font-size="14.00" fill="#cdd6f4">DRM plane format change</text>
</g>
<!-- watchdog&#45;&gt;ffmpeg_send -->
<g id="edge3" class="edge">
<title>watchdog&#45;&gt;ffmpeg_send</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M346.75,-1375.07C346.75,-1361.9 346.75,-1347.74 346.75,-1333.92"/>
<polygon fill="#585b70" stroke="#585b70" points="350.25,-1333.93 346.75,-1323.93 343.25,-1333.93 350.25,-1333.93"/>
<text xml:space="preserve" text-anchor="middle" x="375.62" y="-1344.03" font-family="monospace" font-size="14.00" fill="#a6adc8">restart</text>
</g>
<!-- ffmpeg_send&#45;&gt;net -->
<g id="edge4" class="edge">
<title>ffmpeg_send&#45;&gt;net</title>
<path fill="none" stroke="#585b70" d="M346.75,-1183.73C346.75,-1175.4 346.75,-1166.91 346.75,-1158.67"/>
<polygon fill="#585b70" stroke="#585b70" points="350.25,-1158.73 346.75,-1148.73 343.25,-1158.73 350.25,-1158.73"/>
</g>
<!-- scene_pipe -->
<g id="node7" class="node">
<title>scene_pipe</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="458.12,-775.69 199.38,-775.69 199.38,-672.16 458.12,-672.16 458.12,-775.69"/>
<text xml:space="preserve" text-anchor="middle" x="328.75" y="-753.75" font-family="monospace" font-size="14.00" fill="#cdd6f4">scene&#45;detect parser</text>
<text xml:space="preserve" text-anchor="middle" x="328.75" y="-736.5" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="328.75" y="-719.25" font-family="monospace" font-size="14.00" fill="#cdd6f4">reads stdout pipe</text>
<text xml:space="preserve" text-anchor="middle" x="328.75" y="-702" font-family="monospace" font-size="14.00" fill="#cdd6f4">showinfo → scene timestamps</text>
<text xml:space="preserve" text-anchor="middle" x="328.75" y="-684.75" font-family="monospace" font-size="14.00" fill="#cdd6f4">emits raw_frame(jpeg, ts)</text>
</g>
<!-- ffmpeg_recv&#45;&gt;scene_pipe -->
<g id="edge8" class="edge">
<title>ffmpeg_recv&#45;&gt;scene_pipe</title>
<path fill="none" stroke="#585b70" d="M339.58,-828.48C338.15,-814.79 336.65,-800.49 335.25,-787.03"/>
<polygon fill="#585b70" stroke="#585b70" points="338.77,-787.04 334.25,-777.46 331.81,-787.77 338.77,-787.04"/>
<text xml:space="preserve" text-anchor="middle" x="362.5" y="-797.66" font-family="monospace" font-size="14.00" fill="#a6adc8">stdout</text>
</g>
<!-- fmp4 -->
<g id="node13" class="node">
<title>fmp4</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="680.62,-749.82 677.62,-753.82 656.62,-753.82 653.62,-749.82 520.88,-749.82 520.88,-698.04 680.62,-698.04 680.62,-749.82"/>
<text xml:space="preserve" text-anchor="middle" x="600.75" y="-727.88" font-family="monospace" font-size="14.00" fill="#cdd6f4">stream/</text>
<text xml:space="preserve" text-anchor="middle" x="600.75" y="-710.63" font-family="monospace" font-size="14.00" fill="#cdd6f4">recording_*.mp4</text>
</g>
<!-- ffmpeg_recv&#45;&gt;fmp4 -->
<g id="edge6" class="edge">
<title>ffmpeg_recv&#45;&gt;fmp4</title>
<path fill="none" stroke="#585b70" d="M447.87,-828.48C484.57,-803.62 524.28,-776.73 554.03,-756.57"/>
<polygon fill="#585b70" stroke="#585b70" points="555.75,-759.63 562.07,-751.13 551.83,-753.84 555.75,-759.63"/>
</g>
<!-- udp -->
<g id="node14" class="node">
<title>udp</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="981.05,-775.71 799.22,-775.71 752.45,-672.15 934.28,-672.15 981.05,-775.71"/>
<text xml:space="preserve" text-anchor="middle" x="866.75" y="-727.88" font-family="monospace" font-size="14.00" fill="#cdd6f4">UDP :4445</text>
<text xml:space="preserve" text-anchor="middle" x="866.75" y="-710.63" font-family="monospace" font-size="14.00" fill="#cdd6f4">→ mpv</text>
</g>
<!-- ffmpeg_recv&#45;&gt;udp -->
<g id="edge7" class="edge">
<title>ffmpeg_recv&#45;&gt;udp</title>
<path fill="none" stroke="#585b70" d="M505.24,-844.54C594.36,-815.05 702.36,-779.32 776.98,-754.63"/>
<polygon fill="#585b70" stroke="#585b70" points="777.91,-758.01 786.31,-751.55 775.71,-751.36 777.91,-758.01"/>
</g>
<!-- frame_writer -->
<g id="node8" class="node">
<title>frame_writer</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="419.38,-595.65 144.12,-595.65 144.12,-492.12 419.38,-492.12 419.38,-595.65"/>
<text xml:space="preserve" text-anchor="middle" x="281.75" y="-573.71" font-family="monospace" font-size="14.00" fill="#cdd6f4">frame writer</text>
<text xml:space="preserve" text-anchor="middle" x="281.75" y="-556.46" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="281.75" y="-539.21" font-family="monospace" font-size="14.00" fill="#cdd6f4">writes JPEG to frames/</text>
<text xml:space="preserve" text-anchor="middle" x="281.75" y="-521.96" font-family="monospace" font-size="14.00" fill="#cdd6f4">appends to index.json</text>
<text xml:space="preserve" text-anchor="middle" x="281.75" y="-504.71" font-family="monospace" font-size="14.00" fill="#cdd6f4">fires on_new_frames(ts, path)</text>
</g>
<!-- scene_pipe&#45;&gt;frame_writer -->
<g id="edge10" class="edge">
<title>scene_pipe&#45;&gt;frame_writer</title>
<path fill="none" stroke="#585b70" d="M315.28,-671.91C309.92,-651.61 303.71,-628.06 298.09,-606.8"/>
<polygon fill="#585b70" stroke="#585b70" points="301.54,-606.15 295.61,-597.38 294.77,-607.94 301.54,-606.15"/>
<text xml:space="preserve" text-anchor="middle" x="346.58" y="-640.85" font-family="monospace" font-size="14.00" fill="#a6adc8">raw_frame</text>
</g>
<!-- frames -->
<g id="node15" class="node">
<title>frames</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="464,-192.31 461,-196.31 440,-196.31 437,-192.31 279.5,-192.31 279.5,-140.53 464,-140.53 464,-192.31"/>
<text xml:space="preserve" text-anchor="middle" x="371.75" y="-170.37" font-family="monospace" font-size="14.00" fill="#cdd6f4">frames/</text>
<text xml:space="preserve" text-anchor="middle" x="371.75" y="-153.12" font-family="monospace" font-size="14.00" fill="#cdd6f4">index.json + *.jpg</text>
</g>
<!-- frame_writer&#45;&gt;frames -->
<g id="edge11" class="edge">
<title>frame_writer&#45;&gt;frames</title>
<path fill="none" stroke="#585b70" d="M293.98,-491.86C312.47,-414.71 347.09,-270.31 363.15,-203.29"/>
<polygon fill="#585b70" stroke="#585b70" points="366.47,-204.47 365.39,-193.93 359.66,-202.84 366.47,-204.47"/>
</g>
<!-- audio_extract -->
<g id="node9" class="node">
<title>audio_extract</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="671.75,-595.65 437.75,-595.65 437.75,-492.12 671.75,-492.12 671.75,-595.65"/>
<text xml:space="preserve" text-anchor="middle" x="554.75" y="-573.71" font-family="monospace" font-size="14.00" fill="#cdd6f4">audio extractor</text>
<text xml:space="preserve" text-anchor="middle" x="554.75" y="-556.46" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="554.75" y="-539.21" font-family="monospace" font-size="14.00" fill="#cdd6f4">polls fMP4 for new audio</text>
<text xml:space="preserve" text-anchor="middle" x="554.75" y="-521.96" font-family="monospace" font-size="14.00" fill="#cdd6f4">ffmpeg → 16 kHz mono WAV</text>
<text xml:space="preserve" text-anchor="middle" x="554.75" y="-504.71" font-family="monospace" font-size="14.00" fill="#cdd6f4">chunks for transcription</text>
</g>
<!-- audio -->
<g id="node16" class="node">
<title>audio</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="620.12,-455.12 617.12,-459.12 596.12,-459.12 593.12,-455.12 493.38,-455.12 493.38,-403.34 620.12,-403.34 620.12,-455.12"/>
<text xml:space="preserve" text-anchor="middle" x="556.75" y="-433.18" font-family="monospace" font-size="14.00" fill="#cdd6f4">audio/</text>
<text xml:space="preserve" text-anchor="middle" x="556.75" y="-415.93" font-family="monospace" font-size="14.00" fill="#cdd6f4">chunk_*.wav</text>
</g>
<!-- audio_extract&#45;&gt;audio -->
<g id="edge13" class="edge">
<title>audio_extract&#45;&gt;audio</title>
<path fill="none" stroke="#585b70" d="M555.66,-491.83C555.8,-483.48 555.96,-474.97 556.1,-467.05"/>
<polygon fill="#585b70" stroke="#585b70" points="559.59,-467.16 556.27,-457.1 552.6,-467.03 559.59,-467.16"/>
</g>
<!-- tracker -->
<g id="node10" class="node">
<title>tracker</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="865.88,-595.65 689.62,-595.65 689.62,-492.12 865.88,-492.12 865.88,-595.65"/>
<text xml:space="preserve" text-anchor="middle" x="777.75" y="-573.71" font-family="monospace" font-size="14.00" fill="#cdd6f4">RecordingTracker</text>
<text xml:space="preserve" text-anchor="middle" x="777.75" y="-556.46" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="777.75" y="-539.21" font-family="monospace" font-size="14.00" fill="#cdd6f4">ffprobe duration</text>
<text xml:space="preserve" text-anchor="middle" x="777.75" y="-521.96" font-family="monospace" font-size="14.00" fill="#cdd6f4">sums segments</text>
<text xml:space="preserve" text-anchor="middle" x="777.75" y="-504.71" font-family="monospace" font-size="14.00" fill="#cdd6f4">feeds timeline UI</text>
</g>
<!-- gui -->
<g id="node12" class="node">
<title>gui</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="860.62,-103.53 568.88,-103.53 568.88,0 860.62,0 860.62,-103.53"/>
<text xml:space="preserve" text-anchor="middle" x="714.75" y="-81.59" font-family="monospace" font-size="14.00" fill="#cdd6f4">Mitus GUI (GTK4)</text>
<text xml:space="preserve" text-anchor="middle" x="714.75" y="-64.34" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="714.75" y="-47.09" font-family="monospace" font-size="14.00" fill="#cdd6f4">Monitor (mpv UDP)</text>
<text xml:space="preserve" text-anchor="middle" x="714.75" y="-29.84" font-family="monospace" font-size="14.00" fill="#cdd6f4">Scrub bar · Frames · Transcript</text>
<text xml:space="preserve" text-anchor="middle" x="714.75" y="-12.59" font-family="monospace" font-size="14.00" fill="#cdd6f4">Agent input/output</text>
</g>
<!-- tracker&#45;&gt;gui -->
<g id="edge17" class="edge">
<title>tracker&#45;&gt;gui</title>
<path fill="none" stroke="#585b70" d="M771.41,-491.83C767.7,-462.02 763.75,-430.23 763.75,-430.23 763.75,-430.23 763.75,-430.23 763.75,-165.42 763.75,-165.42 752.97,-140.64 741.53,-114.33"/>
<polygon fill="#585b70" stroke="#585b70" points="744.76,-112.98 737.56,-105.21 738.34,-115.77 744.76,-112.98"/>
<text xml:space="preserve" text-anchor="middle" x="796.75" y="-276.4" font-family="monospace" font-size="14.00" fill="#a6adc8">duration</text>
</g>
<!-- transcriber -->
<g id="node11" class="node">
<title>transcriber</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="726,-332.84 409.5,-332.84 409.5,-229.31 726,-229.31 726,-332.84"/>
<text xml:space="preserve" text-anchor="middle" x="567.75" y="-310.9" font-family="monospace" font-size="14.00" fill="#cdd6f4">TranscriberEngine</text>
<text xml:space="preserve" text-anchor="middle" x="567.75" y="-293.65" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="567.75" y="-276.4" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht/transcriber/engine.py</text>
<text xml:space="preserve" text-anchor="middle" x="567.75" y="-259.15" font-family="monospace" font-size="14.00" fill="#cdd6f4">faster&#45;whisper (CUDA)</text>
<text xml:space="preserve" text-anchor="middle" x="567.75" y="-241.9" font-family="monospace" font-size="14.00" fill="#cdd6f4">grouped segments → transcript.json</text>
</g>
<!-- txt -->
<g id="node17" class="node">
<title>txt</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="691.62,-184.42 688.62,-188.42 667.62,-188.42 664.62,-184.42 531.88,-184.42 531.88,-148.42 691.62,-148.42 691.62,-184.42"/>
<text xml:space="preserve" text-anchor="middle" x="611.75" y="-161.75" font-family="monospace" font-size="14.00" fill="#cdd6f4">transcript.json</text>
</g>
<!-- transcriber&#45;&gt;txt -->
<g id="edge15" class="edge">
<title>transcriber&#45;&gt;txt</title>
<path fill="none" stroke="#585b70" d="M587.69,-229.02C592.18,-217.52 596.79,-205.72 600.76,-195.56"/>
<polygon fill="#585b70" stroke="#585b70" points="603.99,-196.9 604.37,-186.31 597.47,-194.35 603.99,-196.9"/>
</g>
<!-- fmp4&#45;&gt;audio_extract -->
<g id="edge12" class="edge">
<title>fmp4&#45;&gt;audio_extract</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M594.26,-697.8C588.16,-674.22 578.81,-638 570.73,-606.73"/>
<polygon fill="#585b70" stroke="#585b70" points="574.2,-606.17 568.31,-597.37 567.42,-607.92 574.2,-606.17"/>
<text xml:space="preserve" text-anchor="middle" x="598.37" y="-640.85" font-family="monospace" font-size="14.00" fill="#a6adc8">poll</text>
</g>
<!-- fmp4&#45;&gt;tracker -->
<g id="edge16" class="edge">
<title>fmp4&#45;&gt;tracker</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M625.73,-697.8C649.89,-673.5 687.4,-635.77 719.11,-603.87"/>
<polygon fill="#585b70" stroke="#585b70" points="721.4,-606.53 725.97,-596.97 716.43,-601.6 721.4,-606.53"/>
<text xml:space="preserve" text-anchor="middle" x="712.82" y="-640.85" font-family="monospace" font-size="14.00" fill="#a6adc8">ffprobe</text>
</g>
<!-- udp&#45;&gt;gui -->
<g id="edge9" class="edge">
<title>udp&#45;&gt;gui</title>
<path fill="none" stroke="#585b70" d="M874.49,-671.91C882.47,-619.3 893.75,-544.88 893.75,-544.88 893.75,-544.88 893.75,-544.88 893.75,-165.42 893.75,-165.42 850.06,-137.93 805.85,-110.1"/>
<polygon fill="#585b70" stroke="#585b70" points="807.74,-107.15 797.41,-104.79 804.01,-113.08 807.74,-107.15"/>
<text xml:space="preserve" text-anchor="middle" x="922.62" y="-372.04" font-family="monospace" font-size="14.00" fill="#a6adc8">live</text>
<text xml:space="preserve" text-anchor="middle" x="922.62" y="-354.79" font-family="monospace" font-size="14.00" fill="#a6adc8">monitor</text>
</g>
<!-- frames&#45;&gt;gui -->
<g id="edge18" class="edge">
<title>frames&#45;&gt;gui</title>
<path fill="none" stroke="#585b70" d="M448.73,-140.14C481.04,-129.52 519.87,-116.77 557.75,-104.33"/>
<polygon fill="#585b70" stroke="#585b70" points="558.56,-107.75 566.97,-101.3 556.37,-101.1 558.56,-107.75"/>
</g>
<!-- audio&#45;&gt;transcriber -->
<g id="edge14" class="edge">
<title>audio&#45;&gt;transcriber</title>
<path fill="none" stroke="#585b70" d="M558.64,-403.11C559.86,-386.92 561.5,-365.12 563.05,-344.48"/>
<polygon fill="#585b70" stroke="#585b70" points="566.53,-344.92 563.79,-334.69 559.55,-344.4 566.53,-344.92"/>
<text xml:space="preserve" text-anchor="middle" x="574.62" y="-363.41" font-family="monospace" font-size="14.00" fill="#a6adc8">WAV</text>
</g>
<!-- txt&#45;&gt;gui -->
<g id="edge19" class="edge">
<title>txt&#45;&gt;gui</title>
<path fill="none" stroke="#585b70" d="M627.39,-148.31C636.43,-138.42 648.4,-125.33 660.57,-112.02"/>
<polygon fill="#585b70" stroke="#585b70" points="662.96,-114.59 667.13,-104.85 657.8,-109.87 662.96,-114.59"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -0,0 +1,66 @@
// Mitus — Rust client (sender) pipeline — media/client/
// Sender machine (Wayland, VAAPI GPU)
digraph rust_client {
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"]
// 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"]
}

264
docs/graphs/rust_client.svg Normal file
View File

@@ -0,0 +1,264 @@
<?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: rust_client 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>rust_client</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>

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1,71 @@
// Mitus — Rust server (receiver) pipeline — media/server/
// Receiver machine (mcrn: X11, RTX 3080, NVDEC)
digraph rust_server {
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]
}

263
docs/graphs/rust_server.svg Normal file
View File

@@ -0,0 +1,263 @@
<?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: rust_server Pages: 1 -->
<svg width="1429pt" height="1141pt"
viewBox="0.00 0.00 1429.00 1141.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 1097.94)">
<title>rust_server</title>
<polygon fill="#1e1e2e" stroke="none" points="-43.2,43.2 -43.2,-1097.94 1385.33,-1097.94 1385.33,43.2 -43.2,43.2"/>
<g id="clust1" class="cluster">
<title>cluster_rust</title>
<polygon fill="#1e1e2e" stroke="#a6e3a1" points="175.12,-520.12 175.12,-907.93 1205.12,-907.93 1205.12,-520.12 175.12,-520.12"/>
<text xml:space="preserve" text-anchor="middle" x="690.12" y="-890.63" 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="493.12,-114.28 493.12,-310.81 1321.12,-310.81 1321.12,-114.28 493.12,-114.28"/>
<text xml:space="preserve" text-anchor="middle" x="907.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="806.09,-1054.74 583.44,-1054.74 526.16,-951.18 748.81,-951.18 806.09,-1054.74"/>
<text xml:space="preserve" text-anchor="middle" x="666.12" y="-1006.91" font-family="monospace" font-size="14.00" fill="#cdd6f4">TCP :4447</text>
<text xml:space="preserve" text-anchor="middle" x="666.12" y="-989.66" 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="807.88,-874.68 524.38,-874.68 524.38,-719.4 807.88,-719.4 807.88,-874.68"/>
<text xml:space="preserve" text-anchor="middle" x="666.12" y="-852.74" font-family="monospace" font-size="14.00" fill="#cdd6f4">Listener</text>
<text xml:space="preserve" text-anchor="middle" x="666.12" y="-835.49" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="666.12" y="-818.24" font-family="monospace" font-size="14.00" fill="#cdd6f4">TCP accept</text>
<text xml:space="preserve" text-anchor="middle" x="666.12" y="-800.99" font-family="monospace" font-size="14.00" fill="#cdd6f4">reads WirePacket</text>
<text xml:space="preserve" text-anchor="middle" x="666.12" y="-783.74" font-family="monospace" font-size="14.00" fill="#cdd6f4">routes by type:</text>
<text xml:space="preserve" text-anchor="middle" x="666.12" y="-766.49" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;Video → ffmpeg + scene relay</text>
<text xml:space="preserve" text-anchor="middle" x="666.12" y="-749.24" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;Audio → ADTS file</text>
<text xml:space="preserve" text-anchor="middle" x="666.12" y="-731.99" 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="M666.12,-950.77C666.12,-931.28 666.12,-908.46 666.12,-886.5"/>
<polygon fill="#585b70" stroke="#585b70" points="669.63,-886.63 666.13,-876.63 662.63,-886.63 669.63,-886.63"/>
<text xml:space="preserve" text-anchor="middle" x="707.38" y="-919.88" 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="1155.98,-457.62 960.55,-457.62 910.27,-354.06 1105.7,-354.06 1155.98,-457.62"/>
<text xml:space="preserve" text-anchor="middle" x="1033.12" y="-409.79" font-family="monospace" font-size="14.00" fill="#cdd6f4">Python GUI</text>
<text xml:space="preserve" text-anchor="middle" x="1033.12" y="-392.54" 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="425.25,-640.27 183,-640.27 183,-536.75 425.25,-536.75 425.25,-640.27"/>
<text xml:space="preserve" text-anchor="middle" x="304.12" y="-618.34" font-family="monospace" font-size="14.00" fill="#cdd6f4">ffmpeg subprocess</text>
<text xml:space="preserve" text-anchor="middle" x="304.12" y="-601.09" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="304.12" y="-583.84" font-family="monospace" font-size="14.00" fill="#cdd6f4">H.264 pipe:0 → 2 outputs:</text>
<text xml:space="preserve" text-anchor="middle" x="304.12" y="-566.59" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;1. fMP4 (frag_keyframe)</text>
<text xml:space="preserve" text-anchor="middle" x="304.12" y="-549.34" 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="M531.07,-718.99C488.84,-694.9 443.04,-668.76 403.71,-646.33"/>
<polygon fill="#585b70" stroke="#585b70" points="405.59,-643.37 395.17,-641.45 402.12,-649.45 405.59,-643.37"/>
<text xml:space="preserve" text-anchor="middle" x="541.35" y="-679.48" 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="693.38,-648.9 442.88,-648.9 442.88,-528.12 693.38,-528.12 693.38,-648.9"/>
<text xml:space="preserve" text-anchor="middle" x="568.12" y="-626.96" font-family="monospace" font-size="14.00" fill="#cdd6f4">Scene Relay</text>
<text xml:space="preserve" text-anchor="middle" x="568.12" y="-609.71" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="568.12" y="-592.46" font-family="monospace" font-size="14.00" fill="#cdd6f4">Unix socket (scene.sock)</text>
<text xml:space="preserve" text-anchor="middle" x="568.12" y="-575.21" font-family="monospace" font-size="14.00" fill="#cdd6f4">buffers latest keyframe</text>
<text xml:space="preserve" text-anchor="middle" x="568.12" y="-557.96" font-family="monospace" font-size="14.00" fill="#cdd6f4">best&#45;effort: drops if slow</text>
<text xml:space="preserve" text-anchor="middle" x="568.12" y="-540.71" 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="M629.63,-719.14C620.36,-699.6 610.45,-678.71 601.35,-659.53"/>
<polygon fill="#585b70" stroke="#585b70" points="604.63,-658.27 597.18,-650.74 598.3,-661.28 604.63,-658.27"/>
<text xml:space="preserve" text-anchor="middle" x="681.94" y="-688.1" font-family="monospace" font-size="14.00" fill="#a6adc8">H.264 copy</text>
<text xml:space="preserve" text-anchor="middle" x="681.94" y="-670.85" 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="920.75,-631.65 711.5,-631.65 711.5,-545.37 920.75,-545.37 920.75,-631.65"/>
<text xml:space="preserve" text-anchor="middle" x="816.12" y="-609.71" font-family="monospace" font-size="14.00" fill="#cdd6f4">Audio Writer</text>
<text xml:space="preserve" text-anchor="middle" x="816.12" y="-592.46" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="816.12" y="-575.21" font-family="monospace" font-size="14.00" fill="#cdd6f4">ADTS header + raw AAC</text>
<text xml:space="preserve" text-anchor="middle" x="816.12" y="-557.96" 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="M732.7,-719.2C741.82,-708.68 748.12,-701.4 748.12,-701.4 748.12,-701.4 766.59,-671.01 784.28,-641.91"/>
<polygon fill="#585b70" stroke="#585b70" points="787.23,-643.79 789.44,-633.42 781.25,-640.15 787.23,-643.79"/>
<text xml:space="preserve" text-anchor="middle" x="805.34" y="-679.48" 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="1191.5,-640.27 938.75,-640.27 938.75,-536.75 1197.5,-536.75 1197.5,-634.27 1191.5,-640.27"/>
<polyline fill="none" stroke="#585b70" points="1191.5,-640.27 1191.5,-634.27"/>
<polyline fill="none" stroke="#585b70" points="1197.5,-634.27 1191.5,-634.27"/>
<text xml:space="preserve" text-anchor="middle" x="1068.12" y="-618.34" font-family="monospace" font-size="14.00" fill="#cdd6f4">active&#45;session</text>
<text xml:space="preserve" text-anchor="middle" x="1068.12" y="-601.09" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="1068.12" y="-583.84" font-family="monospace" font-size="14.00" fill="#cdd6f4">file at data/active&#45;session</text>
<text xml:space="preserve" text-anchor="middle" x="1068.12" y="-566.59" font-family="monospace" font-size="14.00" fill="#cdd6f4">Python polls to discover</text>
<text xml:space="preserve" text-anchor="middle" x="1068.12" y="-549.34" 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="M808.14,-723.08C857.5,-697.72 912.08,-669.68 958.43,-645.87"/>
<polygon fill="#585b70" stroke="#585b70" points="959.85,-649.07 967.14,-641.39 956.65,-642.85 959.85,-649.07"/>
<text xml:space="preserve" text-anchor="middle" x="976.83" y="-679.48" 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,-440.36 173.25,-444.36 152.25,-444.36 149.25,-440.36 0,-440.36 0,-371.33 176.25,-371.33 176.25,-440.36"/>
<text xml:space="preserve" text-anchor="middle" x="88.12" y="-418.42" font-family="monospace" font-size="14.00" fill="#cdd6f4">stream/</text>
<text xml:space="preserve" text-anchor="middle" x="88.12" y="-401.17" font-family="monospace" font-size="14.00" fill="#cdd6f4">recording_000.mp4</text>
<text xml:space="preserve" text-anchor="middle" x="88.12" y="-383.92" 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="M243.09,-536.46C209.57,-508.42 168.54,-474.11 137.13,-447.83"/>
<polygon fill="#585b70" stroke="#585b70" points="139.61,-445.34 129.7,-441.61 135.12,-450.71 139.61,-445.34"/>
<text xml:space="preserve" text-anchor="middle" x="225.56" y="-488.19" 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,-457.62 258.33,-457.62 194.06,-354.06 443.92,-354.06 508.19,-457.62"/>
<text xml:space="preserve" text-anchor="middle" x="351.12" y="-409.79" font-family="monospace" font-size="14.00" fill="#cdd6f4">UDP :4445</text>
<text xml:space="preserve" text-anchor="middle" x="351.12" y="-392.54" 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="M317.34,-536.7C322.82,-515.63 329.24,-490.99 334.99,-468.86"/>
<polygon fill="#585b70" stroke="#585b70" points="338.33,-469.92 337.46,-459.36 331.56,-468.15 338.33,-469.92"/>
<text xml:space="preserve" text-anchor="middle" x="349.72" y="-488.19" 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="743.25,-277.56 501,-277.56 501,-122.28 743.25,-122.28 743.25,-277.56"/>
<text xml:space="preserve" text-anchor="middle" x="622.12" y="-255.62" font-family="monospace" font-size="14.00" fill="#cdd6f4">Scene Detector</text>
<text xml:space="preserve" text-anchor="middle" x="622.12" y="-238.37" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="622.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="622.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="622.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="622.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="622.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="622.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="M576.44,-527.95C585.36,-464.09 599.62,-362.03 609.84,-288.9"/>
<polygon fill="#a6e3a1" stroke="#a6e3a1" points="613.25,-289.77 611.17,-279.38 606.32,-288.8 613.25,-289.77"/>
<text xml:space="preserve" text-anchor="middle" x="654.12" y="-409.79" font-family="monospace" font-size="14.00" fill="#a6adc8">raw H.264</text>
<text xml:space="preserve" text-anchor="middle" x="654.12" y="-392.54" font-family="monospace" font-size="14.00" fill="#a6adc8">(Unix socket)</text>
</g>
<!-- aac_file -->
<g id="node13" class="node">
<title>aac_file</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="891.88,-440.36 888.88,-444.36 867.88,-444.36 864.88,-440.36 740.38,-440.36 740.38,-371.33 891.88,-371.33 891.88,-440.36"/>
<text xml:space="preserve" text-anchor="middle" x="816.12" y="-418.42" font-family="monospace" font-size="14.00" fill="#cdd6f4">stream/</text>
<text xml:space="preserve" text-anchor="middle" x="816.12" y="-401.17" font-family="monospace" font-size="14.00" fill="#cdd6f4">audio.aac</text>
<text xml:space="preserve" text-anchor="middle" x="816.12" y="-383.92" 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="M816.12,-545.15C816.12,-517.19 816.12,-480.6 816.12,-452.07"/>
<polygon fill="#585b70" stroke="#585b70" points="819.63,-452.3 816.13,-442.3 812.63,-452.3 819.63,-452.3"/>
</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="M1058.28,-536.7C1054.22,-515.73 1049.47,-491.21 1045.2,-469.16"/>
<polygon fill="#585b70" stroke="#585b70" points="1048.64,-468.53 1043.3,-459.38 1041.77,-469.86 1048.64,-468.53"/>
<text xml:space="preserve" text-anchor="middle" x="1098.1" y="-496.82" font-family="monospace" font-size="14.00" fill="#a6adc8">discovers</text>
<text xml:space="preserve" text-anchor="middle" x="1098.1" y="-479.57" 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="714.38,-51.78 711.38,-55.78 690.38,-55.78 687.38,-51.78 529.88,-51.78 529.88,0 714.38,0 714.38,-51.78"/>
<text xml:space="preserve" text-anchor="middle" x="622.12" y="-29.84" font-family="monospace" font-size="14.00" fill="#cdd6f4">frames/</text>
<text xml:space="preserve" text-anchor="middle" x="622.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="M622.12,-121.96C622.12,-101.46 622.12,-80.27 622.12,-63.11"/>
<polygon fill="#585b70" stroke="#585b70" points="625.63,-63.45 622.13,-53.45 618.63,-63.45 625.63,-63.45"/>
<text xml:space="preserve" text-anchor="middle" x="671.62" y="-90.98" font-family="monospace" font-size="14.00" fill="#a6adc8">JPEG on</text>
<text xml:space="preserve" text-anchor="middle" x="671.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="995.12,-251.69 761.12,-251.69 761.12,-148.16 995.12,-148.16 995.12,-251.69"/>
<text xml:space="preserve" text-anchor="middle" x="878.12" y="-229.75" font-family="monospace" font-size="14.00" fill="#cdd6f4">Audio Extractor</text>
<text xml:space="preserve" text-anchor="middle" x="878.12" y="-212.5" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="878.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="878.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="878.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="1342.12,-440.36 1339.12,-444.36 1318.12,-444.36 1315.12,-440.36 1174.12,-440.36 1174.12,-371.33 1342.12,-371.33 1342.12,-440.36"/>
<text xml:space="preserve" text-anchor="middle" x="1258.12" y="-418.42" font-family="monospace" font-size="14.00" fill="#cdd6f4">audio/</text>
<text xml:space="preserve" text-anchor="middle" x="1258.12" y="-401.17" font-family="monospace" font-size="14.00" fill="#cdd6f4">chunk_*.wav</text>
<text xml:space="preserve" text-anchor="middle" x="1258.12" y="-383.92" 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="M936.7,-252C969.6,-280.7 1004.12,-310.81 1004.12,-310.81 1004.12,-310.81 1165.12,-354.06 1165.12,-354.06 1165.12,-354.06 1173.87,-358.83 1185.99,-365.45"/>
<polygon fill="#585b70" stroke="#585b70" points="1184.05,-368.38 1194.51,-370.1 1187.41,-362.24 1184.05,-368.38"/>
</g>
<!-- transcriber -->
<g id="node10" class="node">
<title>transcriber</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="1313.12,-251.69 1013.12,-251.69 1013.12,-148.16 1313.12,-148.16 1313.12,-251.69"/>
<text xml:space="preserve" text-anchor="middle" x="1163.12" y="-229.75" font-family="monospace" font-size="14.00" fill="#cdd6f4">Transcriber</text>
<text xml:space="preserve" text-anchor="middle" x="1163.12" y="-212.5" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="1163.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="1163.12" y="-178" font-family="monospace" font-size="14.00" fill="#cdd6f4">segment grouping</text>
<text xml:space="preserve" text-anchor="middle" x="1163.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="M826.36,-371.17C835.31,-341.74 848.52,-298.28 859.36,-262.63"/>
<polygon fill="#585b70" stroke="#585b70" points="862.64,-263.88 862.2,-253.3 855.94,-261.85 862.64,-263.88"/>
<text xml:space="preserve" text-anchor="middle" x="862.2" 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="M1242.44,-371.17C1228.67,-341.62 1208.31,-297.91 1191.66,-262.17"/>
<polygon fill="#585b70" stroke="#585b70" points="1194.89,-260.81 1187.49,-253.22 1188.54,-263.76 1194.89,-260.81"/>
<text xml:space="preserve" text-anchor="middle" x="1265.93" y="-322.76" font-family="monospace" font-size="14.00" fill="#a6adc8">WAV chunks</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 21 KiB

77
docs/graphs/system.dot Normal file
View File

@@ -0,0 +1,77 @@
// Mitus — top-level architecture
// Sender (Wayland, VAAPI) → network → Receiver (X11, NVDEC/NVENC) → Mitus GUI app
// Two transport modes share the same recording layout and same GUI.
digraph system {
graph [fontname="monospace" bgcolor="#1e1e2e" rankdir=LR pad="0.6" splines=polyline nodesep=0.5 ranksep=0.8]
node [fontname="monospace" fontcolor="#cdd6f4" style=filled shape=box
fillcolor="#313244" color="#585b70" margin="0.25,0.14"]
edge [color="#585b70" fontname="monospace" fontcolor="#a6adc8"]
subgraph cluster_sender {
label="Sender machine — Wayland, VAAPI GPU" fontcolor="#a6adc8" color="#45475a" fontname="monospace"
capture_py [label="kmsgrab + PulseAudio\n─────────────\nsender/stream_av.sh\nffmpeg CLI · h264_vaapi · AAC\nmpegts over TCP" fillcolor="#2d2038" color="#cba6f7"]
capture_rs [label="cht-client (Rust)\n─────────────\nmedia/client/\nffmpeg subprocess (subprocess backend)\nNUT demux → mpsc → WirePacket TCP" fillcolor="#1e2d3e" color="#89b4fa"]
}
subgraph cluster_net {
label="Network" fontcolor="#a6adc8" color="#45475a" fontname="monospace"
net_py [label="TCP :4444\nmpegts" shape=parallelogram fillcolor="#1e2a3e" color="#89b4fa"]
net_rs [label="TCP :4447\nWirePacket framing" shape=parallelogram fillcolor="#1e2a3e" color="#89b4fa"]
}
subgraph cluster_receiver {
label="Receiver (mcrn) — X11, NVENC/NVDEC GPU" fontcolor="#a6adc8" color="#45475a" fontname="monospace"
recorder_py [label="StreamRecorder (Python)\n─────────────\ncht/stream/recorder.py\nffmpeg listener · TCP receive\nfMP4 writer · UDP relay\nstdout-pipe scene detect"
fillcolor="#2d2038" color="#cba6f7"]
recorder_rs [label="cht-server (Rust)\n─────────────\nmedia/server/\nWirePacket router\nfMP4 + UDP relay (ffmpeg)\nADTS audio writer\nUnix-socket scene relay"
fillcolor="#1e2d3e" color="#89b4fa"]
processor [label="SessionProcessor (Python)\n─────────────\ncht/stream/processor.py\nfMP4 → audio.wav (ffmpeg)\nchunked WAVs for transcribe\n[Rust mode: scene detect via\nUnix socket → ffmpeg pipe]"
fillcolor="#2d2038" color="#cba6f7"]
transcriber [label="Transcriber\n─────────────\ncht/transcriber/engine.py\nfaster-whisper · CUDA\nsegment grouping"
fillcolor="#2d2038" color="#cba6f7"]
gui [label="Mitus GUI (GTK4 + libadwaita)\n─────────────\ncht/window.py · cht/ui/*\nMonitor (mpv UDP) · Scrub bar\nFrames panel · Transcript panel\nAgent input/output"
fillcolor="#2d2038" color="#cba6f7"]
agent [label="Agent runner\n─────────────\ncht/agent/*\nClaude SDK · OpenAI/Groq\n@F frame refs · @T transcript refs"
fillcolor="#2d2038" color="#cba6f7"]
store [label="data/<session_id>/\n─────────────\nstream/recording_*.mp4\nstream/audio.aac (Rust mode)\nframes/*.jpg + index.json\naudio/chunk_*.wav\ntranscript.json · thread.json"
shape=folder fillcolor="#2a2a3e" color="#585b70"]
}
// Python transport flow
capture_py -> net_py [color="#cba6f7"]
net_py -> recorder_py [color="#cba6f7"]
recorder_py -> store [color="#cba6f7"]
recorder_py -> processor [label="raw scene\nframes" color="#cba6f7"]
// Rust transport flow
capture_rs -> net_rs [color="#89b4fa"]
net_rs -> recorder_rs [color="#89b4fa"]
recorder_rs -> store [color="#89b4fa"]
recorder_rs -> processor [label="scene.sock\n(H.264)" style=dashed color="#a6e3a1"]
// Shared downstream
store -> processor [style=dashed]
processor -> transcriber [label="WAV chunks"]
transcriber -> store [label="transcript.json"]
store -> gui [label="files + watchers"]
gui -> agent [label="@-mentions"]
agent -> store [label="thread.json" style=dashed]
// Legend
subgraph cluster_legend {
label="Legend" fontcolor="#a6adc8" color="#585b70" fontname="monospace"
l_py [label="Python" fillcolor="#2d2038" color="#cba6f7"]
l_rs [label="Rust" fillcolor="#1e2d3e" color="#89b4fa"]
l_io [label="I/O · network" shape=parallelogram fillcolor="#1e2a3e" color="#89b4fa"]
l_fs [label="filesystem" shape=folder fillcolor="#2a2a3e" color="#585b70"]
}
}

262
docs/graphs/system.svg Normal file
View File

@@ -0,0 +1,262 @@
<?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: system Pages: 1 -->
<svg width="3430pt" height="767pt"
viewBox="0.00 0.00 3430.00 767.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 724.2)">
<title>system</title>
<polygon fill="#1e1e2e" stroke="none" points="-43.2,43.2 -43.2,-724.2 3386.51,-724.2 3386.51,43.2 -43.2,43.2"/>
<g id="clust1" class="cluster">
<title>cluster_sender</title>
<polygon fill="#1e1e2e" stroke="#45475a" points="8,-40 8,-329 373.5,-329 373.5,-40 8,-40"/>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-311.7" font-family="monospace" font-size="14.00" fill="#a6adc8">Sender machine — Wayland, VAAPI GPU</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_net</title>
<polygon fill="#1e1e2e" stroke="#45475a" points="416.5,-37 416.5,-333 815.06,-333 815.06,-37 416.5,-37"/>
<text xml:space="preserve" text-anchor="middle" x="615.78" y="-315.7" font-family="monospace" font-size="14.00" fill="#a6adc8">Network</text>
</g>
<g id="clust3" class="cluster">
<title>cluster_receiver</title>
<polygon fill="#1e1e2e" stroke="#45475a" points="858.06,-8 858.06,-349 3335.31,-349 3335.31,-8 858.06,-8"/>
<text xml:space="preserve" text-anchor="middle" x="2096.69" y="-331.7" font-family="monospace" font-size="14.00" fill="#a6adc8">Receiver (mcrn) — X11, NVENC/NVDEC GPU</text>
</g>
<g id="clust4" class="cluster">
<title>cluster_legend</title>
<polygon fill="#1e1e2e" stroke="#585b70" points="34.24,-337 34.24,-673 347.26,-673 347.26,-337 34.24,-337"/>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-655.7" font-family="monospace" font-size="14.00" fill="#a6adc8">Legend</text>
</g>
<!-- capture_py -->
<g id="node1" class="node">
<title>capture_py</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="328.38,-296.2 53.12,-296.2 53.12,-189.8 328.38,-189.8 328.38,-296.2"/>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-272.82" font-family="monospace" font-size="14.00" fill="#cdd6f4">kmsgrab + PulseAudio</text>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-255.57" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-238.32" font-family="monospace" font-size="14.00" fill="#cdd6f4">sender/stream_av.sh</text>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-221.07" font-family="monospace" font-size="14.00" fill="#cdd6f4">ffmpeg CLI · h264_vaapi · AAC</text>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-203.82" font-family="monospace" font-size="14.00" fill="#cdd6f4">mpegts over TCP</text>
</g>
<!-- net_py -->
<g id="node3" class="node">
<title>net_py</title>
<polygon fill="#1e2a3e" stroke="#89b4fa" points="730.08,-299.66 548.25,-299.66 501.48,-190.34 683.31,-190.34 730.08,-299.66"/>
<text xml:space="preserve" text-anchor="middle" x="615.78" y="-248.95" font-family="monospace" font-size="14.00" fill="#cdd6f4">TCP :4444</text>
<text xml:space="preserve" text-anchor="middle" x="615.78" y="-231.7" font-family="monospace" font-size="14.00" fill="#cdd6f4">mpegts</text>
</g>
<!-- capture_py&#45;&gt;net_py -->
<g id="edge1" class="edge">
<title>capture_py&#45;&gt;net_py</title>
<path fill="none" stroke="#cba6f7" d="M328.8,-243.65C388.75,-243.93 457.83,-244.26 513.09,-244.52"/>
<polygon fill="#cba6f7" stroke="#cba6f7" points="512.77,-248.02 522.79,-244.57 512.8,-241.02 512.77,-248.02"/>
</g>
<!-- capture_rs -->
<g id="node2" class="node">
<title>capture_rs</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="365.5,-154.2 16,-154.2 16,-47.8 365.5,-47.8 365.5,-154.2"/>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-130.82" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht&#45;client (Rust)</text>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-113.58" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-96.33" font-family="monospace" font-size="14.00" fill="#cdd6f4">media/client/</text>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-79.08" font-family="monospace" font-size="14.00" fill="#cdd6f4">ffmpeg subprocess (subprocess backend)</text>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-61.83" font-family="monospace" font-size="14.00" fill="#cdd6f4">NUT demux → mpsc → WirePacket TCP</text>
</g>
<!-- net_rs -->
<g id="node4" class="node">
<title>net_rs</title>
<polygon fill="#1e2a3e" stroke="#89b4fa" points="807.06,-154.66 502.78,-154.66 424.5,-45.34 728.79,-45.34 807.06,-154.66"/>
<text xml:space="preserve" text-anchor="middle" x="615.78" y="-103.95" font-family="monospace" font-size="14.00" fill="#cdd6f4">TCP :4447</text>
<text xml:space="preserve" text-anchor="middle" x="615.78" y="-86.7" font-family="monospace" font-size="14.00" fill="#cdd6f4">WirePacket framing</text>
</g>
<!-- capture_rs&#45;&gt;net_rs -->
<g id="edge5" class="edge">
<title>capture_rs&#45;&gt;net_rs</title>
<path fill="none" stroke="#89b4fa" d="M365.95,-100.59C394.5,-100.52 423.99,-100.45 452.24,-100.38"/>
<polygon fill="#89b4fa" stroke="#89b4fa" points="451.91,-103.89 461.91,-100.36 451.9,-96.89 451.91,-103.89"/>
</g>
<!-- recorder_py -->
<g id="node5" class="node">
<title>recorder_py</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="1141.31,-315.83 866.06,-315.83 866.06,-192.17 1141.31,-192.17 1141.31,-315.83"/>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-292.45" font-family="monospace" font-size="14.00" fill="#cdd6f4">StreamRecorder (Python)</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-275.2" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-257.95" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht/stream/recorder.py</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-240.7" font-family="monospace" font-size="14.00" fill="#cdd6f4">ffmpeg listener · TCP receive</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-223.45" font-family="monospace" font-size="14.00" fill="#cdd6f4">fMP4 writer · UDP relay</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-206.2" font-family="monospace" font-size="14.00" fill="#cdd6f4">stdout&#45;pipe scene detect</text>
</g>
<!-- net_py&#45;&gt;recorder_py -->
<g id="edge2" class="edge">
<title>net_py&#45;&gt;recorder_py</title>
<path fill="none" stroke="#cba6f7" d="M707.75,-247.12C751.47,-248.14 805,-249.39 854.39,-250.54"/>
<polygon fill="#cba6f7" stroke="#cba6f7" points="854.26,-254.04 864.34,-250.77 854.43,-247.04 854.26,-254.04"/>
</g>
<!-- recorder_rs -->
<g id="node6" class="node">
<title>recorder_rs</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="1124.81,-156.45 882.56,-156.45 882.56,-15.55 1124.81,-15.55 1124.81,-156.45"/>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-133.07" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht&#45;server (Rust)</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-115.83" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-98.58" font-family="monospace" font-size="14.00" fill="#cdd6f4">media/server/</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-81.33" font-family="monospace" font-size="14.00" fill="#cdd6f4">WirePacket router</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-64.08" font-family="monospace" font-size="14.00" fill="#cdd6f4">fMP4 + UDP relay (ffmpeg)</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-46.83" font-family="monospace" font-size="14.00" fill="#cdd6f4">ADTS audio writer</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-29.57" font-family="monospace" font-size="14.00" fill="#cdd6f4">Unix&#45;socket scene relay</text>
</g>
<!-- net_rs&#45;&gt;recorder_rs -->
<g id="edge6" class="edge">
<title>net_rs&#45;&gt;recorder_rs</title>
<path fill="none" stroke="#89b4fa" d="M764.6,-94.64C799.57,-93.37 836.63,-92.02 870.83,-90.78"/>
<polygon fill="#89b4fa" stroke="#89b4fa" points="870.94,-94.28 880.8,-90.42 870.68,-87.29 870.94,-94.28"/>
</g>
<!-- processor -->
<g id="node7" class="node">
<title>processor</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="1548.81,-219.45 1281.81,-219.45 1281.81,-78.55 1548.81,-78.55 1548.81,-219.45"/>
<text xml:space="preserve" text-anchor="middle" x="1415.31" y="-196.07" font-family="monospace" font-size="14.00" fill="#cdd6f4">SessionProcessor (Python)</text>
<text xml:space="preserve" text-anchor="middle" x="1415.31" y="-178.82" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="1415.31" y="-161.57" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht/stream/processor.py</text>
<text xml:space="preserve" text-anchor="middle" x="1415.31" y="-144.32" font-family="monospace" font-size="14.00" fill="#cdd6f4">fMP4 → audio.wav (ffmpeg)</text>
<text xml:space="preserve" text-anchor="middle" x="1415.31" y="-127.08" font-family="monospace" font-size="14.00" fill="#cdd6f4">chunked WAVs for transcribe</text>
<text xml:space="preserve" text-anchor="middle" x="1415.31" y="-109.83" font-family="monospace" font-size="14.00" fill="#cdd6f4">[Rust mode: scene detect via</text>
<text xml:space="preserve" text-anchor="middle" x="1415.31" y="-92.58" font-family="monospace" font-size="14.00" fill="#cdd6f4">Unix socket → ffmpeg pipe]</text>
</g>
<!-- recorder_py&#45;&gt;processor -->
<g id="edge4" class="edge">
<title>recorder_py&#45;&gt;processor</title>
<path fill="none" stroke="#cba6f7" d="M1141.66,-218.89C1183.07,-208.27 1228.67,-196.59 1270.52,-185.86"/>
<polygon fill="#cba6f7" stroke="#cba6f7" points="1271.36,-189.26 1280.18,-183.38 1269.62,-182.48 1271.36,-189.26"/>
<text xml:space="preserve" text-anchor="middle" x="1211.56" y="-232.1" font-family="monospace" font-size="14.00" fill="#a6adc8">raw scene</text>
<text xml:space="preserve" text-anchor="middle" x="1211.56" y="-214.85" font-family="monospace" font-size="14.00" fill="#a6adc8">frames</text>
</g>
<!-- store -->
<g id="node11" class="node">
<title>store</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="2388.56,-233.45 2385.56,-237.45 2364.56,-237.45 2361.56,-233.45 2113.31,-233.45 2113.31,-92.55 2388.56,-92.55 2388.56,-233.45"/>
<text xml:space="preserve" text-anchor="middle" x="2250.94" y="-210.07" font-family="monospace" font-size="14.00" fill="#cdd6f4">data/&lt;session_id&gt;/</text>
<text xml:space="preserve" text-anchor="middle" x="2250.94" y="-192.82" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="2250.94" y="-175.57" font-family="monospace" font-size="14.00" fill="#cdd6f4">stream/recording_*.mp4</text>
<text xml:space="preserve" text-anchor="middle" x="2250.94" y="-158.32" font-family="monospace" font-size="14.00" fill="#cdd6f4">stream/audio.aac (Rust mode)</text>
<text xml:space="preserve" text-anchor="middle" x="2250.94" y="-141.07" font-family="monospace" font-size="14.00" fill="#cdd6f4">frames/*.jpg + index.json</text>
<text xml:space="preserve" text-anchor="middle" x="2250.94" y="-123.83" font-family="monospace" font-size="14.00" fill="#cdd6f4">audio/chunk_*.wav</text>
<text xml:space="preserve" text-anchor="middle" x="2250.94" y="-106.58" font-family="monospace" font-size="14.00" fill="#cdd6f4">transcript.json · thread.json</text>
</g>
<!-- recorder_py&#45;&gt;store -->
<g id="edge3" class="edge">
<title>recorder_py&#45;&gt;store</title>
<path fill="none" stroke="#cba6f7" d="M1141.45,-253.45C1198.97,-253.22 1252.81,-253 1252.81,-253 1252.81,-253 1548.81,-237 1548.81,-237 1548.81,-237 1931.56,-211 1931.56,-211 1931.56,-211 2084.31,-200 2084.31,-200 2084.31,-200 2091.16,-198.47 2102.08,-196.03"/>
<polygon fill="#cba6f7" stroke="#cba6f7" points="2102.69,-199.48 2111.68,-193.89 2101.16,-192.65 2102.69,-199.48"/>
</g>
<!-- recorder_rs&#45;&gt;processor -->
<g id="edge8" class="edge">
<title>recorder_rs&#45;&gt;processor</title>
<path fill="none" stroke="#a6e3a1" stroke-dasharray="5,2" d="M1125.12,-98.86C1188.33,-105.61 1252.81,-112.5 1252.81,-112.5 1252.81,-112.5 1259.59,-114.03 1270.37,-116.47"/>
<polygon fill="#a6e3a1" stroke="#a6e3a1" points="1269.31,-119.82 1279.84,-118.61 1270.85,-112.99 1269.31,-119.82"/>
<text xml:space="preserve" text-anchor="middle" x="1211.56" y="-133.7" font-family="monospace" font-size="14.00" fill="#a6adc8">scene.sock</text>
<text xml:space="preserve" text-anchor="middle" x="1211.56" y="-116.45" font-family="monospace" font-size="14.00" fill="#a6adc8">(H.264)</text>
</g>
<!-- recorder_rs&#45;&gt;store -->
<g id="edge7" class="edge">
<title>recorder_rs&#45;&gt;store</title>
<path fill="none" stroke="#89b4fa" d="M1125.03,-75.14C1199.82,-68.4 1281.81,-61 1281.81,-61 1281.81,-61 1689.31,-59 1689.31,-59 1689.31,-59 1931.56,-59 1931.56,-59 1931.56,-59 2018.95,-87.55 2102.16,-114.73"/>
<polygon fill="#89b4fa" stroke="#89b4fa" points="2101.04,-118.04 2111.63,-117.82 2103.21,-111.39 2101.04,-118.04"/>
</g>
<!-- transcriber -->
<g id="node8" class="node">
<title>transcriber</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="1931.56,-183.2 1689.31,-183.2 1689.31,-76.8 1931.56,-76.8 1931.56,-183.2"/>
<text xml:space="preserve" text-anchor="middle" x="1810.44" y="-159.82" font-family="monospace" font-size="14.00" fill="#cdd6f4">Transcriber</text>
<text xml:space="preserve" text-anchor="middle" x="1810.44" y="-142.57" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="1810.44" y="-125.33" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht/transcriber/engine.py</text>
<text xml:space="preserve" text-anchor="middle" x="1810.44" y="-108.08" font-family="monospace" font-size="14.00" fill="#cdd6f4">faster&#45;whisper · CUDA</text>
<text xml:space="preserve" text-anchor="middle" x="1810.44" y="-90.83" font-family="monospace" font-size="14.00" fill="#cdd6f4">segment grouping</text>
</g>
<!-- processor&#45;&gt;transcriber -->
<g id="edge10" class="edge">
<title>processor&#45;&gt;transcriber</title>
<path fill="none" stroke="#585b70" d="M1549.13,-142.58C1590.57,-140.58 1636.24,-138.37 1677.61,-136.37"/>
<polygon fill="#585b70" stroke="#585b70" points="1677.54,-139.88 1687.36,-135.9 1677.2,-132.89 1677.54,-139.88"/>
<text xml:space="preserve" text-anchor="middle" x="1619.06" y="-144.59" font-family="monospace" font-size="14.00" fill="#a6adc8">WAV chunks</text>
</g>
<!-- transcriber&#45;&gt;store -->
<g id="edge11" class="edge">
<title>transcriber&#45;&gt;store</title>
<path fill="none" stroke="#585b70" d="M1931.84,-139.06C1984.38,-143.02 2046.5,-147.69 2101.83,-151.85"/>
<polygon fill="#585b70" stroke="#585b70" points="2101.42,-155.33 2111.66,-152.59 2101.95,-148.35 2101.42,-155.33"/>
<text xml:space="preserve" text-anchor="middle" x="2022.44" y="-154.38" font-family="monospace" font-size="14.00" fill="#a6adc8">transcript.json</text>
</g>
<!-- gui -->
<g id="node9" class="node">
<title>gui</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="2870.31,-244.83 2578.56,-244.83 2578.56,-121.17 2870.31,-121.17 2870.31,-244.83"/>
<text xml:space="preserve" text-anchor="middle" x="2724.44" y="-221.45" font-family="monospace" font-size="14.00" fill="#cdd6f4">Mitus GUI (GTK4 + libadwaita)</text>
<text xml:space="preserve" text-anchor="middle" x="2724.44" y="-204.2" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="2724.44" y="-186.95" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht/window.py · cht/ui/*</text>
<text xml:space="preserve" text-anchor="middle" x="2724.44" y="-169.7" font-family="monospace" font-size="14.00" fill="#cdd6f4">Monitor (mpv UDP) · Scrub bar</text>
<text xml:space="preserve" text-anchor="middle" x="2724.44" y="-152.45" font-family="monospace" font-size="14.00" fill="#cdd6f4">Frames panel · Transcript panel</text>
<text xml:space="preserve" text-anchor="middle" x="2724.44" y="-135.2" font-family="monospace" font-size="14.00" fill="#cdd6f4">Agent input/output</text>
</g>
<!-- agent -->
<g id="node10" class="node">
<title>agent</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="3327.31,-161.2 3010.81,-161.2 3010.81,-54.8 3327.31,-54.8 3327.31,-161.2"/>
<text xml:space="preserve" text-anchor="middle" x="3169.06" y="-137.82" font-family="monospace" font-size="14.00" fill="#cdd6f4">Agent runner</text>
<text xml:space="preserve" text-anchor="middle" x="3169.06" y="-120.58" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="3169.06" y="-103.33" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht/agent/*</text>
<text xml:space="preserve" text-anchor="middle" x="3169.06" y="-86.08" font-family="monospace" font-size="14.00" fill="#cdd6f4">Claude SDK · OpenAI/Groq</text>
<text xml:space="preserve" text-anchor="middle" x="3169.06" y="-68.83" font-family="monospace" font-size="14.00" fill="#cdd6f4">@F frame refs · @T transcript refs</text>
</g>
<!-- gui&#45;&gt;agent -->
<g id="edge13" class="edge">
<title>gui&#45;&gt;agent</title>
<path fill="none" stroke="#585b70" d="M2870.68,-158.39C2911.74,-151.43 2956.79,-143.8 2999.13,-136.63"/>
<polygon fill="#585b70" stroke="#585b70" points="2999.64,-140.09 3008.91,-134.97 2998.47,-133.19 2999.64,-140.09"/>
<text xml:space="preserve" text-anchor="middle" x="2940.56" y="-156.17" font-family="monospace" font-size="14.00" fill="#a6adc8">@&#45;mentions</text>
</g>
<!-- agent&#45;&gt;store -->
<g id="edge14" class="edge">
<title>agent&#45;&gt;store</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M3010.47,-96.22C2939.05,-90.89 2870.31,-85.75 2870.31,-85.75 2870.31,-85.75 2578.56,-85.75 2578.56,-85.75 2578.56,-85.75 2486.47,-107.53 2400.08,-127.96"/>
<polygon fill="#585b70" stroke="#585b70" points="2399.43,-124.52 2390.51,-130.23 2401.05,-131.33 2399.43,-124.52"/>
<text xml:space="preserve" text-anchor="middle" x="2724.44" y="-89.7" font-family="monospace" font-size="14.00" fill="#a6adc8">thread.json</text>
</g>
<!-- store&#45;&gt;processor -->
<g id="edge9" class="edge">
<title>store&#45;&gt;processor</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M2113.07,-179.34C2026.78,-189.64 1931.56,-201 1931.56,-201 1931.56,-201 1689.31,-201 1689.31,-201 1689.31,-201 1625.9,-188.92 1560.2,-176.41"/>
<polygon fill="#585b70" stroke="#585b70" points="1561.18,-173.03 1550.7,-174.6 1559.87,-179.91 1561.18,-173.03"/>
</g>
<!-- store&#45;&gt;gui -->
<g id="edge12" class="edge">
<title>store&#45;&gt;gui</title>
<path fill="none" stroke="#585b70" d="M2388.95,-168.81C2444.64,-171.17 2509.32,-173.92 2566.86,-176.36"/>
<polygon fill="#585b70" stroke="#585b70" points="2566.62,-179.85 2576.76,-176.78 2566.92,-172.86 2566.62,-179.85"/>
<text xml:space="preserve" text-anchor="middle" x="2483.56" y="-179.33" font-family="monospace" font-size="14.00" fill="#a6adc8">files + watchers</text>
</g>
<!-- l_py -->
<g id="node12" class="node">
<title>l_py</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="233.5,-382.7 148,-382.7 148,-345.3 233.5,-345.3 233.5,-382.7"/>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-359.32" font-family="monospace" font-size="14.00" fill="#cdd6f4">Python</text>
</g>
<!-- l_rs -->
<g id="node13" class="node">
<title>l_rs</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="225.25,-455.7 156.25,-455.7 156.25,-418.3 225.25,-418.3 225.25,-455.7"/>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-432.32" font-family="monospace" font-size="14.00" fill="#cdd6f4">Rust</text>
</g>
<!-- l_io -->
<g id="node14" class="node">
<title>l_io</title>
<polygon fill="#1e2a3e" stroke="#89b4fa" points="339.26,-566.41 103.01,-566.41 42.24,-491.59 278.49,-491.59 339.26,-566.41"/>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-524.33" font-family="monospace" font-size="14.00" fill="#cdd6f4">I/O · network</text>
</g>
<!-- l_fs -->
<g id="node15" class="node">
<title>l_fs</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="250,-639.71 247,-643.71 226,-643.71 223,-639.71 131.5,-639.71 131.5,-602.29 250,-602.29 250,-639.71"/>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-616.33" font-family="monospace" font-size="14.00" fill="#cdd6f4">filesystem</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 21 KiB