session tweak, update docs

This commit is contained in:
2026-04-09 23:25:41 -03:00
parent 512d8ecef8
commit 6f8f260b05
8 changed files with 652 additions and 459 deletions

View File

@@ -1,4 +1,4 @@
// Client pipeline data flow — Phase 2
// Client pipeline data flow
// Sender machine (Wayland, VAAPI GPU)
digraph client_pipeline {
graph [fontname="monospace" bgcolor="#1e1e2e" rankdir=TB pad="0.6" splines=polyline]
@@ -9,43 +9,58 @@ digraph client_pipeline {
// 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"]
net [label="TCP :4444\nmcrndeb" shape=parallelogram fillcolor="#1e2a3e" color="#89b4fa"]
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"]
// Thread boundary
subgraph cluster_main {
label="main thread (tokio async)" fontcolor="#a6adc8" color="#45475a" fontname="monospace"
session_start [label="session_start\ncontrol message" fillcolor="#2d2038" color="#cba6f7"]
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"]
keepalive [label="keepalive / 5s" 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_pipeline {
label="capture-pipeline thread (blocking)" fontcolor="#a6adc8" color="#45475a" fontname="monospace"
subgraph cluster_subprocess {
label="Subprocess backend (default)" fontcolor="#a6adc8" color="#45475a" fontname="monospace"
capture [label="KmsCapture\n─────────────────\nffmpeg kmsgrab device\ndecoder: passthrough\noutput: DRM_PRIME frames\n+ hw_frames_ctx (DRM device)"
fillcolor="#1e2d3e" color="#89b4fa"]
ffmpeg_cli [label="ffmpeg subprocess\n─────────────\nkmsgrab → VAAPI h264\n+ PulseAudio inputs:\n amix(monitor, mic)\noutput: NUT pipe" fillcolor="#1e2d3e" color="#89b4fa"]
encoder [label="VaapiEncoder\n────────────────\n[lazy init on frame 1]\nbuffersrc ← hw_frames_ctx\nhwmap derive_device=vaapi\nscale_vaapi NV12 1920×1080\nh264_vaapi QP=20 GOP=30"
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"]
}
// Flow
drm -> capture [label="DMA-BUF\n(zero copy)"]
vaapi -> encoder [label="hw device\n(derived)" style=dashed color="#a6e3a1"]
capture -> encoder [label="AVFrame\nDRM_PRIME"]
encoder -> chan [label="EncodedPacket\n{ data, pts, keyframe, … }"]
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"]
mux -> keepalive [style=dashed]
keepalive -> write
mux -> write [label="WirePacket\nVideo | Audio"]
write -> net
mux -> shutdown [label="Ctrl-C or\nchannel closed"]
// Types note
types [label="EncodedPacket\n─────────────\ndata: Vec\<u8\> (H.264 NALUs)\npts / dts: i64\nkeyframe: bool\ntime_base: num/den"
types [label="WirePacket types\n─────────────\nVideo: H.264 NALUs + keyframe flag\nAudio: AAC frames\nControl: SessionStart/Stop/Keepalive"
shape=note fillcolor="#2a2a3e" color="#585b70"]
}

View File

@@ -4,182 +4,261 @@
<!-- Generated by graphviz version 14.1.2 (0)
-->
<!-- Title: client_pipeline Pages: 1 -->
<svg width="779pt" height="1234pt"
viewBox="0.00 0.00 779.00 1234.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 1191.25)">
<svg width="1291pt" height="1237pt"
viewBox="0.00 0.00 1291.00 1237.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(43.2 1194.19)">
<title>client_pipeline</title>
<polygon fill="#1e1e2e" stroke="none" points="-43.2,43.2 -43.2,-1191.25 735.58,-1191.25 735.58,43.2 -43.2,43.2"/>
<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="8,-132.56 8,-403.15 473,-403.15 473,-132.56 8,-132.56"/>
<text xml:space="preserve" text-anchor="middle" x="240.5" y="-385.85" font-family="monospace" font-size="14.00" fill="#a6adc8">main thread &#160;(tokio async)</text>
<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_pipeline</title>
<polygon fill="#1e1e2e" stroke="#45475a" points="110,-422.15 110,-966.77 492,-966.77 492,-422.15 110,-422.15"/>
<text xml:space="preserve" text-anchor="middle" x="301" y="-949.47" font-family="monospace" font-size="14.00" fill="#a6adc8">capture&#45;pipeline thread &#160;(blocking)</text>
<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="M383.75,-1116.79C383.75,-1120.36 349.8,-1123.26 308,-1123.26 266.2,-1123.26 232.25,-1120.36 232.25,-1116.79 232.25,-1116.79 232.25,-1058.53 232.25,-1058.53 232.25,-1054.96 266.2,-1052.06 308,-1052.06 349.8,-1052.06 383.75,-1054.96 383.75,-1058.53 383.75,-1058.53 383.75,-1116.79 383.75,-1116.79"/>
<path fill="none" stroke="#a6e3a1" d="M383.75,-1116.79C383.75,-1113.21 349.8,-1110.31 308,-1110.31 266.2,-1110.31 232.25,-1113.21 232.25,-1116.79"/>
<text xml:space="preserve" text-anchor="middle" x="308" y="-1091.61" font-family="monospace" font-size="14.00" fill="#cdd6f4">/dev/dri/card0</text>
<text xml:space="preserve" text-anchor="middle" x="308" y="-1074.36" font-family="monospace" font-size="14.00" fill="#cdd6f4">(KMS scanout)</text>
<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="node8" class="node">
<g id="node13" class="node">
<title>capture</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="441.5,-933.52 174.5,-933.52 174.5,-812.74 441.5,-812.74 441.5,-933.52"/>
<text xml:space="preserve" text-anchor="middle" x="308" y="-911.58" font-family="monospace" font-size="14.00" fill="#cdd6f4">KmsCapture</text>
<text xml:space="preserve" text-anchor="middle" x="308" y="-894.33" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────────</text>
<text xml:space="preserve" text-anchor="middle" x="308" y="-877.08" font-family="monospace" font-size="14.00" fill="#cdd6f4">ffmpeg kmsgrab device</text>
<text xml:space="preserve" text-anchor="middle" x="308" y="-859.83" font-family="monospace" font-size="14.00" fill="#cdd6f4">decoder: passthrough</text>
<text xml:space="preserve" text-anchor="middle" x="308" y="-842.58" font-family="monospace" font-size="14.00" fill="#cdd6f4">output: DRM_PRIME frames</text>
<text xml:space="preserve" text-anchor="middle" x="308" y="-825.33" font-family="monospace" font-size="14.00" fill="#cdd6f4">+ hw_frames_ctx (DRM device)</text>
<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="edge1" class="edge">
<g id="edge6" class="edge">
<title>drm&#45;&gt;capture</title>
<path fill="none" stroke="#585b70" d="M308,-1051.56C308,-1022.73 308,-980.97 308,-945.2"/>
<polygon fill="#585b70" stroke="#585b70" points="311.5,-945.34 308,-935.34 304.5,-945.34 311.5,-945.34"/>
<text xml:space="preserve" text-anchor="middle" x="353.38" y="-995.97" font-family="monospace" font-size="14.00" fill="#a6adc8">DMA&#45;BUF</text>
<text xml:space="preserve" text-anchor="middle" x="353.38" y="-978.72" font-family="monospace" font-size="14.00" fill="#a6adc8">(zero copy)</text>
<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="M692.38,-902.26C692.38,-905.83 649.18,-908.73 596,-908.73 542.82,-908.73 499.62,-905.83 499.62,-902.26 499.62,-902.26 499.62,-844 499.62,-844 499.62,-840.43 542.82,-837.53 596,-837.53 649.18,-837.53 692.38,-840.43 692.38,-844 692.38,-844 692.38,-902.26 692.38,-902.26"/>
<path fill="none" stroke="#a6e3a1" d="M692.38,-902.26C692.38,-898.68 649.18,-895.78 596,-895.78 542.82,-895.78 499.62,-898.68 499.62,-902.26"/>
<text xml:space="preserve" text-anchor="middle" x="596" y="-877.08" font-family="monospace" font-size="14.00" fill="#cdd6f4">/dev/dri/renderD128</text>
<text xml:space="preserve" text-anchor="middle" x="596" y="-859.83" font-family="monospace" font-size="14.00" fill="#cdd6f4">(VAAPI)</text>
<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="node9" class="node">
<g id="node14" class="node">
<title>encoder</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="433.25,-742.24 182.75,-742.24 182.75,-604.21 433.25,-604.21 433.25,-742.24"/>
<text xml:space="preserve" text-anchor="middle" x="308" y="-720.3" font-family="monospace" font-size="14.00" fill="#cdd6f4">VaapiEncoder</text>
<text xml:space="preserve" text-anchor="middle" x="308" y="-703.05" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────────</text>
<text xml:space="preserve" text-anchor="middle" x="308" y="-685.8" font-family="monospace" font-size="14.00" fill="#cdd6f4">[lazy init on frame 1]</text>
<text xml:space="preserve" text-anchor="middle" x="308" y="-668.55" font-family="monospace" font-size="14.00" fill="#cdd6f4">buffersrc ← hw_frames_ctx</text>
<text xml:space="preserve" text-anchor="middle" x="308" y="-651.3" font-family="monospace" font-size="14.00" fill="#cdd6f4">hwmap derive_device=vaapi</text>
<text xml:space="preserve" text-anchor="middle" x="308" y="-634.05" font-family="monospace" font-size="14.00" fill="#cdd6f4">scale_vaapi NV12 1920×1080</text>
<text xml:space="preserve" text-anchor="middle" x="308" y="-616.8" font-family="monospace" font-size="14.00" fill="#cdd6f4">h264_vaapi QP=20 GOP=30</text>
<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="edge2" class="edge">
<g id="edge7" class="edge">
<title>vaapi&#45;&gt;encoder</title>
<path fill="none" stroke="#a6e3a1" stroke-dasharray="5,2" d="M545.17,-837.2C509.67,-812.81 460.92,-779.31 416.85,-749.03"/>
<polygon fill="#a6e3a1" stroke="#a6e3a1" points="419,-746.25 408.78,-743.48 415.04,-752.02 419,-746.25"/>
<text xml:space="preserve" text-anchor="middle" x="514.92" y="-781.44" font-family="monospace" font-size="14.00" fill="#a6adc8">hw device</text>
<text xml:space="preserve" text-anchor="middle" x="514.92" y="-764.19" font-family="monospace" font-size="14.00" fill="#a6adc8">(derived)</text>
<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="node3" class="node">
<g id="node4" class="node">
<title>net</title>
<polygon fill="#1e2a3e" stroke="#89b4fa" points="384.3,-103.56 202.47,-103.56 155.7,0 337.53,0 384.3,-103.56"/>
<text xml:space="preserve" text-anchor="middle" x="270" y="-55.73" font-family="monospace" font-size="14.00" fill="#cdd6f4">TCP :4444</text>
<text xml:space="preserve" text-anchor="middle" x="270" y="-38.48" font-family="monospace" font-size="14.00" fill="#cdd6f4">mcrndeb</text>
<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="node4" class="node">
<g id="node6" class="node">
<title>session_start</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="175.88,-281.12 16.12,-281.12 16.12,-229.34 175.88,-229.34 175.88,-281.12"/>
<text xml:space="preserve" text-anchor="middle" x="96" y="-259.18" font-family="monospace" font-size="14.00" fill="#cdd6f4">session_start</text>
<text xml:space="preserve" text-anchor="middle" x="96" y="-241.93" font-family="monospace" font-size="14.00" fill="#cdd6f4">control message</text>
<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="node7" class="node">
<g id="node8" class="node">
<title>write</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="345.75,-192.34 194.25,-192.34 194.25,-140.56 345.75,-140.56 345.75,-192.34"/>
<text xml:space="preserve" text-anchor="middle" x="270" y="-170.4" font-family="monospace" font-size="14.00" fill="#cdd6f4">BufWriter</text>
<text xml:space="preserve" text-anchor="middle" x="270" y="-153.15" font-family="monospace" font-size="14.00" fill="#cdd6f4">write_packet()</text>
<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="edge6" class="edge">
<g id="edge12" class="edge">
<title>session_start&#45;&gt;write</title>
<path fill="none" stroke="#585b70" d="M146.8,-228.9C166.28,-219.18 188.7,-208 209.04,-197.85"/>
<polygon fill="#585b70" stroke="#585b70" points="210.4,-201.09 217.79,-193.49 207.28,-194.82 210.4,-201.09"/>
<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="node5" class="node">
<g id="node7" class="node">
<title>mux</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="446.88,-369.9 155.12,-369.9 155.12,-318.12 446.88,-318.12 446.88,-369.9"/>
<text xml:space="preserve" text-anchor="middle" x="301" y="-347.96" font-family="monospace" font-size="14.00" fill="#cdd6f4">select!</text>
<text xml:space="preserve" text-anchor="middle" x="301" y="-330.71" font-family="monospace" font-size="14.00" fill="#cdd6f4">pkt_rx &#160;| &#160;keepalive &#160;| &#160;ctrl&#45;c</text>
</g>
<!-- keepalive -->
<g id="node6" class="node">
<title>keepalive</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="345.75,-273.23 194.25,-273.23 194.25,-237.23 345.75,-237.23 345.75,-273.23"/>
<text xml:space="preserve" text-anchor="middle" x="270" y="-250.55" font-family="monospace" font-size="14.00" fill="#cdd6f4">keepalive / 5s</text>
</g>
<!-- mux&#45;&gt;keepalive -->
<g id="edge8" class="edge">
<title>mux&#45;&gt;keepalive</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M292.03,-317.91C288.26,-307.36 283.86,-295.04 279.99,-284.2"/>
<polygon fill="#585b70" stroke="#585b70" points="283.36,-283.24 276.7,-275 276.77,-285.59 283.36,-283.24"/>
<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="edge7" class="edge">
<g id="edge13" class="edge">
<title>mux&#45;&gt;write</title>
<path fill="none" stroke="#585b70" d="M322.96,-317.84C337.91,-300.7 355,-281.12 355,-281.12 355,-281.12 355,-229.34 355,-229.34 355,-229.34 334.59,-214.48 313.78,-199.32"/>
<polygon fill="#585b70" stroke="#585b70" points="315.94,-196.57 305.8,-193.51 311.82,-202.23 315.94,-196.57"/>
<text xml:space="preserve" text-anchor="middle" x="396.25" y="-250.55" font-family="monospace" font-size="14.00" fill="#a6adc8">WirePacket</text>
<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>
<!-- keepalive&#45;&gt;write -->
<g id="edge9" class="edge">
<title>keepalive&#45;&gt;write</title>
<path fill="none" stroke="#585b70" d="M270,-237.09C270,-227.6 270,-215.44 270,-203.94"/>
<polygon fill="#585b70" stroke="#585b70" points="273.5,-204.3 270,-194.3 266.5,-204.3 273.5,-204.3"/>
<!-- 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="edge10" class="edge">
<g id="edge14" class="edge">
<title>write&#45;&gt;net</title>
<path fill="none" stroke="#585b70" d="M270,-140.16C270,-132.59 270,-123.93 270,-115.07"/>
<polygon fill="#585b70" stroke="#585b70" points="273.5,-115.32 270,-105.32 266.5,-115.32 273.5,-115.32"/>
<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>
<!-- capture&#45;&gt;encoder -->
<g id="edge3" class="edge">
<title>capture&#45;&gt;encoder</title>
<path fill="none" stroke="#585b70" d="M308,-812.48C308,-794.03 308,-773.39 308,-753.81"/>
<polygon fill="#585b70" stroke="#585b70" points="311.5,-753.94 308,-743.94 304.5,-753.94 311.5,-753.94"/>
<text xml:space="preserve" text-anchor="middle" x="345.12" y="-781.44" font-family="monospace" font-size="14.00" fill="#a6adc8">AVFrame</text>
<text xml:space="preserve" text-anchor="middle" x="345.12" y="-764.19" font-family="monospace" font-size="14.00" fill="#a6adc8">DRM_PRIME</text>
<!-- 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="node10" class="node">
<g id="node12" class="node">
<title>chan</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="483.73,-533.71 193.05,-533.71 118.27,-430.15 408.95,-430.15 483.73,-533.71"/>
<text xml:space="preserve" text-anchor="middle" x="301" y="-485.88" font-family="monospace" font-size="14.00" fill="#cdd6f4">mpsc::channel(64)</text>
<text xml:space="preserve" text-anchor="middle" x="301" y="-468.63" font-family="monospace" font-size="14.00" fill="#cdd6f4">EncodedPacket</text>
<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>
<!-- encoder&#45;&gt;chan -->
<g id="edge4" class="edge">
<title>encoder&#45;&gt;chan</title>
<path fill="none" stroke="#585b70" d="M305.47,-603.89C304.77,-584.77 304,-564.09 303.31,-545.34"/>
<polygon fill="#585b70" stroke="#585b70" points="306.81,-545.31 302.94,-535.45 299.81,-545.57 306.81,-545.31"/>
<text xml:space="preserve" text-anchor="middle" x="411.96" y="-572.91" font-family="monospace" font-size="14.00" fill="#a6adc8">EncodedPacket</text>
<text xml:space="preserve" text-anchor="middle" x="411.96" y="-555.66" font-family="monospace" font-size="14.00" fill="#a6adc8">{ data, pts, keyframe, … }</text>
<!-- 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="edge5" class="edge">
<g id="edge10" class="edge">
<title>chan&#45;&gt;mux</title>
<path fill="none" stroke="#585b70" d="M301,-429.93C301,-413.88 301,-396.49 301,-381.64"/>
<polygon fill="#585b70" stroke="#585b70" points="304.5,-381.87 301,-371.87 297.5,-381.87 304.5,-381.87"/>
<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="node11" class="node">
<g id="node15" class="node">
<title>types</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="662.5,-1148.05 401.5,-1148.05 401.5,-1027.27 668.5,-1027.27 668.5,-1142.05 662.5,-1148.05"/>
<polyline fill="none" stroke="#585b70" points="662.5,-1148.05 662.5,-1142.05"/>
<polyline fill="none" stroke="#585b70" points="668.5,-1142.05 662.5,-1142.05"/>
<text xml:space="preserve" text-anchor="middle" x="535" y="-1126.11" font-family="monospace" font-size="14.00" fill="#cdd6f4">EncodedPacket</text>
<text xml:space="preserve" text-anchor="middle" x="535" y="-1108.86" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="535" y="-1091.61" font-family="monospace" font-size="14.00" fill="#cdd6f4">data: Vec&lt;u8&gt; &#160;(H.264 NALUs)</text>
<text xml:space="preserve" text-anchor="middle" x="535" y="-1074.36" font-family="monospace" font-size="14.00" fill="#cdd6f4">pts / dts: i64</text>
<text xml:space="preserve" text-anchor="middle" x="535" y="-1057.11" font-family="monospace" font-size="14.00" fill="#cdd6f4">keyframe: bool</text>
<text xml:space="preserve" text-anchor="middle" x="535" y="-1039.86" font-family="monospace" font-size="14.00" fill="#cdd6f4">time_base: num/den</text>
<polygon fill="#2a2a3e" stroke="#585b70" points="1107.5,-1143.43 780.5,-1143.43 780.5,-1039.9 1113.5,-1039.9 1113.5,-1137.43 1107.5,-1143.43"/>
<polyline fill="none" stroke="#585b70" points="1107.5,-1143.43 1107.5,-1137.43"/>
<polyline fill="none" stroke="#585b70" points="1113.5,-1137.43 1107.5,-1137.43"/>
<text xml:space="preserve" text-anchor="middle" x="947" y="-1121.49" font-family="monospace" font-size="14.00" fill="#cdd6f4">WirePacket types</text>
<text xml:space="preserve" text-anchor="middle" x="947" y="-1104.24" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="947" y="-1086.99" font-family="monospace" font-size="14.00" fill="#cdd6f4">Video: &#160;&#160;H.264 NALUs + keyframe flag</text>
<text xml:space="preserve" text-anchor="middle" x="947" y="-1069.74" font-family="monospace" font-size="14.00" fill="#cdd6f4">Audio: &#160;&#160;AAC frames</text>
<text xml:space="preserve" text-anchor="middle" x="947" y="-1052.49" font-family="monospace" font-size="14.00" fill="#cdd6f4">Control: SessionStart/Stop/Keepalive</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -1,5 +1,4 @@
// Cargo workspace crate dependency graph
// Phase 2: client capture + encode implemented; server is a stub
digraph crates {
graph [fontname="monospace" bgcolor="#1e1e2e" pad="0.5"]
node [fontname="monospace" fontcolor="#cdd6f4" style=filled shape=box
@@ -7,21 +6,23 @@ digraph crates {
edge [color="#585b70" fontname="monospace" fontcolor="#a6adc8"]
// External
ffmpeg_next [label="ffmpeg-next 8\n(ffmpeg-sys-next)" shape=component fillcolor="#1e3a2f" color="#a6e3a1"]
tokio [label="tokio 1\n(async runtime)" shape=component fillcolor="#1e2a3e" color="#89b4fa"]
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 (wire framing)\nframe.rs (Frame, AudioBuffer)\nlogging.rs"
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─────────────────────────────\ncapture.rs KMS/DRM → DRM_PRIME frames\nencoder.rs VAAPI H.264 (lazy init)\npipeline.rs capture→encode thread\nmain.rs TCP transport + keepalive"
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 (stub)\n counts packets, no decode yet"
fillcolor="#2d1e1e" color="#f38ba8"]
server [label="cht-server [receiver, mcrndeb]\n─────────────────────────────\nmain.rs TCP listener\n routes Video/Audio/Control\nsession.rs ffmpeg subprocess:\n fMP4 + UDP relay\n ADTS audio writer\n Scene relay (Unix socket)\n keyframe buffering"
fillcolor="#1e2d3e" color="#89b4fa"]
// Deps
client -> common
@@ -36,6 +37,8 @@ digraph crates {
client -> tokio
client -> tracing
client -> anyhow
client -> libc_crate
client -> nix_crate
server -> tokio
server -> tracing
@@ -45,7 +48,6 @@ digraph crates {
subgraph cluster_legend {
label="Legend" fontcolor="#a6adc8" color="#585b70" fontname="monospace"
l1 [label="implemented" fillcolor="#1e2d3e" color="#89b4fa" shape=box]
l2 [label="stub / planned" fillcolor="#2d1e1e" color="#f38ba8" shape=box]
l3 [label="external crate" fillcolor="#1e3a2f" color="#a6e3a1" shape=component]
}
}

View File

@@ -4,186 +4,219 @@
<!-- Generated by graphviz version 14.1.2 (0)
-->
<!-- Title: crates Pages: 1 -->
<svg width="1369pt" height="412pt"
viewBox="0.00 0.00 1369.00 412.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 375.75)">
<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,-375.75 1333.17,-375.75 1333.17,36 -36,36"/>
<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="829.17,-254.5 829.17,-331.75 1289.17,-331.75 1289.17,-254.5 829.17,-254.5"/>
<text xml:space="preserve" text-anchor="middle" x="1059.17" y="-314.45" font-family="monospace" font-size="14.00" fill="#a6adc8">Legend</text>
<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="196.7,-159.68 27.65,-159.68 27.65,-155.68 23.65,-155.68 23.65,-151.68 27.65,-151.68 27.65,-118.78 23.65,-118.78 23.65,-114.78 27.65,-114.78 27.65,-110.78 196.7,-110.78 196.7,-159.68"/>
<polyline fill="none" stroke="#a6e3a1" points="27.65,-155.68 31.65,-155.68 31.65,-151.68 27.65,-151.68"/>
<polyline fill="none" stroke="#a6e3a1" points="27.65,-118.78 31.65,-118.78 31.65,-114.78 27.65,-114.78"/>
<text xml:space="preserve" text-anchor="middle" x="112.17" y="-139.18" font-family="monospace" font-size="14.00" fill="#cdd6f4">ffmpeg&#45;next 8</text>
<text xml:space="preserve" text-anchor="middle" x="112.17" y="-121.93" font-family="monospace" font-size="14.00" fill="#cdd6f4">(ffmpeg&#45;sys&#45;next)</text>
<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="554.45,-48.9 401.9,-48.9 401.9,-44.9 397.9,-44.9 397.9,-40.9 401.9,-40.9 401.9,-8 397.9,-8 397.9,-4 401.9,-4 401.9,0 554.45,0 554.45,-48.9"/>
<polyline fill="none" stroke="#89b4fa" points="401.9,-44.9 405.9,-44.9 405.9,-40.9 401.9,-40.9"/>
<polyline fill="none" stroke="#89b4fa" points="401.9,-8 405.9,-8 405.9,-4 401.9,-4"/>
<text xml:space="preserve" text-anchor="middle" x="478.17" y="-28.4" font-family="monospace" font-size="14.00" fill="#cdd6f4">tokio 1</text>
<text xml:space="preserve" text-anchor="middle" x="478.17" y="-11.15" font-family="monospace" font-size="14.00" fill="#cdd6f4">(async runtime)</text>
<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="383.82,-42.45 206.52,-42.45 206.52,-38.45 202.52,-38.45 202.52,-34.45 206.52,-34.45 206.52,-14.45 202.52,-14.45 202.52,-10.45 206.52,-10.45 206.52,-6.45 383.82,-6.45 383.82,-42.45"/>
<polyline fill="none" stroke="#cba6f7" points="206.52,-38.45 210.52,-38.45 210.52,-34.45 206.52,-34.45"/>
<polyline fill="none" stroke="#cba6f7" points="206.52,-14.45 210.52,-14.45 210.52,-10.45 206.52,-10.45"/>
<text xml:space="preserve" text-anchor="middle" x="295.17" y="-19.78" font-family="monospace" font-size="14.00" fill="#cdd6f4">serde / serde_json</text>
<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="749.82,-48.9 572.52,-48.9 572.52,-44.9 568.52,-44.9 568.52,-40.9 572.52,-40.9 572.52,-8 568.52,-8 568.52,-4 572.52,-4 572.52,0 749.82,0 749.82,-48.9"/>
<polyline fill="none" stroke="#cba6f7" points="572.52,-44.9 576.52,-44.9 576.52,-40.9 572.52,-40.9"/>
<polyline fill="none" stroke="#cba6f7" points="572.52,-8 576.52,-8 576.52,-4 572.52,-4"/>
<text xml:space="preserve" text-anchor="middle" x="661.17" y="-28.4" font-family="monospace" font-size="14.00" fill="#cdd6f4">tracing</text>
<text xml:space="preserve" text-anchor="middle" x="661.17" y="-11.15" font-family="monospace" font-size="14.00" fill="#cdd6f4">tracing&#45;subscriber</text>
<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="188.32,-42.45 110.02,-42.45 110.02,-38.45 106.02,-38.45 106.02,-34.45 110.02,-34.45 110.02,-14.45 106.02,-14.45 106.02,-10.45 110.02,-10.45 110.02,-6.45 188.32,-6.45 188.32,-42.45"/>
<polyline fill="none" stroke="#cba6f7" points="110.02,-38.45 114.02,-38.45 114.02,-34.45 110.02,-34.45"/>
<polyline fill="none" stroke="#cba6f7" points="110.02,-14.45 114.02,-14.45 114.02,-10.45 110.02,-10.45"/>
<text xml:space="preserve" text-anchor="middle" x="149.17" y="-19.78" font-family="monospace" font-size="14.00" fill="#cdd6f4">anyhow</text>
<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="node6" class="node">
<g id="node8" class="node">
<title>common</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="592.7,-185.55 291.65,-185.55 291.65,-84.9 592.7,-84.9 592.7,-185.55"/>
<text xml:space="preserve" text-anchor="middle" x="442.17" y="-165.05" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht&#45;common</text>
<text xml:space="preserve" text-anchor="middle" x="442.17" y="-147.8" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="442.17" y="-130.55" font-family="monospace" font-size="14.00" fill="#cdd6f4">protocol.rs &#160;(wire framing)</text>
<text xml:space="preserve" text-anchor="middle" x="442.17" y="-113.3" font-family="monospace" font-size="14.00" fill="#cdd6f4">frame.rs &#160;&#160;&#160;&#160;(Frame, AudioBuffer)</text>
<text xml:space="preserve" text-anchor="middle" x="442.17" y="-96.05" font-family="monospace" font-size="14.00" fill="#cdd6f4">logging.rs</text>
<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="M458.59,-84.61C461.33,-76.33 464.12,-67.9 466.71,-60.1"/>
<polygon fill="#585b70" stroke="#585b70" points="470.02,-61.23 469.84,-50.64 463.37,-59.03 470.02,-61.23"/>
<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="M375.12,-84.61C358.64,-72.41 341.71,-59.89 327.7,-49.52"/>
<polygon fill="#585b70" stroke="#585b70" points="329.85,-46.76 319.73,-43.62 325.69,-52.38 329.85,-46.76"/>
<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="M542.38,-84.45C563.19,-74.12 584.45,-63.56 603.13,-54.28"/>
<polygon fill="#585b70" stroke="#585b70" points="604.47,-57.52 611.87,-49.94 601.35,-51.25 604.47,-57.52"/>
<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="M291.45,-84.91C260.03,-73.75 227.31,-61.46 197.17,-48.9 196.06,-48.44 194.94,-47.96 193.8,-47.47"/>
<polygon fill="#585b70" stroke="#585b70" points="195.49,-44.39 184.93,-43.48 192.62,-50.77 195.49,-44.39"/>
<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="node7" class="node">
<g id="node9" class="node">
<title>client</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="400.45,-339.45 49.9,-339.45 49.9,-221.55 400.45,-221.55 400.45,-339.45"/>
<text xml:space="preserve" text-anchor="middle" x="225.17" y="-318.95" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht&#45;client &#160;[sender, Wayland]</text>
<text xml:space="preserve" text-anchor="middle" x="225.17" y="-301.7" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────────────────────</text>
<text xml:space="preserve" text-anchor="middle" x="225.17" y="-284.45" font-family="monospace" font-size="14.00" fill="#cdd6f4">capture.rs &#160;&#160;KMS/DRM → DRM_PRIME frames</text>
<text xml:space="preserve" text-anchor="middle" x="225.17" y="-267.2" font-family="monospace" font-size="14.00" fill="#cdd6f4">encoder.rs &#160;&#160;VAAPI H.264 (lazy init)</text>
<text xml:space="preserve" text-anchor="middle" x="225.17" y="-249.95" font-family="monospace" font-size="14.00" fill="#cdd6f4">pipeline.rs &#160;capture→encode thread</text>
<text xml:space="preserve" text-anchor="middle" x="225.17" y="-232.7" font-family="monospace" font-size="14.00" fill="#cdd6f4">main.rs &#160;&#160;&#160;&#160;&#160;TCP transport + keepalive</text>
<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="M179.21,-221.22C165.11,-203.35 150.11,-184.32 137.88,-168.82"/>
<polygon fill="#585b70" stroke="#585b70" points="140.9,-167 131.96,-161.31 135.4,-171.33 140.9,-167"/>
<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="M227.82,-221.48C232.83,-178.38 246.37,-121.04 282.17,-84.9 286.38,-80.65 340.37,-64.43 390.61,-50"/>
<polygon fill="#585b70" stroke="#585b70" points="391.48,-53.39 400.13,-47.28 389.56,-46.66 391.48,-53.39"/>
<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="M400.86,-230.22C412.46,-227.22 423.97,-224.3 435.17,-221.55 508.91,-203.44 544.84,-235.33 602.17,-185.55 638.8,-153.74 652.71,-97.11 657.98,-60.43"/>
<polygon fill="#585b70" stroke="#585b70" points="661.42,-61.14 659.21,-50.78 654.47,-60.26 661.42,-61.14"/>
<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="M51.41,-221.15C38.48,-211.09 27.04,-199.32 18.17,-185.55 -6.06,-147.95 -6.06,-122.5 18.17,-84.9 35.92,-57.35 69.9,-42.42 98.8,-34.42"/>
<polygon fill="#585b70" stroke="#585b70" points="99.29,-37.9 108.12,-32.05 97.57,-31.12 99.29,-37.9"/>
<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="M313.43,-221.22C327.97,-211.63 343.01,-201.7 357.45,-192.16"/>
<polygon fill="#585b70" stroke="#585b70" points="359.17,-195.22 365.58,-186.79 355.31,-189.38 359.17,-195.22"/>
<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="node8" class="node">
<g id="node10" class="node">
<title>server</title>
<polygon fill="#2d1e1e" stroke="#f38ba8" points="819.82,-322.2 444.52,-322.2 444.52,-238.8 819.82,-238.8 819.82,-322.2"/>
<text xml:space="preserve" text-anchor="middle" x="632.17" y="-301.7" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht&#45;server &#160;[receiver, mcrn]</text>
<text xml:space="preserve" text-anchor="middle" x="632.17" y="-284.45" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────────────────────</text>
<text xml:space="preserve" text-anchor="middle" x="632.17" y="-267.2" font-family="monospace" font-size="14.00" fill="#cdd6f4">main.rs &#160;&#160;&#160;&#160;&#160;TCP listener (stub)</text>
<text xml:space="preserve" text-anchor="middle" x="632.17" y="-249.95" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;counts packets, no decode yet</text>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="1119.22,-417.07 760.42,-417.07 760.42,-247.43 1119.22,-247.43 1119.22,-417.07"/>
<text xml:space="preserve" text-anchor="middle" x="939.82" y="-396.57" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht&#45;server &#160;[receiver, mcrndeb]</text>
<text xml:space="preserve" text-anchor="middle" x="939.82" y="-379.32" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────────────────────</text>
<text xml:space="preserve" text-anchor="middle" x="939.82" y="-362.07" font-family="monospace" font-size="14.00" fill="#cdd6f4">main.rs &#160;&#160;&#160;&#160;&#160;&#160;TCP listener</text>
<text xml:space="preserve" text-anchor="middle" x="939.82" y="-344.82" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;routes Video/Audio/Control</text>
<text xml:space="preserve" text-anchor="middle" x="939.82" y="-327.57" font-family="monospace" font-size="14.00" fill="#cdd6f4">session.rs &#160;&#160;&#160;ffmpeg subprocess:</text>
<text xml:space="preserve" text-anchor="middle" x="939.82" y="-310.32" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;fMP4 + UDP relay</text>
<text xml:space="preserve" text-anchor="middle" x="939.82" y="-293.07" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;ADTS audio writer</text>
<text xml:space="preserve" text-anchor="middle" x="939.82" y="-275.82" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Scene relay (Unix socket)</text>
<text xml:space="preserve" text-anchor="middle" x="939.82" y="-258.57" font-family="monospace" font-size="14.00" fill="#cdd6f4"> &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;keyframe buffering</text>
</g>
<!-- server&#45;&gt;tokio -->
<g id="edge11" class="edge">
<g id="edge13" class="edge">
<title>server&#45;&gt;tokio</title>
<path fill="none" stroke="#585b70" d="M636.34,-238.44C638.44,-195.9 635.27,-129.51 602.17,-84.9 592.46,-71.81 579.12,-61.48 564.76,-53.36"/>
<polygon fill="#585b70" stroke="#585b70" points="566.52,-50.33 556.04,-48.79 563.27,-56.53 566.52,-50.33"/>
<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="edge12" class="edge">
<g id="edge14" class="edge">
<title>server&#45;&gt;tracing</title>
<path fill="none" stroke="#585b70" d="M654.37,-238.63C661.92,-222.57 669.39,-203.67 673.17,-185.55 682.02,-143.14 675.7,-93.22 669.37,-60.54"/>
<polygon fill="#585b70" stroke="#585b70" points="672.81,-59.89 667.37,-50.8 665.96,-61.3 672.81,-59.89"/>
<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="edge13" class="edge">
<g id="edge15" class="edge">
<title>server&#45;&gt;anyhow</title>
<path fill="none" stroke="#585b70" d="M474.46,-238.36C383.66,-214.48 286.57,-188.52 282.17,-185.55 235.69,-154.22 242.52,-127.57 206.17,-84.9 196.39,-73.42 185,-61.31 174.98,-51.03"/>
<polygon fill="#585b70" stroke="#585b70" points="177.52,-48.63 168.01,-43.97 172.54,-53.55 177.52,-48.63"/>
<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="M577.73,-238.44C558.9,-224.24 537.45,-208.07 517.26,-192.85"/>
<polygon fill="#585b70" stroke="#585b70" points="519.39,-190.07 509.3,-186.84 515.17,-195.66 519.39,-190.07"/>
<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="node9" class="node">
<g id="node11" class="node">
<title>l1</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="956.95,-298.5 837.4,-298.5 837.4,-262.5 956.95,-262.5 956.95,-298.5"/>
<text xml:space="preserve" text-anchor="middle" x="897.17" y="-275.82" font-family="monospace" font-size="14.00" fill="#cdd6f4">implemented</text>
</g>
<!-- l2 -->
<g id="node10" class="node">
<title>l2</title>
<polygon fill="#2d1e1e" stroke="#f38ba8" points="1119.32,-298.5 975.02,-298.5 975.02,-262.5 1119.32,-262.5 1119.32,-298.5"/>
<text xml:space="preserve" text-anchor="middle" x="1047.17" y="-275.82" font-family="monospace" font-size="14.00" fill="#cdd6f4">stub / planned</text>
<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="node11" class="node">
<g id="node12" class="node">
<title>l3</title>
<polygon fill="#1e3a2f" stroke="#a6e3a1" points="1281.32,-298.5 1137.02,-298.5 1137.02,-294.5 1133.02,-294.5 1133.02,-290.5 1137.02,-290.5 1137.02,-270.5 1133.02,-270.5 1133.02,-266.5 1137.02,-266.5 1137.02,-262.5 1281.32,-262.5 1281.32,-298.5"/>
<polyline fill="none" stroke="#a6e3a1" points="1137.02,-294.5 1141.02,-294.5 1141.02,-290.5 1137.02,-290.5"/>
<polyline fill="none" stroke="#a6e3a1" points="1137.02,-270.5 1141.02,-270.5 1141.02,-266.5 1137.02,-266.5"/>
<text xml:space="preserve" text-anchor="middle" x="1209.17" y="-275.82" font-family="monospace" font-size="14.00" fill="#cdd6f4">external crate</text>
<polygon fill="#1e3a2f" stroke="#a6e3a1" points="1418.97,-350.25 1274.67,-350.25 1274.67,-346.25 1270.67,-346.25 1270.67,-342.25 1274.67,-342.25 1274.67,-322.25 1270.67,-322.25 1270.67,-318.25 1274.67,-318.25 1274.67,-314.25 1418.97,-314.25 1418.97,-350.25"/>
<polyline fill="none" stroke="#a6e3a1" points="1274.67,-346.25 1278.67,-346.25 1278.67,-342.25 1274.67,-342.25"/>
<polyline fill="none" stroke="#a6e3a1" points="1274.67,-322.25 1278.67,-322.25 1278.67,-318.25 1274.67,-318.25"/>
<text xml:space="preserve" text-anchor="middle" x="1346.82" y="-327.57" font-family="monospace" font-size="14.00" fill="#cdd6f4">external crate</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -117,18 +117,18 @@
<h1>Media Transport</h1>
<div class="section">Workspace</div>
<a href="#" data-svg="crates.svg" data-title="Crate Dependency Graph" data-desc="Workspace members, external deps, what's implemented vs stubbed">
Crate graph <span class="phase-badge">phase 2</span>
<a href="#" data-svg="crates.svg" data-title="Crate Dependency Graph" data-desc="Workspace members and external deps">
Crate graph
</a>
<div class="section">Client (sender)</div>
<a href="#" data-svg="client-pipeline.svg" data-title="Client Pipeline" data-desc="KMS capture → VAAPI encode → TCP transport">
Pipeline <span class="phase-badge">phase 2</span>
<a href="#" data-svg="client-pipeline.svg" data-title="Client Pipeline" data-desc="KMS capture + PulseAudio → VAAPI H.264 + AAC → TCP transport">
Pipeline
</a>
<div class="section">Server (receiver)</div>
<a href="#" data-svg="server-pipeline.svg" data-title="Server Pipeline" data-desc="Current stub + planned: NVDEC, scene detection, IPC, frame buffer">
Pipeline <span class="phase-badge">phase 2 stub</span>
<a href="#" data-svg="server-pipeline.svg" data-title="Server Pipeline" data-desc="fMP4 recording, UDP live relay, scene detection (UDS → Python), audio extraction">
Pipeline
</a>
</nav>

View File

@@ -1,54 +1,71 @@
// Server pipeline — Phase 2 (stub) + planned architecture
// Receiver machine (X11, RTX 3080, NVDEC)
// Server pipeline — current implementation
// Receiver machine (mcrndeb: X11, RTX 3080, NVDEC)
digraph server_pipeline {
graph [fontname="monospace" bgcolor="#1e1e2e" rankdir=TB pad="0.6" splines=polyline]
node [fontname="monospace" fontcolor="#cdd6f4" style=filled shape=box
fillcolor="#313244" color="#585b70" margin="0.25,0.12"]
edge [color="#585b70" fontname="monospace" fontcolor="#a6adc8"]
net [label="TCP :4444" shape=parallelogram fillcolor="#1e2a3e" color="#89b4fa"]
python [label="Python app\n(stream/manager.py)" shape=parallelogram fillcolor="#2a2a3e" color="#cba6f7"]
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_implemented {
label="Implemented (Phase 2)" fontcolor="#a6e3a1" color="#a6e3a1" fontname="monospace"
subgraph cluster_rust {
label="cht-server (Rust)" fontcolor="#a6e3a1" color="#a6e3a1" fontname="monospace"
listener [label="Listener\n─────────────\nTCP accept loop\nspawns task per client\nreads WirePacket headers\ncounts video/audio pkts\nlogs keyframes + ts"
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_planned {
label="Planned" fontcolor="#f38ba8" color="#f38ba8" fontname="monospace" style=dashed
subgraph cluster_python {
label="Python (cht app)" fontcolor="#cba6f7" color="#cba6f7" fontname="monospace"
decoder [label="Decoder (Phase 3)\n─────────────\nNVDEC H.264 → NV12\nGPU frames" fillcolor="#2d1e1e" color="#f38ba8"]
scene [label="Scene Detector (Phase 3)\n─────────────\nffmpeg select filter\nin-process (no subprocess)\nJPEG → frames/\nframes/index.json" fillcolor="#2d1e1e" color="#f38ba8"]
audio [label="Audio Extractor (Phase 4)\n─────────────\nAAC decode\nWAV chunks → audio/" fillcolor="#2d1e1e" color="#f38ba8"]
writer [label="Segment Writer (Phase 3)\n─────────────\nfMP4 segments → stream/\nkeyframe boundaries" fillcolor="#2d1e1e" color="#f38ba8"]
framebuf [label="Frame Buffer (Phase 6)\n─────────────\nGPU ring buffer ~300 frames\nscrub: GPU→CPU on demand\n→ /dev/shm/cht_scrub_frame" fillcolor="#2d1e1e" color="#f38ba8"]
ipc [label="IPC Server (Phase 5)\n─────────────\nUnix socket JSON-lines\ncommands: start/stop/get_frame\nevents: frame_detected/audio_chunk/…" fillcolor="#2d1e1e" color="#f38ba8"]
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 — implemented
// 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 — planned
listener -> decoder [style=dashed label="H.264 payload"]
decoder -> scene [style=dashed label="NV12 frame"]
decoder -> writer [style=dashed label="encoded pkt"]
decoder -> framebuf [style=dashed label="GPU frame"]
decoder -> audio [style=dashed label="audio pkt"]
scene -> ipc [style=dashed label="frame_detected"]
audio -> ipc [style=dashed label="audio_chunk"]
writer -> ipc [style=dashed label="segment_completed"]
ipc -> python [style=dashed label="JSON-lines\n(Unix socket)"]
// Flow — Python scene detection
scene_relay -> scene_ffmpeg [label="raw H.264\n(Unix socket)" color="#a6e3a1"]
// Outputs
frames_dir [label="frames/\nindex.json + *.jpg" shape=folder fillcolor="#2a2a3e" color="#585b70"]
audio_dir [label="audio/\n*.wav chunks" shape=folder fillcolor="#2a2a3e" color="#585b70"]
stream_dir [label="stream/\n*.mp4 segments" shape=folder fillcolor="#2a2a3e" color="#585b70"]
shm [label="/dev/shm/cht_scrub_frame\nraw RGBA pixels" shape=folder fillcolor="#2a2a3e" color="#585b70"]
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"]
scene -> frames_dir [style=dashed]
audio -> audio_dir [style=dashed]
writer -> stream_dir [style=dashed]
framebuf -> shm [style=dashed label="get_frame cmd"]
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]
}

View File

@@ -4,227 +4,260 @@
<!-- Generated by graphviz version 14.1.2 (0)
-->
<!-- Title: server_pipeline Pages: 1 -->
<svg width="1933pt" height="1038pt"
viewBox="0.00 0.00 1933.00 1038.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 994.44)">
<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>server_pipeline</title>
<polygon fill="#1e1e2e" stroke="none" points="-43.2,43.2 -43.2,-994.44 1890.2,-994.44 1890.2,43.2 -43.2,43.2"/>
<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_implemented</title>
<polygon fill="#1e1e2e" stroke="#a6e3a1" points="417,-659.65 417,-838.93 667,-838.93 667,-659.65 417,-659.65"/>
<text xml:space="preserve" text-anchor="middle" x="542" y="-821.63" font-family="monospace" font-size="14.00" fill="#a6e3a1">Implemented (Phase 2)</text>
<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_planned</title>
<polygon fill="#1e1e2e" stroke="#f38ba8" stroke-dasharray="5,2" points="8,-166.06 8,-624.4 1080,-624.4 1080,-166.06 8,-166.06"/>
<text xml:space="preserve" text-anchor="middle" x="544" y="-607.1" font-family="monospace" font-size="14.00" fill="#f38ba8">Planned</text>
<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="656.3,-951.24 474.47,-951.24 427.7,-882.18 609.53,-882.18 656.3,-951.24"/>
<text xml:space="preserve" text-anchor="middle" x="542" y="-912.03" font-family="monospace" font-size="14.00" fill="#cdd6f4">TCP :4444</text>
<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="659,-805.68 425,-805.68 425,-667.65 659,-667.65 659,-805.68"/>
<text xml:space="preserve" text-anchor="middle" x="542" y="-783.74" font-family="monospace" font-size="14.00" fill="#cdd6f4">Listener</text>
<text xml:space="preserve" text-anchor="middle" x="542" y="-766.49" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="542" y="-749.24" font-family="monospace" font-size="14.00" fill="#cdd6f4">TCP accept loop</text>
<text xml:space="preserve" text-anchor="middle" x="542" y="-731.99" font-family="monospace" font-size="14.00" fill="#cdd6f4">spawns task per client</text>
<text xml:space="preserve" text-anchor="middle" x="542" y="-714.74" font-family="monospace" font-size="14.00" fill="#cdd6f4">reads WirePacket headers</text>
<text xml:space="preserve" text-anchor="middle" x="542" y="-697.49" font-family="monospace" font-size="14.00" fill="#cdd6f4">counts video/audio pkts</text>
<text xml:space="preserve" text-anchor="middle" x="542" y="-680.24" font-family="monospace" font-size="14.00" fill="#cdd6f4">logs keyframes + ts</text>
<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="M542,-881.8C542,-863.5 542,-840.08 542,-817.47"/>
<polygon fill="#585b70" stroke="#585b70" points="545.5,-817.61 542,-807.61 538.5,-817.61 545.5,-817.61"/>
<text xml:space="preserve" text-anchor="middle" x="583.25" y="-850.88" font-family="monospace" font-size="14.00" fill="#a6adc8">WirePacket</text>
<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="609.83,-103.56 291.94,-103.56 210.17,0 528.06,0 609.83,-103.56"/>
<text xml:space="preserve" text-anchor="middle" x="410" y="-55.73" font-family="monospace" font-size="14.00" fill="#cdd6f4">Python app</text>
<text xml:space="preserve" text-anchor="middle" x="410" y="-38.48" font-family="monospace" font-size="14.00" fill="#cdd6f4">(stream/manager.py)</text>
<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>
<!-- decoder -->
<!-- ffmpeg_rec -->
<g id="node4" class="node">
<title>decoder</title>
<polygon fill="#2d1e1e" stroke="#f38ba8" points="634.25,-591.15 449.75,-591.15 449.75,-504.87 634.25,-504.87 634.25,-591.15"/>
<text xml:space="preserve" text-anchor="middle" x="542" y="-569.21" font-family="monospace" font-size="14.00" fill="#cdd6f4">Decoder &#160;(Phase 3)</text>
<text xml:space="preserve" text-anchor="middle" x="542" y="-551.96" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="542" y="-534.71" font-family="monospace" font-size="14.00" fill="#cdd6f4">NVDEC H.264 → NV12</text>
<text xml:space="preserve" text-anchor="middle" x="542" y="-517.46" font-family="monospace" font-size="14.00" fill="#cdd6f4">GPU frames</text>
<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;decoder -->
<!-- listener&#45;&gt;ffmpeg_rec -->
<g id="edge2" class="edge">
<title>listener&#45;&gt;decoder</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M542,-667.22C542,-646 542,-622.93 542,-602.84"/>
<polygon fill="#585b70" stroke="#585b70" points="545.5,-602.91 542,-592.91 538.5,-602.91 545.5,-602.91"/>
<text xml:space="preserve" text-anchor="middle" x="595.62" y="-636.35" font-family="monospace" font-size="14.00" fill="#a6adc8">H.264 payload</text>
<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 -->
<!-- scene_relay -->
<g id="node5" class="node">
<title>scene</title>
<polygon fill="#2d1e1e" stroke="#f38ba8" points="266.25,-451.62 15.75,-451.62 15.75,-330.84 266.25,-330.84 266.25,-451.62"/>
<text xml:space="preserve" text-anchor="middle" x="141" y="-429.68" font-family="monospace" font-size="14.00" fill="#cdd6f4">Scene Detector &#160;(Phase 3)</text>
<text xml:space="preserve" text-anchor="middle" x="141" y="-412.43" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="141" y="-395.18" font-family="monospace" font-size="14.00" fill="#cdd6f4">ffmpeg select filter</text>
<text xml:space="preserve" text-anchor="middle" x="141" y="-377.93" font-family="monospace" font-size="14.00" fill="#cdd6f4">in&#45;process (no subprocess)</text>
<text xml:space="preserve" text-anchor="middle" x="141" y="-360.68" font-family="monospace" font-size="14.00" fill="#cdd6f4">JPEG → frames/</text>
<text xml:space="preserve" text-anchor="middle" x="141" y="-343.43" font-family="monospace" font-size="14.00" fill="#cdd6f4">frames/index.json</text>
<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>
<!-- decoder&#45;&gt;scene -->
<!-- listener&#45;&gt;scene_relay -->
<g id="edge3" class="edge">
<title>decoder&#45;&gt;scene</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M449.34,-513.78C384.87,-490.66 306.43,-462.53 277.46,-451.7"/>
<polygon fill="#585b70" stroke="#585b70" points="278.72,-448.43 268.13,-448.08 276.19,-454.96 278.72,-448.43"/>
<text xml:space="preserve" text-anchor="middle" x="410.88" y="-473.57" font-family="monospace" font-size="14.00" fill="#a6adc8">NV12 frame</text>
<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 -->
<!-- audio_writer -->
<g id="node6" class="node">
<title>audio</title>
<polygon fill="#2d1e1e" stroke="#f38ba8" points="535.25,-434.37 284.75,-434.37 284.75,-348.09 535.25,-348.09 535.25,-434.37"/>
<text xml:space="preserve" text-anchor="middle" x="410" y="-412.43" font-family="monospace" font-size="14.00" fill="#cdd6f4">Audio Extractor &#160;(Phase 4)</text>
<text xml:space="preserve" text-anchor="middle" x="410" y="-395.18" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="410" y="-377.93" font-family="monospace" font-size="14.00" fill="#cdd6f4">AAC decode</text>
<text xml:space="preserve" text-anchor="middle" x="410" y="-360.68" font-family="monospace" font-size="14.00" fill="#cdd6f4">WAV chunks → audio/</text>
<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>
<!-- decoder&#45;&gt;audio -->
<g id="edge6" class="edge">
<title>decoder&#45;&gt;audio</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M505.93,-504.72C489.77,-485.77 470.58,-463.26 453.62,-443.38"/>
<polygon fill="#585b70" stroke="#585b70" points="456.39,-441.23 447.24,-435.89 451.06,-445.77 456.39,-441.23"/>
<text xml:space="preserve" text-anchor="middle" x="524.95" y="-473.57" font-family="monospace" font-size="14.00" fill="#a6adc8">audio pkt</text>
</g>
<!-- writer -->
<g id="node7" class="node">
<title>writer</title>
<polygon fill="#2d1e1e" stroke="#f38ba8" points="795.12,-434.37 552.88,-434.37 552.88,-348.09 795.12,-348.09 795.12,-434.37"/>
<text xml:space="preserve" text-anchor="middle" x="674" y="-412.43" font-family="monospace" font-size="14.00" fill="#cdd6f4">Segment Writer &#160;(Phase 3)</text>
<text xml:space="preserve" text-anchor="middle" x="674" y="-395.18" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="674" y="-377.93" font-family="monospace" font-size="14.00" fill="#cdd6f4">fMP4 segments → stream/</text>
<text xml:space="preserve" text-anchor="middle" x="674" y="-360.68" font-family="monospace" font-size="14.00" fill="#cdd6f4">keyframe boundaries</text>
</g>
<!-- decoder&#45;&gt;writer -->
<!-- listener&#45;&gt;audio_writer -->
<g id="edge4" class="edge">
<title>decoder&#45;&gt;writer</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M578.07,-504.72C594.23,-485.77 613.42,-463.26 630.38,-443.38"/>
<polygon fill="#585b70" stroke="#585b70" points="632.94,-445.77 636.76,-435.89 627.61,-441.23 632.94,-445.77"/>
<text xml:space="preserve" text-anchor="middle" x="653.38" y="-473.57" font-family="monospace" font-size="14.00" fill="#a6adc8">encoded pkt</text>
<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>
<!-- framebuf -->
<g id="node8" class="node">
<title>framebuf</title>
<polygon fill="#2d1e1e" stroke="#f38ba8" points="1072.38,-442.99 813.62,-442.99 813.62,-339.46 1072.38,-339.46 1072.38,-442.99"/>
<text xml:space="preserve" text-anchor="middle" x="943" y="-421.05" font-family="monospace" font-size="14.00" fill="#cdd6f4">Frame Buffer &#160;(Phase 6)</text>
<text xml:space="preserve" text-anchor="middle" x="943" y="-403.8" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="943" y="-386.55" font-family="monospace" font-size="14.00" fill="#cdd6f4">GPU ring buffer ~300 frames</text>
<text xml:space="preserve" text-anchor="middle" x="943" y="-369.3" font-family="monospace" font-size="14.00" fill="#cdd6f4">scrub: GPU→CPU on demand</text>
<text xml:space="preserve" text-anchor="middle" x="943" y="-352.05" font-family="monospace" font-size="14.00" fill="#cdd6f4">→ /dev/shm/cht_scrub_frame</text>
<!-- 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>
<!-- decoder&#45;&gt;framebuf -->
<!-- listener&#45;&gt;active_session -->
<g id="edge5" class="edge">
<title>decoder&#45;&gt;framebuf</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M634.74,-513.24C710.11,-485.8 804,-451.62 804,-451.62 804,-451.62 807.23,-450.24 812.71,-447.9"/>
<polygon fill="#585b70" stroke="#585b70" points="813.82,-451.23 821.64,-444.08 811.07,-444.79 813.82,-451.23"/>
<text xml:space="preserve" text-anchor="middle" x="791.01" y="-473.57" font-family="monospace" font-size="14.00" fill="#a6adc8">GPU frame</text>
<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>
<!-- ipc -->
<g id="node9" class="node">
<title>ipc</title>
<polygon fill="#2d1e1e" stroke="#f38ba8" points="576.5,-277.59 243.5,-277.59 243.5,-174.06 576.5,-174.06 576.5,-277.59"/>
<text xml:space="preserve" text-anchor="middle" x="410" y="-255.65" font-family="monospace" font-size="14.00" fill="#cdd6f4">IPC Server &#160;(Phase 5)</text>
<text xml:space="preserve" text-anchor="middle" x="410" y="-238.4" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="410" y="-221.15" font-family="monospace" font-size="14.00" fill="#cdd6f4">Unix socket JSON&#45;lines</text>
<text xml:space="preserve" text-anchor="middle" x="410" y="-203.9" font-family="monospace" font-size="14.00" fill="#cdd6f4">commands: start/stop/get_frame</text>
<text xml:space="preserve" text-anchor="middle" x="410" y="-186.65" font-family="monospace" font-size="14.00" fill="#cdd6f4">events: frame_detected/audio_chunk/…</text>
<!-- 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>
<!-- scene&#45;&gt;ipc -->
<!-- ffmpeg_rec&#45;&gt;fmp4 -->
<g id="edge7" class="edge">
<title>scene&#45;&gt;ipc</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M235.26,-330.56C265.11,-311.66 290.5,-295.59 290.5,-295.59 290.5,-295.59 298.93,-290.74 311.31,-283.61"/>
<polygon fill="#585b70" stroke="#585b70" points="312.73,-286.84 319.65,-278.81 309.24,-280.77 312.73,-286.84"/>
<text xml:space="preserve" text-anchor="middle" x="348.25" y="-299.54" font-family="monospace" font-size="14.00" fill="#a6adc8">frame_detected</text>
<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>
<!-- frames_dir -->
<g id="node10" class="node">
<title>frames_dir</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="1272.25,-251.72 1269.25,-255.72 1248.25,-255.72 1245.25,-251.72 1087.75,-251.72 1087.75,-199.93 1272.25,-199.93 1272.25,-251.72"/>
<text xml:space="preserve" text-anchor="middle" x="1180" y="-229.77" font-family="monospace" font-size="14.00" fill="#cdd6f4">frames/</text>
<text xml:space="preserve" text-anchor="middle" x="1180" y="-212.52" font-family="monospace" font-size="14.00" fill="#cdd6f4">index.json + *.jpg</text>
<!-- 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>
<!-- scene&#45;&gt;frames_dir -->
<g id="edge11" class="edge">
<title>scene&#45;&gt;frames_dir</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M266.72,-334.92C272.54,-332.36 276,-330.84 276,-330.84 276,-330.84 686,-312.84 686,-312.84 686,-312.84 1084,-277.59 1084,-277.59 1084,-277.59 1101.72,-268.22 1121.94,-257.53"/>
<polygon fill="#585b70" stroke="#585b70" points="1123.46,-260.68 1130.66,-252.92 1120.19,-254.5 1123.46,-260.68"/>
</g>
<!-- audio&#45;&gt;ipc -->
<!-- ffmpeg_rec&#45;&gt;udp_live -->
<g id="edge8" class="edge">
<title>audio&#45;&gt;ipc</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M410,-347.72C410,-329.86 410,-308.72 410,-289.15"/>
<polygon fill="#585b70" stroke="#585b70" points="413.5,-289.36 410,-279.36 406.5,-289.36 413.5,-289.36"/>
<text xml:space="preserve" text-anchor="middle" x="455.38" y="-299.54" font-family="monospace" font-size="14.00" fill="#a6adc8">audio_chunk</text>
<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="node11" class="node">
<g id="node15" class="node">
<title>audio_dir</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="1425.5,-251.72 1422.5,-255.72 1401.5,-255.72 1398.5,-251.72 1290.5,-251.72 1290.5,-199.93 1425.5,-199.93 1425.5,-251.72"/>
<text xml:space="preserve" text-anchor="middle" x="1358" y="-229.77" font-family="monospace" font-size="14.00" fill="#cdd6f4">audio/</text>
<text xml:space="preserve" text-anchor="middle" x="1358" y="-212.52" font-family="monospace" font-size="14.00" fill="#cdd6f4">*.wav chunks</text>
<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&#45;&gt;audio_dir -->
<g id="edge12" class="edge">
<title>audio&#45;&gt;audio_dir</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M505.96,-347.7C527.44,-338.18 544,-330.84 544,-330.84 544,-330.84 1194,-312.84 1194,-312.84 1194,-312.84 1281,-277.59 1281,-277.59 1281,-277.59 1294.45,-268.72 1310.11,-258.4"/>
<polygon fill="#585b70" stroke="#585b70" points="1311.81,-261.47 1318.24,-253.04 1307.96,-255.62 1311.81,-261.47"/>
<!-- 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>
<!-- writer&#45;&gt;ipc -->
<g id="edge9" class="edge">
<title>writer&#45;&gt;ipc</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M605.33,-347.72C573.77,-328.19 535.88,-304.74 501.9,-283.71"/>
<polygon fill="#585b70" stroke="#585b70" points="504.06,-280.93 493.72,-278.64 500.38,-286.88 504.06,-280.93"/>
<text xml:space="preserve" text-anchor="middle" x="612.12" y="-299.54" font-family="monospace" font-size="14.00" fill="#a6adc8">segment_completed</text>
<!-- 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>
<!-- stream_dir -->
<g id="node12" class="node">
<title>stream_dir</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="1594.75,-251.72 1591.75,-255.72 1570.75,-255.72 1567.75,-251.72 1443.25,-251.72 1443.25,-199.93 1594.75,-199.93 1594.75,-251.72"/>
<text xml:space="preserve" text-anchor="middle" x="1519" y="-229.77" font-family="monospace" font-size="14.00" fill="#cdd6f4">stream/</text>
<text xml:space="preserve" text-anchor="middle" x="1519" y="-212.52" font-family="monospace" font-size="14.00" fill="#cdd6f4">*.mp4 segments</text>
</g>
<!-- writer&#45;&gt;stream_dir -->
<!-- aac_file&#45;&gt;audio_extract -->
<g id="edge13" class="edge">
<title>writer&#45;&gt;stream_dir</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M767.81,-347.7C788.81,-338.18 805,-330.84 805,-330.84 805,-330.84 1372,-312.84 1372,-312.84 1372,-312.84 1435,-277.59 1435,-277.59 1435,-277.59 1449.95,-268.55 1467.24,-258.11"/>
<polygon fill="#585b70" stroke="#585b70" points="1468.96,-261.16 1475.7,-252.99 1465.34,-255.17 1468.96,-261.16"/>
<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>
<!-- shm -->
<g id="node13" class="node">
<title>shm</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="1847,-251.72 1844,-255.72 1823,-255.72 1820,-251.72 1613,-251.72 1613,-199.93 1847,-199.93 1847,-251.72"/>
<text xml:space="preserve" text-anchor="middle" x="1730" y="-229.77" font-family="monospace" font-size="14.00" fill="#cdd6f4">/dev/shm/cht_scrub_frame</text>
<text xml:space="preserve" text-anchor="middle" x="1730" y="-212.52" font-family="monospace" font-size="14.00" fill="#cdd6f4">raw RGBA pixels</text>
</g>
<!-- framebuf&#45;&gt;shm -->
<g id="edge14" class="edge">
<title>framebuf&#45;&gt;shm</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M1072.84,-373.2C1246.56,-350.41 1533,-312.84 1533,-312.84 1533,-312.84 1604.65,-281.56 1661.53,-256.72"/>
<polygon fill="#585b70" stroke="#585b70" points="1662.81,-259.98 1670.57,-252.77 1660.01,-253.57 1662.81,-259.98"/>
<text xml:space="preserve" text-anchor="middle" x="1624.31" y="-299.54" font-family="monospace" font-size="14.00" fill="#a6adc8">get_frame cmd</text>
</g>
<!-- ipc&#45;&gt;python -->
<g id="edge10" class="edge">
<title>ipc&#45;&gt;python</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M410,-173.67C410,-155.38 410,-134.55 410,-115.39"/>
<polygon fill="#585b70" stroke="#585b70" points="413.5,-115.55 410,-105.55 406.5,-115.55 413.5,-115.55"/>
<text xml:space="preserve" text-anchor="middle" x="463.62" y="-142.76" font-family="monospace" font-size="14.00" fill="#a6adc8">JSON&#45;lines</text>
<text xml:space="preserve" text-anchor="middle" x="463.62" y="-125.51" font-family="monospace" font-size="14.00" fill="#a6adc8">(Unix socket)</text>
<!-- 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>

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -112,6 +112,7 @@ impl Session {
// Scene relay: Unix socket for Python scene detection.
let socket_path = stream_dir.join(SCENE_SOCKET_NAME);
let (scene_tx, scene_rx) = tokio::sync::mpsc::channel(32);
info!("Scene relay: spawning for {}", socket_path.display());
tokio::spawn(scene_relay_task(socket_path, scene_rx));
// Tell Python which session dir to watch.
@@ -217,10 +218,23 @@ async fn scene_relay_task(
last_keyframe = Some(pkt.data.clone());
}
let stream = client.as_mut().unwrap();
if stream.write_all(&pkt.data).await.is_err() {
// Use a short timeout so a slow reader doesn't stall us.
// A stalled relay would queue old frames — better to drop.
let write_result = tokio::time::timeout(
std::time::Duration::from_millis(100),
stream.write_all(&pkt.data),
).await;
match write_result {
Ok(Ok(())) => {}
Ok(Err(_)) => {
info!("Scene relay: client disconnected");
client = None;
}
Err(_) => {
// Timeout — reader too slow, drop this packet.
debug!("Scene relay: slow reader, dropping packet");
}
}
}
None => break, // Channel closed, session ending.
}